> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting: Integration action and sync error codes

> Decode the INTEGRATION_*, SYNC_*, NANGO_*, and DISCONNECT_FAILED codes an API-key integration (PostHog, Mixpanel, Freshdesk, Resend, ElevenLabs, Google Gemini, People Data Labs) can return, and recover with one reconnect or a decoded retry instead of a blind one.

# 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](https://docs.orbit.devotel.io/troubleshooting/crm-integration-errors.md) decodes. Read `error.code` on the failed response first; the code tells you which leg failed and who owns the fix.

| Code | Status | Meaning | Who fixes |
| - | - | - | - |
| `INTEGRATION_CONNECTION_MISSING` | 404 | No stored connection exists for this provider key in your organization. | You: connect first. |
| `INTEGRATION_AUTH_EXPIRED` | 401 | The stored credential was revoked or expired at the provider. | You: rotate the key and reconnect once. |
| `INTEGRATION_ACTION_FAILED` | 502 | The provider received the action and rejected it. | You: decode the raw response, then retry. |
| `UNKNOWN_INTEGRATION_ACTION` | 400 | The action slug is not in the provider's advertised catalog. | You: pick a listed action. |
| `SYNC_TRIGGER_FAILED` | 502 | The sync trigger did not complete against the integration server. | One retry is yours; persistence escalates. |
| `SYNC_RECORDS_FAILED` | 502 | The synced-records read failed upstream. | One retry is yours; persistence escalates. |
| `NANGO_NOT_CONFIGURED` | 503 | The integration server is not configured on the platform side. | Platform: nothing to fix on your side; escalate. |
| `NANGO_UNAVAILABLE` | 502 | The integration server is temporarily unreachable. | Platform: retry after a minute; persistence escalates. |
| `DISCONNECT_FAILED` | 502 | The disconnect did not complete, so the connection may still be live. | You: retry once, then reconcile via the audit log. |

## 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.

> [!NOTE]
>
> 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](https://docs.orbit.devotel.io/guides/settings-integrations-console.md).

## 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](https://docs.orbit.devotel.io/guides/oauth-saas-integrations.md).
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:

| Code | Meaning | Owner action |
| - | - | - |
| `WOO_INVALID_URL` | The value is not a parseable URL. | Enter the full store URL, e.g. `https://shop.example.com`. |
| `WOO_HTTPS_REQUIRED` | The URL is plain HTTP. | Serve the store over HTTPS; the REST API must be reachable over TLS. |
| `WOO_INVALID_STORE_URL` | The URL parses but is not accepted as a store URL. | Check the host against the store's actual domain. |
| `WOO_UNREACHABLE` | The store did not answer (network error, 404, or timeout). | Confirm the store is public and reachable from outside your network. |
| `WOO_AUTH_FAILED` | The store answered but rejected the key pair. | Re-check key and secret, and their scopes, in WooCommerce → Settings → Advanced → REST API. |
| `WOO_REST_NOT_FOUND` | The store answered but the WooCommerce REST API is not present. | Confirm the WooCommerce plugin is active on that install. |
| `WOO_VALIDATION_FAILED` | The final validation probe failed. | Read the message detail; it names the probe that failed. |

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](mailto:support@devotel.io). The raw transport line that decodes a provider rejection is located by that request id.

## Related reading

* [CRM integration sync error codes](https://docs.orbit.devotel.io/troubleshooting/crm-integration-errors.md) — the sibling runbook for the Salesforce / HubSpot OAuth lane.
* [Connect API-key providers](https://docs.orbit.devotel.io/guides/oauth-saas-integrations.md) — per-provider prerequisites for the credential form.
* [Settings → Integrations console](https://docs.orbit.devotel.io/guides/settings-integrations-console.md) — card drawers, statuses, and the empty-sync-list rule.
* [Error codes reference](https://docs.orbit.devotel.io/reference/error-codes.md) — retry classes for every code.
* [Troubleshooting hub](https://docs.orbit.devotel.io/reference/troubleshooting-hub.md) — runbooks by surface.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.