IdenIden Docs
Bridge Developer Guide

Error Reference

Complete reference of HTTP status codes, validation errors, sync errors, and task error codes returned by the Bridge API.

This page is a consolidated reference for every error the Bridge API can return. Bookmark it -- when your connector hits an unexpected response, start here.

HTTP Status Codes

The Bridge API uses standard HTTP status codes. Every endpoint returns one of the following:

StatusMeaningWhen returned
200 OKRequest succeededSync page push, task list, task detail, sync progress, resource type update
201 CreatedResource createdApp created, resource type registered, sync session started
202 AcceptedRequest accepted for async processingSync session completed (cleanup runs in the background)
204 No ContentRequest succeeded with no response bodySync session abandoned
400 Bad RequestMalformed requestInvalid JSON, schema validation failures (missing required fields, invalid field types), attempting to push to a completed/abandoned sync
401 UnauthorizedAuthentication failedMissing Authorization header, invalid API key, expired key
403 ForbiddenInsufficient permissionsAPI key does not have access to the requested app
404 Not FoundResource does not existApp, resource type, sync session, or task not found. Also returned when clone_from references a nonexistent app
409 ConflictState conflictDuplicate resource type slug, or task already in a terminal state (completed/failed)
422 Unprocessable EntityBusiness-rule validation failedUnknown membership/assignment slugs or database integrity errors when pushing a sync page
500 Internal Server ErrorUnexpected server errorUnhandled exception during sync processing

Error Response Format

Most error responses return a JSON body with a detail field:

{
  "detail": "Human-readable error message."
}

Both 400 and 422 responses from sync page pushes use the detail field format -- see Sync Validation Errors below.

Sync Validation Errors

When you push a page of records (PUT .../sync/{sync_id}/{resource_type_slug}/), the API validates every record before processing. If any record fails validation, the entire page is rejected with 422 Unprocessable Entity.

Response format

Schema validation failures (400): If records fail Pydantic schema validation (missing required fields, wrong types), the API returns 400 Bad Request:

{"detail": "Field required"}

Business-rule validation failures (422): If records pass schema validation but fail business-rule checks (unknown slugs, integrity errors), the API returns 422 Unprocessable Entity:

{"detail": "Record 'u1': unknown membership slug 'nonexistent'"}

Common validation triggers

TriggerStatusExample message
Missing required field on account400Field required
Missing required field on group/license400Field required (missing id or name)
Unknown membership slug422Record 'u1': unknown membership slug 'nonexistent'
Unknown assignment slug422Record 'u1': unknown assignment slug 'nonexistent'
Too many records in a page400List should have at most 100 items after validation
Too many refs per slug400memberships['teams'] has 150 refs, max is 100.
Invalid field type400Input should be a valid string

Data integrity errors (also 422)

If records pass schema validation but fail at the database level, the API returns 422 with a detail field:

{
  "detail": "Sync failed for 'accounts': a required field is missing. Ensure all records have complete data."
}

Common integrity error messages:

MessageCause
a required field is missing. Ensure all records have complete data.A database-level NOT NULL constraint was violated. Usually a missing name on a group or license referenced in memberships/assignments
a record with the same identifier already exists.Duplicate key in the database. Rare -- usually means concurrent writes
a referenced record does not exist.A foreign key reference failed. The group or license ID referenced by an account does not exist in the current session

Sync Session Errors

After you call complete, Iden runs an asynchronous cleanup pass that marks records not included in the session as inactive. If cleanup fails, the sync session transitions to the error status.

Error response format

Poll the sync status endpoint (GET .../sync/{sync_id}/) to check for errors:

{
  "sync_id": "d4e5f6a7-...",
  "status": "error",
  "progress": [
    {"slug": "team", "name": "Team", "type": "group", "synced_count": 42},
    {"slug": "account", "name": "Account", "type": "account", "synced_count": 150}
  ],
  "error": {
    "error_code": "CLEANUP_FAILED",
    "message": "Cleanup task failed after maximum retries."
  }
}

Error codes

CodeDescription
CLEANUP_FAILEDThe asynchronous cleanup task failed after maximum retries. Records that were pushed successfully are preserved, but stale records were not removed. Start a new sync to retry

Task Error Codes

When your connector cannot execute a provisioning task, it reports failure with an error code and message. These codes are passed in the error.code field when patching a task to failed status.

Standard codes

