Skip to content

guides

Intended Documentation

Error Patterns and Remediation

Common error codes, troubleshooting steps, and retry strategies for Intended API integration.

Error Response Format#

All Intended API errors follow a consistent structure:

json
{
  "error": {
    "code": "INTENT_VALIDATION_FAILED",
    "message": "The intent payload is missing required field: action",
    "details": {
      "field": "action",
      "requirement": "required"
    },
    "request_id": "req_abc123def456"
  }
}

Every error response includes a request_id for support escalation.

Common Error Codes#

Authentication Errors#

INVALID_API_KEY (401)#

The API key is missing, malformed, or revoked.

Remediation:

  • Verify the key is set correctly in the Authorization header
  • Check that the key has not been revoked in the console
  • Ensure you are using the correct environment (staging vs production)
bash
# Correct format
curl -H "Authorization: Bearer mrt_live_abc123..." \
  https://api.intended.so/intent

INSUFFICIENT_PERMISSIONS (403)#

The API key does not have the required scope for this operation.

Remediation:

  • Check the key's assigned scopes in the console
  • Request additional scopes from your tenant administrator

Intent Evaluation Errors#

INTENT_VALIDATION_FAILED (400)#

The intent payload does not meet the required schema.

Remediation:

Ensure all required fields are present: action, resource, context.

json
{
  "action": "read",
  "resource": "customer_records",
  "context": {
    "user_id": "usr_123",
    "purpose": "support_ticket_resolution"
  }
}

POLICY_EVALUATION_TIMEOUT (504)#

Policy evaluation exceeded the timeout threshold.

Remediation:

  • This is typically transient. Retry with exponential backoff.
  • If persistent, check if policies have become overly complex.
  • Contact support if latency consistently exceeds 5 seconds.

NO_MATCHING_POLICY (200, decision: deny)#

No policy matched the intent, and the default is deny.

Info

This is not an error. It is the fail-closed default behavior. An intent that matches no policy is denied. Create a policy that covers this intent pattern.

Token Errors#

TOKEN_EXPIRED (401)#

The decision token has passed its expiration time.

Remediation:

  • Decision tokens have a configurable TTL (default: 5 minutes)
  • Re-evaluate the intent to get a fresh token
  • Do not cache tokens beyond their exp claim

TOKEN_SIGNATURE_INVALID (401)#

The token signature does not verify against the known public key.

Remediation:

  • Ensure you are verifying against the correct public key
  • Fetch the latest key from GET /tenants/:tenantId/authority-keys/public
  • Check that the token has not been tampered with

Rate Limiting#

RATE_LIMIT_EXCEEDED (429)#

You have exceeded the allowed request rate.

Remediation:

  • Implement exponential backoff
  • Check the Retry-After header for the recommended wait time
  • Consider batching requests if submitting many intents

Retry Strategy#

Use exponential backoff with jitter for transient errors:

typescript
async function evaluateWithRetry(
  intent: IntentPayload,
  maxRetries = 3
): Promise<EvaluationResponse> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch('https://api.intended.so/intent', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${API_KEY}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(intent),
      });

      if (response.status === 429) {
        const retryAfter = Number(
          response.headers.get('Retry-After') ?? 1
        );
        await sleep(retryAfter * 1000);
        continue;
      }

      if (response.status >= 500 && attempt < maxRetries) {
        await sleep(baseDelay(attempt));
        continue;
      }

      return await response.json();
    } catch (error) {
      if (attempt === maxRetries) throw error;
      await sleep(baseDelay(attempt));
    }
  }
  throw new Error('Max retries exceeded');
}

function baseDelay(attempt: number): number {
  const base = Math.min(1000 * 2 ** attempt, 30000);
  const jitter = Math.random() * base * 0.1;
  return base + jitter;
}

Retryable vs Non-Retryable Errors#

  • 400, 401, 403, 404 — Non-retryable. Fix the request.
  • 429 — Retryable. Backoff and retry.
  • 500, 502, 503, 504 — Retryable. Transient server errors.

Support Escalation#

If you cannot resolve an error after following the remediation steps:

  1. Collect the request_id from the error response
  2. Note the timestamp, endpoint, and request payload
  3. Check the Troubleshooting Index for known issues
  4. Contact support with the collected information

Next Steps#