Submit a transaction
POST /transactions
Submit a transaction for a wallet. You sign a canonical description of the
transaction (the intent) with your API key and send that signature —
XKOVA binds the signature to exactly what it will broadcast, so the
operator cannot alter the recipient or amount. Requires an
idempotency_key (safe to retry) and the intent_signature.
Asynchronous: returns 202 with the transaction in an early state; track
it via GET /transactions/{txId} or webhooks. Reaching broadcast means
the signature was accepted by the network’s own validity checks.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”object
Hex of the exact canonical intent bytes you signed. This is the
preferred path and is required for Bitcoin. When present,
to/amount/asset are ignored. The SDK builds these bytes for you.
EVM convenience: recipient 0x address
EVM convenience: base units, decimal string
EVM convenience: native if omitted; otherwise a token contract address
Optional fee ceiling (base units)
Your unique key for safe retries (for Bitcoin it is carried inside the signed intent)
Your Ed25519 signature over the canonical intent
Hosted tier: an end-user token authorizing this one signature
Examples
EVM native transfer (convenience fields)
{ "wallet_id": "wlt_9f3a", "to": "0x000000000000000000000000000000000000dEaD", "amount": "5000000000000000", "idempotency_key": "order-4417", "intent_signature": "3b1c…ee02"}Signed canonical intent (required for Bitcoin, works for any chain)
{ "wallet_id": "wlt_9f3a", "intent": "786d…6431", "intent_signature": "3b1c…ee02"}Responses
Section titled “ Responses ”Transaction accepted.
object
Lifecycle: created → checks → signed → broadcast → confirmed →
final. Terminal failures are failed (broadcast/chain error) or
rejected (refused by policy, limits, or a bad intent).
The on-chain transaction hash, once broadcast
Set when state is failed/rejected (see Error codes)
The request body or parameters failed validation.
Failures return a single human-readable error message; the HTTP status
carries the category (400 validation, 401 auth, 403 refused, 404 missing,
409 conflict, 429 rate limit). When a signing request is refused for a
specific reason, the message names it — for example the amount exceeds a
spend limit, the policy denied the recipient, or the intent signature did
not verify.
object
Stable machine slug (invalid_request, unauthorized, forbidden, not_found, conflict, rate_limited); a refused sign names the reason in the message
Example
{ "error": "amount must be a base-10 integer", "code": "invalid_request"}Missing, malformed, or invalid request signature.
Failures return a single human-readable error message; the HTTP status
carries the category (400 validation, 401 auth, 403 refused, 404 missing,
409 conflict, 429 rate limit). When a signing request is refused for a
specific reason, the message names it — for example the amount exceeds a
spend limit, the policy denied the recipient, or the intent signature did
not verify.
object
Stable machine slug (invalid_request, unauthorized, forbidden, not_found, conflict, rate_limited); a refused sign names the reason in the message
Example
{ "error": "request signature did not verify", "code": "unauthorized"}Refused by policy or spend limits.
Failures return a single human-readable error message; the HTTP status
carries the category (400 validation, 401 auth, 403 refused, 404 missing,
409 conflict, 429 rate limit). When a signing request is refused for a
specific reason, the message names it — for example the amount exceeds a
spend limit, the policy denied the recipient, or the intent signature did
not verify.
object
Stable machine slug (invalid_request, unauthorized, forbidden, not_found, conflict, rate_limited); a refused sign names the reason in the message
Example
{ "error": "transaction exceeds the vault's per-transaction limit", "code": "cap_exceeded"}The resource does not exist (or is not visible to your org).
Failures return a single human-readable error message; the HTTP status
carries the category (400 validation, 401 auth, 403 refused, 404 missing,
409 conflict, 429 rate limit). When a signing request is refused for a
specific reason, the message names it — for example the amount exceeds a
spend limit, the policy denied the recipient, or the intent signature did
not verify.
object
Stable machine slug (invalid_request, unauthorized, forbidden, not_found, conflict, rate_limited); a refused sign names the reason in the message
Example
{ "error": "vault not found", "code": "not_found"}The resource is not in a state that allows this operation.
Failures return a single human-readable error message; the HTTP status
carries the category (400 validation, 401 auth, 403 refused, 404 missing,
409 conflict, 429 rate limit). When a signing request is refused for a
specific reason, the message names it — for example the amount exceeds a
spend limit, the policy denied the recipient, or the intent signature did
not verify.
object
Stable machine slug (invalid_request, unauthorized, forbidden, not_found, conflict, rate_limited); a refused sign names the reason in the message
Example
{ "error": "vault is not active", "code": "conflict"}