CodeDescriptionSuggested resolution
ACCOUNT_NOT_FOUNDThe account does not exist in the target systemVerify the account_id in the task payload. The account may have been deleted outside of Iden. Run a sync to update Iden's records
ACCOUNT_ALREADY_EXISTSThe account already exists (on create_account)Check if the account was created manually or by another system. Consider reporting success if the account exists with the correct attributes
LICENSE_EXHAUSTEDNo available license seatsPurchase additional seats in the external system, or free up existing allocations. Then retry the task
PERMISSION_DENIEDThe connector's credentials lack sufficient permissionsUpdate the API key or service account permissions in the external system. Check that the connector has write access
RATE_LIMITEDThe external system rejected the request due to rate limitingWait and retry. Consider adding backoff logic to your connector. Iden may retry the task automatically
TIMEOUTThe operation timed out before completingCheck network connectivity and the external system's health. Increase timeout thresholds in your connector if applicable
INTERNAL_ERRORAn unspecified internal errorUse as a catch-all when no other code applies. Include a descriptive message to help administrators diagnose the issue

Reporting format

When reporting a failed task, include both the code and a human-readable message:

{
  "status": "failed",
  "error": {
    "code": "ACCOUNT_NOT_FOUND",
    "message": "No account with ID ext-123 exists in the Jira instance."
  }
}

The message is displayed to administrators in the Iden dashboard. Be specific -- "User ext-123 not found in Jira" is more useful than "Not found."

409 Conflict on task updates

If you attempt to update a task that is already in completed or failed status, the API returns:

{
  "detail": "Task is already in 'completed' state and cannot be updated."
}

This is not an error in your connector -- it means the task was already resolved. Handle 409 gracefully by logging and moving on.

Troubleshooting

"I'm getting 409 on resource type creation"

The slug already exists for this app. This happens when:

  • You already registered the type in a previous run.
  • Another app in the same clone group (via clone_from) already registered it.

Resolution: List existing resource types with GET .../resource-types/ to confirm the type exists. If it does, no action is needed -- proceed with syncing.

curl "https://developer.idenhq.com/org/acme/api/v1/bridge/apps/${APP_ID}/resource-types/" \
  -H "Authorization: Api-Key <your-api-key>"

"My sync shows status: error"

The sync session entered an error state during the asynchronous cleanup phase.

Resolution: Check the error object in the sync status response:

response = requests.get(
    f"{BASE_URL}/api/v1/bridge/apps/{app_id}/sync/{sync_id}/",
    headers=HEADERS,
)
data = response.json()
if data["status"] == "error":
    error = data["error"]
    print(f"Error code: {error['error_code']}")
    print(f"Message: {error['message']}")

The most common cause is CLEANUP_FAILED, which means the cleanup task failed after retries. Records you pushed are still saved -- only the stale-record removal did not complete. Start a new sync session to retry.

"Records are disappearing after sync"

This is expected behavior. The Bridge sync uses PUT semantics: when you call complete, the set of records you pushed becomes the full truth. Any record that existed before but was not included in the session is marked inactive (removed).

Resolution: If you do not want records removed:

  • Push all records every time, not just changed ones.
  • Use abandon instead of complete if you are doing an incremental/partial sync. Abandoned sessions do not trigger the removal pass.

See Completion vs Abandonment in the Sync Guide for details.

"Validation errors on accounts"

Account validation errors are most commonly caused by:

  1. Missing email or username. Every account record must have at least one of email or username.
  2. Unknown membership or assignment slug. The memberships or assignments field references a resource type slug that is not registered for this app. Check that you registered the group/license type before syncing accounts.

Resolution: Check the detail field in the 400/422 response for the specific record and message, then fix the offending records:

response = requests.put(
    f"{BASE_URL}/api/v1/bridge/apps/{app_id}/sync/{sync_id}/account/",
    headers=HEADERS,
    json={"records": accounts},
)

if response.status_code in (400, 422):
    print(f"Validation error: {response.json().get('detail')}")

"Getting 401 Unauthorized on every request"

Resolution:

  • Verify the Authorization header format: Api-Key <your-api-key> (note the space after Api-Key).
  • Confirm the API key is valid and has not been revoked.
  • Check that you are using the correct base URL for your organization.

"Getting 403 Forbidden on a specific app"

Your API key does not have access to the requested Bridge app.

Resolution: Confirm the app was created with the same API key or that the key has been granted access to the app. List your accessible apps with GET /api/v1/bridge/apps/ to see what is available.

"Push succeeds but synced_count stays at 0"

The sync session may have been cancelled by a newer sync. Starting a new sync session automatically cancels any previous incomplete session for the same app.

Resolution: Make sure only one sync process runs per app at a time. If your connector restarts mid-sync, it will start a fresh session that cancels the previous one. Any records pushed to the cancelled session are discarded.

On this page