Skip to content

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.

object
wallet_id
required
string
intent

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.

string
to

EVM convenience: recipient 0x address

string
amount

EVM convenience: base units, decimal string

string
asset

EVM convenience: native if omitted; otherwise a token contract address

string
max_fee

Optional fee ceiling (base units)

string
idempotency_key

Your unique key for safe retries (for Bitcoin it is carried inside the signed intent)

string
<= 64 characters
intent_signature
required

Your Ed25519 signature over the canonical intent

string
end_user_jwt

Hosted tier: an end-user token authorizing this one signature

string
Examples

EVM native transfer (convenience fields)

{
"wallet_id": "wlt_9f3a",
"to": "0x000000000000000000000000000000000000dEaD",
"amount": "5000000000000000",
"idempotency_key": "order-4417",
"intent_signature": "3b1c…ee02"
}

Transaction accepted.

object
id
required
string
wallet_id
required
string
state
required

Lifecycle: created → checks → signedbroadcastconfirmedfinal. Terminal failures are failed (broadcast/chain error) or rejected (refused by policy, limits, or a bad intent).

string
Allowed values: created policy_checked awaiting_cosigner signed broadcast confirmed final failed rejected
chain_tx_hash

The on-chain transaction hash, once broadcast

string
error_code

Set when state is failed/rejected (see Error codes)

string
created_at
string format: date-time

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
error
required
string
code
required

Stable machine slug (invalid_request, unauthorized, forbidden, not_found, conflict, rate_limited); a refused sign names the reason in the message

string
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
error
required
string
code
required

Stable machine slug (invalid_request, unauthorized, forbidden, not_found, conflict, rate_limited); a refused sign names the reason in the message

string
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
error
required
string
code
required

Stable machine slug (invalid_request, unauthorized, forbidden, not_found, conflict, rate_limited); a refused sign names the reason in the message

string
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
error
required
string
code
required

Stable machine slug (invalid_request, unauthorized, forbidden, not_found, conflict, rate_limited); a refused sign names the reason in the message

string
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
error
required
string
code
required

Stable machine slug (invalid_request, unauthorized, forbidden, not_found, conflict, rate_limited); a refused sign names the reason in the message

string
Example
{
"error": "vault is not active",
"code": "conflict"
}