Skip to main content

Troubleshooting: Integration action and sync error codes

An API-key integration on your dashboard — PostHog, Mixpanel, Freshdesk, Resend, ElevenLabs, Google Gemini, or People Data Labs — flips its card to errored, paused, or stopped, or an action or sync call against it returns a 4xx/5xx. This lane (the credential lane, connected by pasting a provider-issued key into the hosted form) speaks a distinct code family from the Salesforce / HubSpot OAuth lane, which its sibling page CRM integration sync error codes decodes. Read error.code on the failed response first; the code tells you which leg failed and who owns the fix.

Symptom

On Settings → Integrations the provider card reads errored, or one of its sync rows reads paused or stopped; an action call (POST /api/v1/integrations/{id}/actions/{name}) returns a 404, 401, 400, or 502 carrying one of the codes above; or a sync trigger (POST /api/v1/integrations/{id}/sync) or synced-records read (GET /api/v1/integrations/{id}/data?model=...) returns a 502 or 503. The flipped card and the failed call are two surfaces of the same failure: the card reflects the latest sync state, the call reports the leg that failed on this attempt.
An empty sync list on a connected card is not a failure. Resend, People Data Labs, and WorkOS act on demand or send only, so their drawers can legitimately show zero scheduled sync rows — see Settings → Integrations console.

Connection and action codes: the three-way split

INTEGRATION_CONNECTION_MISSING (404)

There is no stored connection for this provider key in your organization, so the action has nothing to run against. The fix is to connect, not to retry:
  1. Open Settings → Integrations and find the provider card. The connectable credential-lane keys are posthog, mixpanel, freshdesk, resend, elevenlabs, google-gemini, and peopledatalabs; the per-provider prerequisites live in Connect API-key providers.
  2. Click Connect, create the prerequisite at the provider if you have not (a personal API key, a service account, a verified domain), and submit it through the hosted form. The form validates the value before anything is stored.
  3. Re-run the action once the card reads Connected.
A 404 here is not a defect in your call: retrying the same action without connecting returns the same 404.

INTEGRATION_AUTH_EXPIRED (401)

A connection exists but the credential it holds was revoked, rotated, or expired at the provider. API keys die quietly — someone rotated the key, the project it scoped moved, or its permissions were narrowed.
  1. At the provider, confirm the key still exists and still carries the scopes the integration needs (project read access for PostHog, sending access plus a verified domain for Resend).
  2. Rotate or re-create the key at the provider, then reconnect the card from Settings → Integrations with the fresh value. One reconnect replaces the stored credential.
  3. Confirm connected: true on GET /api/v1/integrations/{id}/status, then re-run the failed action.
Do not reconnect in a loop. One reconnect with a valid key clears the code; a third failing reconnect means the key itself is wrong for the provider, which is a prerequisite problem, not an Orbit one.

INTEGRATION_ACTION_FAILED (502)

The connection is healthy and the action reached the provider, but the provider rejected it: a 4xx validation refusal, a rate limit, or a provider 5xx. Read the raw transport line for the failed call before re-invoking — it carries the provider’s own status and body. Fix the payload, the rate budget, or the provider-side object, then retry; a blind retry resends the same rejected payload and burns provider rate limit for nothing.

UNKNOWN_INTEGRATION_ACTION (400)

The action slug in the URL is not in the catalog the provider advertises. The response message lists the actions that are valid for the provider (Available: ...) — pick one of them. The same catalog is visible in the card drawer’s Actions tab on Settings → Integrations. A 400 here is raised before any provider call is made, so correcting the slug and retrying costs nothing.

The sync ladder

When a sync call fails, the code tells you which leg failed and which side owns it.

SYNC_TRIGGER_FAILED (502)

POST /api/v1/integrations/{id}/sync could not start the named sync against the integration server. Retry once after a minute. If the same sync keeps failing while the card reads connected, the usual cause is the sync’s provider-side configuration — escalate with the request id.

SYNC_RECORDS_FAILED (502)

GET /api/v1/integrations/{id}/data?model=... could not page through the model’s synced records. Retry once; a persistent failure with a healthy card is an upstream read fault, not a tenant misconfiguration — escalate with the request id and the model you queried.

NANGO_NOT_CONFIGURED (503) and NANGO_UNAVAILABLE (502)

Both codes are platform-side: the integration server is not configured, or is briefly unreachable, and nothing on your side changes either code. Wait a minute and retry once; if NANGO_NOT_CONFIGURED persists, or NANGO_UNAVAILABLE outlives the window the status page shows, escalate — there is nothing to fix from your tenant.

Provider-side versus platform-side

INTEGRATION_*, UNKNOWN_INTEGRATION_ACTION, and WOO_* codes are provider- or tenant-side: the fix lives in your connection, your key, or your payload. NANGO_* codes are platform-side. SYNC_TRIGGER_FAILED and SYNC_RECORDS_FAILED sit between: one retry is yours, persistence is an escalation. The split decides whether you reconnect or simply wait.

DISCONNECT_FAILED (502) recovery

A disconnect that ends in DISCONNECT_FAILED has torn down part of the connection and may leave the rest live — the card can keep reading connected and scheduled work can continue. Retry the disconnect once. If it fails again, do not hammer it: check Settings → Audit Log for the disconnect entries, and escalate with the request id so the leftover connection is reconciled on the platform side. Already-synced records remain available after a successful disconnect; on-demand providers simply stop serving new lookups.

Appendix: WooCommerce store-URL family

Connecting a WooCommerce store validates the store URL end to end, and each rejection is its own code: The ladder runs URL shape, reachability, auth, then REST presence — fix the first failing rung; later rungs only run once earlier ones pass.

What NOT to do

  • Do not blind-retry INTEGRATION_ACTION_FAILED. The provider rejected the payload; re-sending it without reading the raw response resends the same rejection and burns rate limit.
  • Do not loop reconnects on INTEGRATION_AUTH_EXPIRED. One reconnect with a freshly rotated key clears it; repeated reconnects with the same dead key change nothing.
  • Do not treat INTEGRATION_CONNECTION_MISSING as an action bug. No retry creates the missing connection; connect first.
  • Do not chase NANGO_NOT_CONFIGURED from your side. It is a platform configuration state; no tenant setting changes it.
  • Do not read an empty sync list as a broken connection on on-demand providers (Resend, People Data Labs, WorkOS).

Escalation

Bring the provider key (e.g. posthog), the integration id from the card, the failing action slug or sync name, and the request_id from meta.request_id on the failed response to support@devotel.io. The raw transport line that decodes a provider rejection is located by that request id.