Error Reference

Every error response includes a request_id for debugging and an optional error_code for programmatic handling.

Error Response Format

{
  "error": "Human-readable error message",
  "error_code": "machine_readable_code",  // optional
  "request_id": "req_abc123def456"
}

Always check error_code first for programmatic handling. Fall back to the HTTP status code if no error_code is present.

HTTP Status Codes

400Bad Request

The request body is missing required fields, has invalid types, or fails validation.

{"error": "participant_id is required", "request_id": "req_abc123"}
401Unauthorized

The API key is missing, invalid, or expired.

{"error": "Unauthorized", "request_id": "req_abc123"}
403Forbidden

The API key does not have permission for this endpoint, or the ledger is inactive.

{"error": "Insufficient scope for this endpoint", "request_id": "req_abc123"}
404Not Found

The requested resource does not exist in this ledger.

{"error": "Participant not found", "request_id": "req_abc123"}
409Conflict

A duplicate resource exists (idempotency). The existing resource is usually returned in the response.

{"error": "Transaction with this reference already exists", "error_code": "duplicate_reference_id", "existing_transaction_id": "uuid", "request_id": "req_abc123"}
422Unprocessable Entity

The request is valid but cannot be processed (e.g., insufficient balance).

{"error": "Insufficient balance", "error_code": "insufficient_balance", "request_id": "req_abc123"}
429Rate Limited

Too many requests. Check the Retry-After and X-RateLimit-Reset headers.

{"error": "Rate limit exceeded. Please try again later.", "retry_after": 30, "request_id": "req_abc123"}
500Internal Error

An unexpected error occurred. The request_id can be shared with support for debugging.

{"error": "An unexpected error occurred", "request_id": "req_abc123"}
503Maintenance

The API is temporarily unavailable for maintenance.

{"error": "API is under maintenance. Please try again shortly.", "request_id": "req_abc123"}

Error Codes

These codes appear in the error_code field. Use them to handle specific failure conditions programmatically.

duplicate_reference_idHTTP 409

A transaction with this reference_id already exists. The existing transaction is returned.

What to do:

Use the existing transaction — this is idempotency working correctly. Do not retry with a different reference_id for the same operation.

duplicate_refund_referenceHTTP 409

A refund with this reference has already been processed.

What to do:

Check the refund status using the returned refund_id.

duplicate_reversal_referenceHTTP 409

This transaction has already been reversed.

What to do:

No action needed — the reversal was already applied.

insufficient_balanceHTTP 422

The wallet or participant account does not have enough balance for this operation.

What to do:

Check the current balance before retrying. Fund the wallet or wait for pending deposits to clear.

wallet_not_foundHTTP 404

No active wallet exists for this participant or user.

What to do:

Create the participant first via POST /v1/participants, then retry the wallet operation.

sale_already_reversedHTTP 409

This sale has already been reversed and cannot be refunded.

What to do:

Check the sale status. Reversed sales cannot be refunded.

sale_already_fully_refundedHTTP 409

This sale has already been fully refunded.

What to do:

No further refund is possible. Check refund history for this sale.

step_up_requiredHTTP 403

This action requires recent authentication (e.g., re-entering password or completing MFA).

What to do:

Prompt the user to re-authenticate, then retry with the new session.

recent_login_requiredHTTP 403

This sensitive action requires a login within the last few minutes.

What to do:

Ask the user to log in again, then retry.

Rate Limiting

Every response includes rate limit headers:

HeaderDescription
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait before retrying (only on 429)
X-Request-IdUnique request identifier for debugging
# Example: handling 429
if response.status == 429:
    wait = int(response.headers["Retry-After"])
    time.sleep(wait)
    retry()

Idempotency

All write endpoints support idempotent requests via reference_id. If you send the same reference_id twice, the second request returns the existing resource with a 409 status and error_code: "duplicate_reference_id".

curl -X POST https://api.soledgic.com/v1/record-sale \
  -H "x-api-key: slk_test_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "order_12345",
    "amount": 2500,
    "participant_id": "creator_001"
  }'

# Second call with same reference_id → 409, returns existing transaction