Developers · Partner API
Error codes
Every non-2xx response from /api/public/partner/v1/* returns the same envelope. Branch on error.code — the HTTP status is a hint, the code is the contract.
Response envelope
{
"error": {
"code": "space_template_not_found",
"message": "No active template with slug \"basic\" for this partner.",
"fix": "Create the template in your partner portal, or send a slug that already exists.",
"docs": "https://tally.ac/docs/partner-api/errors#space_template_not_found",
"request_id": "req_0a1b2c3d4e5f..."
}
}- code — stable snake_case identifier. Never renamed. Add new ones over time.
- message — one-line human summary of what happened.
- fix — the recommended remediation.
- request_id — also echoed in the
x-request-idresponse header. Include it in support tickets. - Some codes add extra fields (e.g.
issues[]oninvalid_request,retry_after_secondsonrate_limited).
Retry rules
- 5xx and
rate_limited— retry with exponential backoff (e.g. 1s, 2s, 4s, 8s, cap at 30s, max 5 tries). - Other 4xx — never retry unchanged. Fix the request and resubmit.
POST /usersis idempotent on(partner_id, external_user_id)— safe to replay.POST /entries/bulkdedupes each entry on(connection_id, external_id). Duplicates come back asresults[].status = "duplicate"inside a 200 response, not as a top-level error.
Code reference
| Code | HTTP | Meaning | Recommended fix |
|---|---|---|---|
| auth_missing | 401 | No Authorization header was sent. | Send `Authorization: Bearer <YOUR_API_KEY>` on every request. |
| auth_invalid | 401 | The API key wasn't recognised or has been revoked. | Confirm the key exists and isn't revoked in the partner portal. Rotate and re-deploy if needed. |
| connection_secret_invalid | 401 | The connection_secret didn't match this connection_id. | Use the secret returned by POST /users when the connection was minted. Re-provision to mint a new one. |
| rate_limited | 429 | You've exceeded the per-partner rate limit for this endpoint. | Back off and retry after `retry_after_seconds`. Batch calls where possible. |
| invalid_json | 400 | Request body wasn't valid JSON. | Set `Content-Type: application/json` and send a JSON body. |
| invalid_request | 422 | Request body failed validation. | Fix the fields listed in `issues[]` and resend. Never retry unchanged. |
| unsupported_media_type | 415 | Only application/json is accepted. | Set the `Content-Type` header to `application/json`. |
| space_template_not_found | 404 | No active space template with that slug exists for this partner. | Create the template in your partner portal, or send a slug that already exists. |
| space_template_archived | 409 | The referenced space template is archived. | Un-archive the template or point new signups at a live one. |
| connection_not_found | 404 | No connection with that id belongs to your partner account. | Use the connection_id returned by POST /users. Don't share connections across partners. |
| connection_inactive | 403 | The connection isn't active. | The user revoked or paused this connection. Re-provision via POST /users to mint a new one. |
| user_provision_failed | 500 | Couldn't create or look up the Tally user for that email. | Safe to retry with exponential backoff. If it persists, share the request_id with support. |
| space_create_failed | 500 | Couldn't create the branded space for this user. | Safe to retry with exponential backoff. Share the request_id with support if it persists. |
| connection_create_failed | 500 | Couldn't mint an integration connection. | Safe to retry with exponential backoff. Share the request_id with support if it persists. |
| internal_error | 500 | Something went wrong on our side. | Safe to retry with exponential backoff. Share the request_id with support if it persists. |
Support
When something looks wrong, capture the response body and the x-request-id header, then email partners@tally.ac. We can find your request in seconds.