SettleMint
Developer guidesAPI integration

Error handling

Handle API errors with retry strategies, error code references, and clear failure handling for DALP integrations.

DALP API errors include a public error object with a stable DALP-#### identifier, HTTP status, retry guidance, and remediation copy. Use that object to decide whether to fix the request, retry with backoff, or show users a clear next step.

Error response format

Direct REST errors return the public error object under error:

{
  "error": {
    "id": "DALP-0006",
    "category": "permission",
    "status": 403,
    "retryable": false,
    "message": "User does not have the required role to execute this action.",
    "why": "The actor lacks at least one role required by the token or system contract.",
    "fix": "Grant the required role or retry with an authorized actor."
  }
}
FieldTypeDescription
error.idstringStable DALP error identifier. Use it for support and logging.
error.categorystringError class such as auth, permission, or dependency.
error.statusnumberHTTP status code.
error.retryablebooleanWhether retrying can make sense after the cause is resolved.
error.messagestringShort human-readable summary.
error.whystringWhy the request failed.
error.fixstringRecommended remediation.
error.detailsobject?Optional route-specific details.

Other transports carry the same public object in transport-specific locations:

  • oRPC REST errors attach it under data.dapiError.
  • oRPC JSON-RPC errors wrap it under error.data.dapiError.
  • Deployment stream error events send it under the event payload's error field.

Some route-specific errors, such as contract execution failures, also preserve public fields such as data.dalpCode, data.retryable, and data.suggestedAction. Keep reading the route-specific data fields documented later in this guide. The DAPI error reference lists every current DALP-#### identifier with its status, retryability, and remediation guidance.

Use the typed fields in this order:

  1. Branch on id for programmatic handling and support triage.
  2. Read retryable before retrying. true means retrying can make sense after the cause is resolved; it does not mean retry the same request in a loop.
  3. Show message as the short user-facing summary.
  4. Use why to explain the failed condition.
  5. Use fix as the next action for the operator, administrator, or end user.
  6. Preserve details.requestId in logs and support tickets when it is present.

Do not parse message, why, or fix for control flow. Public copy can improve over time. Use id, status, category, and retryable for client logic.

function classifyDapiError(error: {
  id: string;
  retryable: boolean;
  message: string;
  why: string;
  fix: string;
  details?: { requestId?: string };
}) {
  return {
    code: error.id,
    retry: error.retryable ? "retry-after-fix" : "do-not-retry",
    userMessage: `${error.message} ${error.fix}`,
    supportReference: error.details?.requestId,
  };
}

Quick reference

Legacy codeStatusRetry?Action
BAD_REQUEST400NoFix request payload
UNAUTHORIZED401NoReauthenticate
FORBIDDEN403NoCheck role permissions
NOT_ONBOARDED403NoComplete user onboarding
SYSTEM_NOT_CREATED403NoInitialize platform first
USER_NOT_AUTHORIZED403NoRequest required role
NOT_FOUND404NoVerify resource exists
CONFLICT409NoResolve state conflict
RESOURCE_ALREADY_EXISTS409NoUse existing resource
INPUT_VALIDATION_FAILED422NoFix validation errors
TOKEN_PRECHECKS_INVALID_ADDRESS_VALID_ETHEREUM_0X_PREFIXED_HEX_CHARACTERS400NoProvide a valid 0x-prefixed Ethereum address for token and holder pre-checks
TOKEN_INTERFACE_NOT_SUPPORTED422NoUse compatible token contract
CONTRACT_ERROR422CheckRead data.retryable and data.dalpCode; fix non-retryable contract errors before retrying
LUNA_MOFN_QUORUM_EXPIRED408YesActivate the Luna partition, then resubmit the transaction
LUNA_MOFN_QUORUM_CLASSIFICATION_FAILED409NoInspect the Luna partition state directly before retrying
INTERNAL_SERVER_ERROR500YesRetry with exponential backoff; contact support if persistent
CONFIRMATION_TIMEOUT504NoCheck transaction status before retrying

Reading OpenAPI error envelopes

The generated OpenAPI document groups known route errors into one response envelope per HTTP status. For example, a route with multiple 422 failures shows one 422 DALP error response schema instead of a separate schema variant for every default message.

Use the envelope fields this way when you generate clients or inspect the API reference:

FieldHow to use it
definedtrue means the code is listed in DALP's public error registry. Treat an unrecognised code as an unexpected integration failure and log the full response.
codeBranch on this stable machine-readable code, such as INPUT_VALIDATION_FAILED or TOKEN_INTERFACE_NOT_SUPPORTED. Do not branch on message text.
statusMatch it to the HTTP response status. Use it for coarse retry handling before checking the exact code.
messageDisplay or log the short diagnostic copy. The message can change as public copy improves.
dataRead route-specific details when the schema documents them. The OpenAPI schema uses oneOf inside data only for error codes that carry extra data.

This shape is an OpenAPI documentation envelope. Direct DALP REST errors still return the public DALP object under error. Generated-client code should prefer stable identifiers over message text and use the DAPI error reference for the public DALP-#### registry.

Retry decision flowchart

Use this flowchart to determine whether to retry a failed request:

Rendering diagram...

Key principles:

  • 4xx errors: Do not retry, except for retryable CONTRACT_ERROR responses or LUNA_MOFN_QUORUM_EXPIRED. For retryable CONTRACT_ERROR responses, inspect data.dalpCode and any data.suggestedAction, then resolve the required condition. For Luna quorum expiry, activate the partition before resubmitting.
  • 5xx errors: Retry with exponential backoff, unless it's a blockchain revert.
  • Blockchain reverts: Check the revert reason, retrying won't help if the underlying issue persists.

Client errors (4xx)

Client errors usually indicate problems with the request itself. Retrying the same request will produce the same error unless the response is a retryable CONTRACT_ERROR or LUNA_MOFN_QUORUM_EXPIRED. Fix the underlying issue before retrying non-retryable client errors.

Authentication errors (401)

UNAUTHORIZED

Authentication is missing or invalid. The API key may be expired, revoked, or malformed.

  • Verify the API key includes the sm_dalp_ prefix
  • Check the key hasn't been deleted in the API Keys page
  • Confirm the X-Api-Key header is set correctly

Authorization errors (403)

FORBIDDEN

The authenticated user lacks permission for this operation.

  • Review the user's assigned roles
  • Check if the operation requires admin or system-level permissions
  • See Platform setup for role management

NOT_ONBOARDED

The user hasn't completed the onboarding process.

  • Direct the user to complete onboarding in the platform UI
  • Onboarding includes profile setup and wallet configuration
  • See User onboarding for the complete flow

SYSTEM_NOT_CREATED

The DALP platform hasn't been initialized. This occurs when accessing a fresh deployment before the first admin completes setup.

USER_NOT_AUTHORIZED

The user lacks the specific role required for this token operation.

For oRPC routes, data.requiredRoles can list the roles required by the operation. The same response can also include the public registry object under data.dapiError.

Resource errors (404, 409)

NOT_FOUND

The requested resource doesn't exist.

  • Verify the resource ID or address is correct
  • Check if the resource was deleted
  • Confirm the API path is correct (include /api suffix in base URL)

CONFLICT

The operation conflicts with the current system state.

  • Check if another operation is in progress
  • Verify the resource state hasn't changed since your last read

RESOURCE_ALREADY_EXISTS

Attempted to create a resource that already exists.

  • Query for the existing resource instead of creating
  • Use a unique identifier if creating a new resource

Workflow and custody approval conflicts

Workflow conflict

A workflow-backed operation returned HTTP 409 because the current workflow or resource state does not allow this call yet. Custody-backed signing is one subcase: the request may be waiting for a policy approval, a nonce reservation, or a Luna m-of-n quorum workflow.

  • Refresh the resource, deployment, or transaction status before submitting another write
  • Do not retry immediately with a different idempotency key
  • For custody-backed signing, wait for the active approval or quorum step to finish
  • Retry only after the required workflow, approval, or resource state has changed

Token creation idempotency conflicts

Token creation is a workflow-backed write. POST /api/v2/tokens and the legacy POST /token/create route can return HTTP 202 Accepted when DALP accepts the instruction but the creation workflow is still running. Treat that response as the normal pending path: poll the returned statusUrl and do not submit another create request with a new idempotency key.

HTTP 409 Conflict is reserved for idempotency states that DALP cannot safely attach to the current request.

409 conditionWhat it meansWhat to do
Expired idempotency keyThe original transaction request is outside the 24-hour idempotency window.Confirm the earlier request did not create the token before sending a new token creation request with a new key.
Cancelled workflowThe transaction request attached to the key was cancelled.Use a new key only after confirming the cancelled request did not create the token.
Different wallet selectionThe retry uses the same key but a different accountWalletId, smartWalletAddress, or forceEoa selection.Keep the original wallet selection for a retry. Use a new key only for a deliberate new instruction.
Existing request cannot be matchedDALP detected an idempotency-key collision but could not recover the existing transaction request for safe attach.Stop retrying and contact support with the diagnostics below.

Production integrations should send an Idempotency-Key on every token creation request and store it with the client-side instruction. Reuse that same key only for the same token creation instruction, signer wallet selection, chain, and executor mode. If the token payload changes, create a new client-side instruction and a new idempotency key; DALP does not use the token payload itself as the deduplication boundary.

Share these fields with SettleMint support when a token creation conflict remains unclear:

  • the HTTP status and public error code from the response
  • the request ID or trace ID returned by the API gateway
  • the Idempotency-Key used by the client
  • the route called, for example POST /api/v2/tokens or legacy POST /token/create
  • the token type, selected template ID when present, and the participant or wallet used to sign
  • the transaction status URL or transaction request identifier when the response includes one
  • the custody provider or HSM approval reference shown in the operator console, if the signer requires external approval

Use customer-facing copy that separates pending workflow states from idempotency conflicts:

SituationSafe wording
Custody approval pending"Token creation is waiting for signer approval. Poll the transaction status link and keep the original idempotency key recorded."
Workflow still running"DALP is still processing the earlier token creation request. Check the transaction status before submitting another create request."
Idempotency conflict"DALP could not attach this retry to the earlier token creation request. Confirm the earlier result before starting a new token creation."
Payload correction needed"The request must be corrected before retrying. Fix the validation or prerequisite error and send a new request only when the instruction changes."

LUNA_MOFN_QUORUM_EXPIRED

A Thales Luna 7 partition was waiting for m-of-n approval and the configured signing window expired before the operator quorum approved it.

  • Activate the partition on the HSM
  • Resubmit the transaction after approval
  • If this happens repeatedly, review the configured signing window with your platform operator

LUNA_MOFN_QUORUM_CLASSIFICATION_FAILED

DALP saw the Luna partition report as activated, but the signing call still returned m-of-n pending.

  • Inspect the partition state on the HSM directly
  • Retry only after the operator quorum is actually active
  • Contact support with the request ID if the HSM state and API response disagree

Token pre-check address errors (400)

TOKEN_PRECHECKS_INVALID_ADDRESS_VALID_ETHEREUM_0X_PREFIXED_HEX_CHARACTERS

Transfer and burn pre-checks validate the token address and each holder address before reading indexed balances. The API returns this error when the request contains an invalid 0x-prefixed Ethereum address.

  • Fix the token or holder address in the request before retrying
  • Do not retry the same payload with backoff
  • After fixing the address, handle any balance, freeze, or indexing state errors separately

Validation errors (422)

INPUT_VALIDATION_FAILED

Request data failed schema validation. For oRPC routes, data.errors can list the exact invalid fields. The same response can also include the public registry object under data.dapiError.

{
  "code": "INPUT_VALIDATION_FAILED",
  "status": 422,
  "message": "Input validation failed",
  "data": {
    "errors": ["amount: Expected positive number", "recipient: Invalid Ethereum address"],
    "dapiError": {
      "id": "DALP-0080",
      "category": "client",
      "status": 422,
      "retryable": false,
      "message": "Input validation failed",
      "why": "The request body or parameters did not match the API contract.",
      "fix": "Check the request fields against the API documentation and retry with valid values."
    }
  }
}

Review each data.errors entry and fix the corresponding field in your request.

TOKEN_INTERFACE_NOT_SUPPORTED

The token contract at the specified address doesn't implement the required interface.

The data.requiredInterfaces field lists the missing interfaces (e.g., ERC20, IYieldSchedule).

  • Verify you're using the correct token address
  • Check if the token type supports the requested operation

Server errors (5xx)

Server errors indicate temporary problems. Most can be resolved by retrying with exponential backoff.

General server errors (500)

INTERNAL_SERVER_ERROR

An unexpected error occurred on the server.

  • Retry with exponential backoff (1s, 2s, 4s delays)
  • Maximum 3 retry attempts
  • If errors persist, contact support with the request details

Timeout errors (504)

CONFIRMATION_TIMEOUT

A blockchain transaction was submitted but confirmation timed out. The transaction may still succeed, do not retry the original API call.

Do not retry the original request, this may create duplicate transactions. Check the transaction status first to determine whether to retry.

  • The data.transactionHash field in the error contains the transaction hash
  • Query GET /api/transaction/{transactionHash} to check if the transaction succeeded, reverted, or was never submitted
  • See Transaction tracking for the full timeout recovery flow

Blockchain transaction errors

When a blockchain transaction reverts, the API returns the revert information in the error details. Revert reasons come directly from smart contract custom errors.

Important: Blockchain reverts should not be retried without fixing the underlying issue. The same transaction will revert again.

Common revert reasons

These errors occur frequently during normal operations and typically require user action or data correction.

Balance and supply errors

ErrorDescriptionResolution
InsufficientTokenBalanceAccount lacks sufficient token balanceQuery balance before retrying; ensure user has enough tokens
ExceededCapMinting would exceed the token's supply capReduce mint amount or increase cap (if authorized)
InsufficientCollateralInsufficient collateral backing for mint operationAdd more collateral before minting

Permission and authorization errors

ErrorDescriptionResolution
AccessControlUnauthorizedAccountAccount lacks the required role for this operationGrant the required role to the account
TransferNotCompliantTransfer failed compliance checksVerify both parties meet compliance requirements
MintNotCompliantMint operation failed compliance checksVerify recipient meets compliance requirements
RecipientNotVerifiedRecipient doesn't meet identity verification requirementsComplete identity verification for recipient
ApprovalRequiredTransfer requires pre-approval from authorized partyRequest transfer approval before executing

Token state errors

ErrorDescriptionResolution
TokenPausedToken operations are paused by adminWait for admin to unpause the token
SenderAddressFrozenSender's address is frozenContact admin to unfreeze the address
RecipientAddressFrozenRecipient's address is frozenContact admin to unfreeze the address

Identity and compliance errors

ErrorDescriptionResolution
IdentityNotRegisteredUser's identity is not registered in the systemRegister identity through onboarding flow
IdentityAlreadyRegisteredIdentity already exists for this userUse existing identity instead of creating new
ComplianceCheckFailedGeneric compliance check failureReview compliance requirements for the operation

Validation errors

ErrorDescriptionResolution
ZeroAddressNotAllowedZero address provided where non-zero requiredProvide a valid non-zero address
LengthMismatchArray lengths don't match in batch operationsEnsure all arrays have equal length
InvalidDecimalsToken decimals value is invalid (typically >18)Use valid decimals value (0-18)

Deployment workflow errors

Long-running deployment workflows can fail after the initial request has been accepted. REST deployment endpoints return a 422 CONTRACT_ERROR envelope when they need to expose a deployment workflow failure to the client. Deployment SSE streams expose the same public contract-error data when they report failed deployment steps. The envelope is designed for client handling and user-facing support flows:

