Headless API troubleshooting

Use this page to resolve common Headless API failures.

Confirm the Headless API is enabled

The Headless API is in private beta. Your workspace may not be enabled if:

  • The genie chat interface doesn't display the custom chat interface option
  • Runtime calls are rejected before you reach the cases below

Contact your Customer Success Manager to request access.

401 auth_failed

Error: A runtime call returns 401 with:

json
{ "error_code": "auth_failed", "error_message": "Authentication failed.", "request_id": "<uuid>" }

Cause and solution: The gateway rejected the credential before reaching the genie. Check the following items in order:

  • Wrong token type: Runtime calls use a genie client API key or an OAuth access token rather than the wrkaus-… Developer API token, which is only for provisioning.
  • Wrong data center: Credentials are data-center-scoped. Call the genie-api host for the same data center your workspace runs in.
  • Expired or rotated key: Regenerating an API key invalidates the previous API key. Regenerate and update your backend to use the updated API key.
  • Malformed header: Send Authorization: Bearer <token> exactly.

401 user_nonactive_or_missing

Error: An API-key call returns 401 with error_code: "user_nonactive_or_missing".

Cause: The X-IDP-User-Id doesn't resolve to an active, allow-listed user. The runtime returns the same code whether the user doesn't exist, isn't in a user group allow-listed on the genie, or isn't active yet. A user is inactive when they've been invited but haven't accepted the invitation and finished setting up their Workato Identity.

Solution: Confirm the user is active. An invited user must accept their email invitation and finish setting up their Workato Identity before you can assert them, so ask the user to check their email if they haven't completed setup. Then make sure the user belongs to a user group allow-listed on the genie, creating the user first if needed. Look the user up with GET /api/iam/users?query=<email> server-side before the runtime call. The response includes a status field (active or invited), which also lets you show a different message for an unknown user versus a non-allow-listed one.

Client creation or attachment fails

Error: Most Developer API calls succeed, but creating, regenerating, or attaching a genie client fails with a permission error.

Cause: The Developer API client's role is missing the Custom chat interface privilege under Genie building, which governs client management. This is the most commonly missed privilege for Headless setup.

Solution: Grant the role Custom chat interface access with privileges to at least Create. You don't need to rotate the token after adding a privilege.

Related: Attaching a client returns 409 when the genie already has a client. The relationship is 1:1. Detach the existing client first.

Empty stream or repeated blank events

Error: The SSE stream stays open but appears idle, or the genie never replies.

Cause and solution:

  • Keep-alive events: During long-running or paused turns, the runtime emits periodic keep-alive events. For example: system.ping roughly every 30 seconds. Ignore event types your handler doesn't recognize.
  • Genie not started: The genie must be active. Start it with POST /api/agentic/genies/:id/start.
  • Waiting on you (skill confirmation): A skill.confirmation_required event pauses the turn until you post your decision with Approve or reject a skill. The same stream resumes after you post the resolution; you don't need to reconnect.
  • Waiting on the user (runtime connection): A runtime_connection.auth_required event pauses the turn while the user authenticates an upstream connection out-of-band. There's nothing for your client to post, and the original stream doesn't resume. It emits a keep alive, then a system.stream_interrupted. Detect completion by polling Get a runtime connection link until status is authorized, then read the resumed reply from message history.

Event recovery returns nothing

Error: Get events returns no events after a disconnection.

Cause: This error happens when you use the wrong parameter or window. The parameter is since_created_at (an RFC3339 timestamp); pass next_since_created_at from the previous response to page. Events are retained for 24 hours.

File upload is rejected

Error: A call to the upload endpoint fails, or the genie never receives the attachment.

Cause and solution:

  • File too large: The maximum file size is 20 MB. Reduce or split the file.
  • Wrong request format: Send the file as multipart/form-data with a single file field. Don't set Content-Type manually. Your HTTP client sets the multipart boundary.
  • Attachment not referenced: Uploading a file doesn't attach it on its own. Pass the returned file_id in the file_id parameter of Send a message.

Conversation topic returns a number instead of text

Problem: GET /conversations/:conversation_id returns a numeric topic such as 5951 instead of a readable title.

Cause: The genie auto-generates the conversation topic from the user's first message, which takes a few moments. If you call GET /conversations/:conversation_id before generation completes, topic is still a numeric placeholder. The list conversations endpoint returns the generated topic once it's ready.

Solution: Use the list conversations endpoint for display, or fall back to the conversation's first user message when topic is a numeric value.

Rebuild a conversation timeline after a reload or dropped stream

Problem: After a page reload or a dropped SSE stream, you need the full turn, including skill.* and processing.* events, but you only have what arrived on the live stream.

Cause: Every conversation event is persisted, not only the live stream. All event types, including skill.* and processing.*, are retrievable from Get events for 24 hours after they occur.

Solution: Fetch the persisted events with GET /conversations/events?conversation_id=<conversation_id>&since_created_at=<RFC3339> and replay them in order to reconstruct the full timeline. Don't treat skill.* and processing.* events as available only during the live stream.

One exception: a turn resumed after a runtime_connection.auth_required pause isn't captured in Get events — persisted events stop at the pause. Recover that reply from message history and merge it in, de-duplicating by message_id.

Get help

Every runtime response includes a request_id and an x-request-id header. Include these values when you contact Workato support so the request can be traced through the gateway.

Last updated: