Skip to main content

Error Codes

Errors come in three shapes depending on where they originate. Handle all three — the API is served by several services, and which shape you get depends on the endpoint, not the status code.

HTTP Status Codes​

CodeStatusDescription
200OKRequest successful
201CreatedResource created
202AcceptedAccepted for asynchronous processing
204No ContentSuccess with an empty body
400Bad RequestInvalid parameters, or a required workspace id is missing
401UnauthorizedInvalid or missing authentication
403ForbiddenInsufficient scope, or no access to that workspace
404Not FoundResource doesn't exist in this workspace
409ConflictDuplicate resource, or a locked accounting period
413Payload Too LargeFile upload over the size limit
422Unprocessable EntityValid request that cannot be applied — e.g. a price that won't convert
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer error
502Bad GatewayAn upstream exchange, chain or price provider failed

Shape 1: authentication and authorization​

OAuth 2.0 format, with no success field. Emitted before your request reaches the endpoint.

{
"error": "insufficient_scope",
"error_description": "Missing: portfolios:read"
}
StatuserrorCause
401unauthorizedNo credentials, or an invalid/expired token
403insufficient_scopeValid credential, but it lacks the endpoint's scope
403forbiddenNot a member of the workspace, or the token is bound to a different one
400bad_requestWorkspace id required but not supplied
500server_errorFailure while authenticating

Solutions

  • unauthorized — send Authorization: Bearer … or x-api-key, not both. Check the token hasn't expired; access tokens last 24 hours on the OAuth flow.
  • insufficient_scope — error_description names the missing scope. Request it at authorization time, or add it to your API key. Note contacts:*, invoices:* and swaps:* are not in the default client scope set.
  • forbidden — either you passed a workspace your credential can't reach, or you passed one at all on a workspace-bound token. See Workspaces.
  • bad_request — API keys and first-party sessions must pass ?wid=; OAuth tokens must not.

Shape 2: wrapped application errors​

Used by integrations, providers, portfolios, credentials, syncs, CSV, user profile, assets, labels, spam and DeFi metadata.

{
"success": false,
"error": "Integration not found",
"code": "NOT_FOUND",
"details": {}
}
CodeStatusCause
VALIDATION_ERROR400Failed schema validation; details.issues lists each field
NOT_FOUND404No such resource in this workspace
CONFLICT409Would duplicate something unique — e.g. an integration alias
CREDENTIAL_VALIDATION_FAILED400The exchange or wallet rejected the credentials
SYNC_ERROR400Sync could not be started
FILE_REQUIRED400No file attached to a CSV upload
INVALID_FILE_TYPE400Not .csv, .xls or .xlsx
TOO_MANY_FILES400Over the per-request file limit
FILE_TOO_LARGE413Over the per-file size limit
RATE_LIMIT_EXCEEDED429Too many requests
EXTERNAL_SERVICE_ERROR502An upstream provider failed
INTERNAL_ERROR500Unexpected server error

Validation errors carry the offending fields:

{
"success": false,
"error": "Validation failed",
"code": "VALIDATION_ERROR",
"details": {
"issues": [
{
"code": "invalid_type",
"expected": "number",
"received": "string",
"path": ["limit"],
"message": "Expected number, received string"
}
]
}
}

Solutions

  • EXTERNAL_SERVICE_ERROR and RATE_LIMIT_EXCEEDED are transient — retry with exponential backoff.
  • CONFLICT on integration creation usually means the alias is taken. Check first with GET /v1/integrations/alias-exists.
  • CREDENTIAL_VALIDATION_FAILED is the exchange's verdict, not ours. Validate up front with POST /v1/integrations/test-credentials rather than discovering it on the first sync.

Shape 3: nested errors (transactions and ledgers)​

Transactions and ledgers nest the error and omit success.

{
"error": { "code": "PERIOD_LOCKED", "message": "Accounting period is locked" }
}
CodeStatusCause
BAD_REQUEST400Ineligible merge or split
NOT_FOUND404No such transaction or ledger in this workspace
PERIOD_LOCKED409Falls in a filed, locked accounting period
CURRENCY_CONVERSION_FAILED422A supplied price could not be converted
INTERNAL_ERROR500Unexpected server error

Both 409 and 422 reject the write whole — nothing is partially applied, so both are safe to retry once you've fixed the cause. For PERIOD_LOCKED, the period must be unlocked before the edit can land. For CURRENCY_CONVERSION_FAILED, check price.baseCurrency is a currency Kryptos can convert on that date.

Partial success is not an error​

Several endpoints report per-item outcomes with a 2xx status. Treating them as wholly successful is a common source of silent data loss:

EndpointCheck
POST /v1/transactions/batchdata.errorCount
POST /v1/integrations/batch-deletedata.failed[]
POST /v1/integrations/resync-alldata.failed[]
POST /v1/csv/uploaddata.failures[]
A sync reaching partially_syncedrecordsFailed and message

Retry guidance​

SituationRetry?
429, 502, 500Yes, with exponential backoff
422 CURRENCY_CONVERSION_FAILEDYes, after correcting the price
409 PERIOD_LOCKEDYes, after unlocking the period
401Only after refreshing the token — a bare retry will fail identically
400, 403, 404No — fix the request