Essentials
Gas sponsorship
Octo can pay the network fees for your users' Stellar transactions out of your master wallet, so your users can hold and move stablecoins without ever buying XLM for gas. Every sponsored transaction is logged with per-wallet spend controls so the feature can't be abused.
How it works
Stellar natively supports fee-bump transactions: one account (your master wallet) pays the fee for another account's transaction. Here's the flow:
- Your user signs a payment or path-payment transaction XDR (the inner transaction).
- Your backend POSTs that XDR to Octo's
/sponsorendpoint. - Octo validates the op types, checks your spend limits, wraps the XDR in a fee-bump envelope signed by your master wallet, and submits it to the Stellar network.
- Octo returns the outcome immediately and fires a
transaction.sponsoredwebhook when you have endpoints registered.
Enable & configure sponsorship
/v1/wallets/:id/sponsorshipEnable or disable gas sponsorship for a wallet and set per-transaction fee caps and the daily budget. Requires a dashboard login token.
| Field | Type | Description |
|---|---|---|
| enabledrequired | boolean | Turn sponsorship on or off for this wallet. |
| per_tx_fee_cap_stroops | integer | Maximum fee (in stroops) the master wallet will pay per sponsored transaction. Must be ≤ daily_budget_stroops. Defaults to 100 000 (0.01 XLM) if omitted. |
| daily_budget_stroops | integer | Maximum total fees (in stroops) the master wallet will spend on sponsorship per calendar day (UTC). Defaults to 100 000 000 (10 XLM) if omitted. |
curl -X PUT http://localhost:8080/v1/wallets/<WALLET_ID>/sponsorship \\
-H "authorization: Bearer eyJ…" \\
-H "content-type: application/json" \\
-d '{
"enabled": true,
"per_tx_fee_cap_stroops": 100000, // 0.01 XLM
"daily_budget_stroops": 50000000 // 5 XLM
}'{
"statusCode": 200,
"message": "OK",
"data": {
"wallet_id": "52775…",
"enabled": true,
"per_tx_fee_cap_stroops": 100000, // 0.01 XLM
"daily_budget_stroops": 50000000, // 5 XLM
"created_at": "2026-06-15T10:30:00Z",
"updated_at": "2026-06-15T12:45:00Z"
}
}Before any config is saved a wallet is disabled with no caps (both per_tx_fee_cap_stroops and daily_budget_stroops are null). Set them explicitly to bound spend.
Read current config
/v1/wallets/:id/sponsorshipReturns the current sponsorship config (or defaults if none has been saved). Requires a dashboard login token.
curl http://localhost:8080/v1/wallets/<WALLET_ID>/sponsorship \\
-H "authorization: Bearer eyJ…"{
"statusCode": 200,
"message": "OK",
"data": {
"wallet_id": "52775…",
"enabled": true,
"per_tx_fee_cap_stroops": 100000, // 0.01 XLM
"daily_budget_stroops": 50000000, // 5 XLM
"created_at": "2026-06-15T10:30:00Z",
"updated_at": "2026-06-15T12:45:00Z"
}
}Sponsor a transaction
/v1/wallets/:id/sponsorSubmit a user-signed inner transaction XDR. Octo validates it, wraps it in a fee-bump signed by the master wallet, and submits the result to Horizon. Accepts both JWT login tokens and API keys.
| Field | Type | Description |
|---|---|---|
| transaction_xdrrequired | string | Base64-encoded TransactionEnvelope XDR of the user's signed inner transaction. |
| max_base_fee_stroopsrequired | integer | Maximum fee (in stroops) the master wallet will pay for the fee-bump. Must be > 0 and ≤ the per-tx cap. |
curl -X POST http://localhost:8080/v1/wallets/<WALLET_ID>/sponsor \\
-H "authorization: Bearer octo_sk_test_abc123…" \\
-H "content-type: application/json" \\
-d '{
"transaction_xdr": "AAAAAgAAAAD…",
"max_base_fee_stroops": 100000 // 0.01 XLM
}'{
"statusCode": 201,
"message": "Created",
"data": {
"id": "3f1a9b2e…",
"status": "confirmed",
"inner_tx_hash": "7f18b2…",
"fee_bump_tx_hash": "a1c3d4…",
"fee_stroops": 100000 // 0.01 XLM
}
}{
"statusCode": 201,
"message": "Created",
"data": {
"id": "3f1a9b2e…",
"status": "failed",
"inner_tx_hash": "7f18b2…",
"fee_bump_tx_hash": null,
"fee_stroops": 100000 // 0.01 XLM
}
}Sponsored transactions history
/v1/wallets/:id/sponsored-transactionsRetrieve the paginated history of sponsored transactions for a wallet. Requires a dashboard login token.
| Field | Type | Description |
|---|---|---|
| limit | integer | Rows per page (default 50, max 200). |
| status | string | Filter by status: pending, confirmed, or failed. |
| before | string | Cursor for page-back; use the next_cursor from the previous response. |
curl "http://localhost:8080/v1/wallets/<WALLET_ID>/sponsored-transactions?limit=50&status=confirmed" \\
-H "authorization: Bearer eyJ…"{
"statusCode": 200,
"message": "OK",
"data": {
"rows": [
{
"id": "3f1a9b2e…",
"wallet_id": "52775…",
"inner_tx_hash": "7f18b2…",
"fee_bump_tx_hash": "a1c3d4…",
"fee_stroops": 100000, // 0.01 XLM
"status": "confirmed",
"error": null,
"created_at": "2026-06-15T14:32:00Z"
}
],
"next_cursor": "3f1a9b2e…"
}
}Spend controls
Every wallet has two guardrails that prevent runaway sponsorship costs:
- Per-transaction fee cap (
per_tx_fee_cap_stroops) — the most the master wallet will ever pay for a single fee-bump. Set it low enough that a burst of sponsor requests can't drain your wallet in one go. - Daily budget (
daily_budget_stroops) — the total fees the master wallet will pay across all sponsored transactions in a UTC calendar day. Each new sponsor request is checked against today's sum of confirmed fees; if it would push spending over the budget, Octo returns a 429.
Worked example
Suppose your wallet is configured with:
- Per-tx cap: 100 000 stroops (0.01 XLM)
- Daily budget: 5 000 000 stroops (0.5 XLM)
You can sponsor up to 50 transactions at 0.01 XLM each today. If you reach the budget cap before midnight UTC, further sponsor requests receive 429 Too Many Requests with the error code budget_exceeded. At midnight UTC the counter resets automatically.
Error reference
| HTTP status | Error code | Meaning |
|---|---|---|
400 | invalid_xdr | The transaction_xdr is not valid Stellar XDR, or it is a fee-bump envelope (not an inner transaction). |
400 | op_not_allowed | The inner XDR contains an operation type that is not on the allowlist (only Payment, PathPaymentStrictSend, and PathPaymentStrictReceive are permitted). |
400 | self_sponsorship | The inner transaction source account matches the master wallet — sponsoring yourself is rejected as a security safeguard. |
403 | sponsorship_disabled | Gas sponsorship is not configured, or is explicitly disabled, for this wallet. |
409 | duplicate_inner_tx | This inner transaction XDR has already been sponsored. Each inner transaction hash is unique — re-submitting the same XDR is rejected to prevent double-sponsoring. |
429 | budget_exceeded | The daily sponsorship budget for this wallet would be exceeded by this request. The counter resets at midnight UTC. |
Webhook events
If you have webhook endpoints registered for a wallet, Octo fires a transaction.sponsored event after every sponsor request is finalized. Delivery uses the same HMAC-SHA256 signed POST pattern as deposits.
X-Octo-Signature: <hmac-sha256 hex>
Content-Type: application/json
{
"event": "transaction.sponsored",
"data": {
"wallet_id": "52775…",
"inner_tx_hash": "7f18b2…",
"fee_bump_tx_hash": "a1c3d4…",
"fee_stroops": 100000,
"status": "confirmed",
"created_at": "2026-06-15T14:32:00Z"
}
}fee_bump_tx_hashisnullwhen the Horizon submission itself fails.- Delivery is best-effort and runs asynchronously — the HTTP response to the
/sponsorcall reflects the outcome before the webhook fires. - Verify the signature the same way you do for deposits; see Webhooks for the verification snippet.
GET /sponsored-transactions history.Audit trail
Every sponsorship action — config changes, successful submissions, on-chain failures, and policy rejections — is recorded in Octo's audit log under the sponsorship category. You can review these entries on the Audit log page. Rejected requests (bad XDR, forbidden op types, budget overruns) are also logged so abuse attempts always leave a trace.