Errors
SatStacker Engine uses standard HTTP status codes and JSON error responses.
Most errors return a response body with a detail field:
{
"detail": "Invalid API key."
}
Validation errors may return a structured list:
{
"detail": [
{
"type": "missing",
"loc": ["body", "partner_user_id"],
"msg": "Field required"
}
]
}
Status codes
| Status | Meaning |
|---|---|
400 Bad Request | The request is syntactically valid but violates an API rule. |
401 Unauthorized | Missing, malformed, revoked, or invalid API key. |
403 Forbidden | The key is valid, but the partner or resource is not active. |
404 Not Found | The requested resource does not exist in your partner/environment scope. |
409 Conflict | The request conflicts with an existing record or execution state. |
422 Unprocessable Entity | Request body validation failed. |
429 Too Many Requests | Request was throttled. Not generally enforced during private integration. |
500 Internal Server Error | Unexpected SatStacker error. Retry if safe, then contact support. |
Authentication errors
Missing or malformed key:
{
"detail": "Missing or malformed Authorization header. Expected 'Authorization: Bearer <key>'."
}
Invalid key:
{
"detail": "Invalid API key."
}
Inactive partner:
{
"detail": "Partner is not active."
}
User errors
Consent is required when linking a user:
{
"detail": "User consent is required."
}
A disabled or deleted partner user cannot create active plans:
{
"detail": "Partner user is not active (status=disabled)."
}
If you attempt to disable a partner user that doesn't exist:
{
"detail": "Partner user not found."
}
Plan errors
If you create a plan for a user that has not been linked yet:
{
"detail": "Partner user not found. Create/link the partner user first."
}
If you try to reuse a partner_plan_id for a different user:
{
"detail": "partner_plan_id already exists under a different partner_user_id. Cancel the existing plan and create a new one with a fresh partner_plan_id if the plan is moving between users."
}
Confirmation field requirements by status
The required fields on POST /partner/v1/executions/{execution_id}/confirm depend on the status value:
| Status | partner_order_id | executed_at | usd_amount | btc_amount | execution_price | partner_fee_usd | failure_reason |
|---|---|---|---|---|---|---|---|
filled | required | required | required | required | required | optional, nonnegative | omit |
partial | required | required | required | required | required | optional, nonnegative | omit |
failed | required | optional | not recorded | not recorded | not recorded | stored as zero | required |
cancelled | required | optional | not recorded | not recorded | not recorded | stored as zero | required |
For failed/cancelled attempts, omit financial fields and send an optional UTC
executed_at for the definitive outcome; if omitted, SatStacker records the
confirmation time. Required fields return 422 when absent. For filled/partial,
omit failure_reason; it is not stored and including it can conflict with an
otherwise identical replay.
partner_order_id is always required so that retried confirmations can be deduped, regardless of outcome.
Execution confirmation errors
You must first retrieve an execution from GET /partner/v1/executions/due before confirming it. Confirming a pending execution returns:
{
"detail": "Execution cannot be confirmed while status='pending'. Poll /partner/v1/executions/due first, then confirm after execution."
}
If an execution has already been confirmed with different values:
{
"detail": "Execution already confirmed as status='filled' with partner_order_id='order_123'. An execution cannot be re-confirmed with different values."
}
If the same partner_order_id was already used for another execution:
{
"detail": "partner_order_id='order_123' is already recorded under execution_id='exec_...'."
}
If usd_amount is greater than the reserved execution amount:
{
"detail": "usd_amount cannot exceed reserved execution amount. reserved=5.00, received=6.00"
}
Idempotent confirmation replay
If you retry the same confirmation payload after it has already been recorded, SatStacker returns success with replayed: true:
{
"ok": true,
"execution_id": "exec_...",
"status": "filled",
"partner_order_id": "order_123",
"replayed": true
}
This means your retry was accepted and no duplicate trade was created.
Retry guidance
Safe to retry:
GET /partner/v1/meGET /partner/v1/executions/dueGET /partner/v1/billing/monthlyGET /partner/v1/billing/monthly/usersPOST /partner/v1/userswith the samepartner_user_idPOST /partner/v1/planswith the samepartner_plan_idPOST /partner/v1/executions/{execution_id}/confirmwith the exact same payloadGET /partner/v1/webhooksandDELETE /partner/v1/webhooks
POST /partner/v1/webhooks is a secret-rotating operation. Every successful call generates a new secret, including a call with an unchanged URL. It does not belong in a generic automatic retry loop. If its response is lost, coordinate a new registration and persist that successful response's secret before updating your verifier.
Do not retry by placing another market order. If a trade executed but confirmation failed, retry the confirmation request with the same partner_order_id.
Partner v2 lifecycle conflicts
409 Conflict also applies when reactivating a cancelled plan, changing an existing start_date, or resetting a window while a delivered execution is unresolved. Confirm the original instruction before changing amount/frequency or restarting Smart Timing. Never retry by placing a second exchange order.
For filled/partial confirmations, usd_amount must be positive with at most two decimal places, btc_amount must be positive with at most eight decimal places, and execution price must remain positive after cent rounding. Fees must be nonnegative. Invalid values return 422.
See Amounts, Fees and Execution Outcomes for the all-in fee convention, and Operations and Reconciliation for handling unknown exchange order outcomes.