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
The request body is missing required fields, has invalid types, or fails validation.
{"error": "participant_id is required", "request_id": "req_abc123"}The API key is missing, invalid, or expired.
{"error": "Unauthorized", "request_id": "req_abc123"}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"}The requested resource does not exist in this ledger.
{"error": "Participant not found", "request_id": "req_abc123"}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"}The request is valid but cannot be processed (e.g., insufficient balance).
{"error": "Insufficient balance", "error_code": "insufficient_balance", "request_id": "req_abc123"}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"}An unexpected error occurred. The request_id can be shared with support for debugging.
{"error": "An unexpected error occurred", "request_id": "req_abc123"}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 409A 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 409A refund with this reference has already been processed.
What to do:
Check the refund status using the returned refund_id.
duplicate_reversal_referenceHTTP 409This transaction has already been reversed.
What to do:
No action needed — the reversal was already applied.
insufficient_balanceHTTP 422The 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 404No 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 409This 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 409This sale has already been fully refunded.
What to do:
No further refund is possible. Check refund history for this sale.
step_up_requiredHTTP 403This 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 403This 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:
| Header | Description |
|---|---|
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds to wait before retrying (only on 429) |
X-Request-Id | Unique 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