Common Issues#
Solutions to frequently encountered problems, organised by area. Each entry shows the symptom (including the actual error message where the API returns one), the likely cause, and the fix.
Authentication#
401: Authorization header missing or invalid#
Symptom:
{"error": "Authorization header missing or invalid. Include 'Authorization: Bearer YOUR_TOKEN' header."}Cause: The request has no Authorization header, or it isn’t in Bearer format.
Fix: Send your API key as a bearer token:
curl https://api.outeract.com/ \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ events(limit: 1) { id } }"}'401: Invalid API key#
Symptom: {"error": "Invalid API key"}
Cause: The key doesn’t exist. It was mistyped, truncated, deleted, or rotated.
Fix: Copy the key again from the console (Settings → API Keys). Keys are shown once at creation; if you’ve lost the value, create a new key. If you recently rotated the key, make sure every deployment picked up the new value.
401: API key is disabled / API key has expired#
Symptom: {"error": "API key is disabled"} or {"error": "API key has expired"}
Cause: The key was toggled off in the console, or its expiresAt date has passed.
Fix: Re-enable the key or create a replacement. Check the key’s status in the console under Settings → API Keys.
API key validation is cached briefly on the server, so a just-revoked key may keep working for a short time, and a just-created key is picked up immediately.
Resource belongs to a different application#
Symptom: Queries return empty results, or Platform connection not found for current organisation style errors, even though the resource exists.
Cause: Each API key is scoped to exactly one application. Querying with a key from a different application won’t find the resource.
Fix: Use a key belonging to the application that owns the resource. The console shows which application each key belongs to under Settings → API Keys.
Sending Messages#
Recipient user not found or not in current application#
Symptom: sendMessage fails with Recipient user not found or not in current application.
Cause: The userId you passed belongs to a different application, or doesn’t exist.
Fix: Users are per-application. Query users in the same application (same API key) to confirm the ID.
Recipient user does not have a platform account#
Symptom: Recipient user does not have a whatsapp account (or another platform name).
Cause: You asked Outeract to send on a platform where the recipient has no identity. A user only gains a platform identity when they message you on that platform or when you create the identity explicitly.
Fix: Check the user’s identities:
query {
user(id: "USER_ID") {
id
identities { id identityType externalId }
}
}Either send on a platform the user actually has, or omit the platform and let Outeract infer it from the recipient’s identities on enabled connections.
Platform connection is disabled#
Symptom: Platform connection <name> is disabled and cannot send messages
Cause: The connection’s enabled toggle is off. Disabled connections are also skipped during platform inference, which can surface as “no account” errors instead.
Fix: Re-enable the connection in the console (Integrations) or via updatePlatformConnection.
No connection found for this application#
Symptom: No whatsapp connection found for this application or No platform connections found for this application
Cause: The application has no connection for the requested platform (or none at all).
Fix: Create the platform connection first; see the platform guides. If you passed an explicit platformConnectionId, verify it with Platform connection not found or not in current application in mind: connections are per-application too.
Validation errors#
Symptom: Errors like Invalid user_id: <value>. Must be a valid UUID. or Identity <id> does not exist.
Cause: A malformed or stale ID in the mutation arguments.
Fix: All entity IDs are UUIDs. Re-query the entity to get the current ID rather than caching IDs across environments.
Message accepted but never delivered#
Symptom: sendMessage succeeds but the recipient never receives anything; the message event shows delivery_status.failed_at set.
Cause: Platform-side rejection, most commonly the 24-hour messaging window on WhatsApp/Instagram/Facebook (error_type: messaging_window_closed).
Fix: See OUTSIDE_MESSAGING_WINDOW and use a pre-approved template. Subscribe to system.message_delivery_failed webhooks to be notified of asynchronous failures.
Webhooks Not Arriving#
Event pattern doesn’t match#
Symptom: Deliveries never appear for certain event types, while others arrive fine.
Cause: Webhook subscriptions match eventTypes patterns against the event’s type using shell-style wildcards (fnmatch). message.* matches message.inbound and message.outbound; a bare message matches nothing. Events without an event type never match any pattern.
Fix: Open the subscription in the console (Settings → Webhooks) and check its patterns: widen them (message.*, or * for everything) or add the missing exact type. Also confirm the subscription is enabled; disabled subscriptions receive nothing.
Endpoint returns 4xx/5xx#
Symptom: Deliveries show failed status with your endpoint’s status code.
Cause: Your endpoint rejected the request. Outeract retries failed deliveries up to the subscription’s max attempts (default 5) on an exponential schedule (5 s, 5 m, 30 m, 2 h, 5 h after each failed attempt), then marks the delivery failed permanently.
Fix: Inspect the delivery records on the subscription’s detail page in the console. They include the response code, error message and attempt count for every attempt.
Return a 2xx quickly (do heavy processing asynchronously), and make sure the URL is publicly reachable over HTTPS with a valid certificate.
Subscription was automatically disabled#
Symptom: Deliveries stop entirely; the subscription shows as disabled in the console with a reason given, and its health status reads disabled.
Cause: Outeract auto-disables a subscription after 20 consecutive delivery failures with no successful delivery in the last 24 hours, typically an endpoint that has been down or rejecting requests for a sustained period. A system.webhook_endpoint_disabled event is emitted when this happens (subscribe to system.* on another endpoint to be notified).
Fix: Repair the endpoint, then re-enable the subscription in the console; the webhook detail page shows a re-enable banner. Re-enabling resets the failure counters.
Signature verification fails#
Symptom: Your endpoint rejects every delivery as having a bad signature.
Cause: Common verification bugs: signing the parsed/re-serialised JSON instead of the raw request body, dropping the sha256= prefix, or comparing against a base64 digest.
Fix: Outeract sends an HMAC-SHA256 of the raw request body, keyed with the subscription’s secret, in the X-Outeract-Signature header formatted as sha256=<hex digest>:
import hmac, hashlib
def verify(secret: str, raw_body: bytes, header: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header)Use the subscription’s test action in the console to send a signed test delivery on demand; see Debugging.
Platform Connections#
OAuth token expired or revoked#
Symptom: Sends fail with platform errors like INVALID_TOKEN; the connection health check reports authentication failures.
Cause: The platform-side access token expired, or the user revoked access / changed their password on the platform.
Fix: Open the connection in the console and run a health check to confirm, then reconnect via OAuth from the same page.
Health check failures#
Symptom: A health check reports the connection as invalid, with one or more failing tests.
Cause: Each test pinpoints a different problem: credential validation, webhook registration, API quota. A failing webhook test usually means the platform-side webhook subscription was removed or points at a stale URL.
Fix: Fix the failing test’s area. For webhook registration issues, the connection’s Reconnect Webhook action in the console re-registers the webhook with the platform (for platforms that support auto-setup).
Invalid configuration on create/update#
Symptom: Invalid configuration: <field errors> when creating or updating a connection.
Cause: The connection’s config is validated against the platform’s config form schema: missing required fields (tokens, phone number IDs, etc.) or wrong types.
Fix: Query availablePlatforms to see each platform’s required config fields, or use the console’s connection form, which renders the same schema.
Rate Limiting#
429: Rate limit exceeded#
Symptom:
{"error": "Rate limit exceeded", "limit": 100, "window": "1 minute"}with headers X-RateLimit-Limit, X-RateLimit-Remaining: 0, X-RateLimit-Reset, and Retry-After: 60.
Cause: The API key exceeded its per-minute request budget. Each key has its own rateLimitPerMinute (default 100, capped at 1000).
Fix:
- Honour
Retry-Afterand back off exponentially. - Batch reads: GraphQL lets you fetch related data in one request instead of N.
- Raise the key’s rate limit in the console (Settings → API Keys) if your workload genuinely needs it.
Every successful response also carries the X-RateLimit-* headers, so you can throttle proactively before hitting 429s.
See Also#
- Debugging - tools for investigating issues
- FAQ
- Error Codes