{
  "code": "CONTRACT_ERROR",
  "status": 422,
  "message": "Contract operation failed",
  "data": {
    "dalpCode": "DALP-WORKFLOW-FAILED",
    "message": "The deployment workflow failed before it could finish.",
    "retryable": true,
    "selector": "0x00000000",
    "solidityError": "WorkflowFailed()",
    "correlationId": "deployment-id"
  }
}

This is one deployment CONTRACT_ERROR surface exposed through both deployment transports:

  • REST deployment endpoints: return 422 CONTRACT_ERROR with data.dalpCode and data.correlationId when a deployment workflow failure must be projected to the caller.
  • SSE deployment streams: report the same deployment error surface in failed deployment completions under complete.failedSteps; each failed step can include a decoded wireError.

Use data.dalpCode for REST programmatic handling and data.correlationId when asking support to investigate a failed deployment.

Each SSE failed step's wireError uses the same public CONTRACT_ERROR.data shape shown above. The older failedSteps[].error field is legacy text and mirrors the sanitized public wireError.message; new clients should read failedSteps[].wireError for structured handling.

The deployment error payload intentionally contains public troubleshooting fields only. It does not expose raw provider responses, RPC URLs, stack traces, contract call context, or nested cause-chain diagnostics.

Operators can still investigate those diagnostics through logs and traces. Client-facing API responses only include the public envelope.

Typed platform errors, such as feature-gating or state conflicts, keep their normal error codes instead of being converted into CONTRACT_ERROR. Handle those errors according to the quick reference above.

For REST responses, base retry behavior on the public fields in data: data.retryable, data.dalpCode, and data.suggestedAction when present. For SSE deployment stream failures, read those values from the failed-step payload: complete.failedSteps[].wireError.retryable, complete.failedSteps[].wireError.dalpCode, and complete.failedSteps[].wireError.suggestedAction when present.


Retry strategies

Exponential backoff

For 5xx errors (except blockchain reverts), use exponential backoff:

AttemptWait time
11 second
22 seconds
34 seconds
4+Fail

Add jitter (random 0-500ms) to prevent thundering herd problems when multiple clients retry simultaneously.

Transaction confirmation

For operations that submit blockchain transactions:

  1. The API polls for confirmation automatically
  2. If CONFIRMATION_TIMEOUT occurs, do not retry the original request
  3. Query GET /api/transaction/{transactionHash} to check if the transaction was processed
  4. The transaction hash (available in X-Transaction-Hash response header or error response) can be used to verify on-chain status
  5. Only submit a new request after confirming the original transaction failed

Multi-transaction operations: Some API calls submit more than one blockchain transaction. Raw HTTP responses emit one X-Transaction-Hash header per transaction hash. Typed clients should use meta.txHashes as the canonical list because repeated headers can be harder to consume consistently. Treat each hash as a transaction to poll. For parallel submission paths, do not infer completion from list order or assume a timeout applies only to the last hash.

See Transaction tracking for detailed polling patterns and response interpretation.


Best practices

Log errors with context

Include the error code, request path, and relevant IDs in your logs. This enables faster debugging when issues occur in production.

Set reasonable timeouts

Configure client timeouts appropriate for the operation type:

  • Read operations: 10-30 seconds
  • Write operations: 60-90 seconds (blockchain transactions take time)

Fail fast on client errors

Don't retry 4xx errors unless the response is a retryable CONTRACT_ERROR or LUNA_MOFN_QUORUM_EXPIRED. For retryable CONTRACT_ERROR responses, inspect data.dalpCode and any data.suggestedAction. Retry only after the required user action, approval, or external state change is complete. Surface non-retryable client errors to users or fix them programmatically.

Circuit breaker for repeated failures

If a specific endpoint fails repeatedly (5+ times in a minute), pause requests to that endpoint temporarily. This prevents cascading failures and allows the service to recover.

Idempotency for critical operations

For financial operations, ensure your integration can handle duplicate responses safely. Network issues may cause a successful request to appear failed, leading to retry attempts on already-completed operations.


Next steps

On this page