API reference menu
API reference · v0.11.3
Phala Pay API
Phala Pay is a non-custodial crypto payments API for merchants: quotes and deposit addresses, deposits credited at a small confirmation depth and watched to finality, refunds you pay from your treasury, and webhooks signed with a key bound to a TDX attestation. The integration guide walks through an integration end to end.
- Production: USDC and USDT on Ethereum, USDC on Base; live and test mode
- https://
pay- api. phala. com - Staging: Sepolia, for integration tests
- https://
pay- api- staging. phala. com
Authentication
Send an API key as Authorization: Bearer ppay_rk_test_… (test mode) or ppay_rk_live_… (live mode). A restricted key (ppay_rk_, Stripe) holds only the permissions it was created with and never manages keys, treasuries, webhook endpoints, webhook keys, or account settings: run production with one. A secret key (ppay_sk_) holds every permission; keep it offline, for administration. The key selects the account and the mode; every object carries livemode, and a key never sees the other mode's objects.
Request ids
Every response carries Request-Id: req_… (Stripe). Quote it when you contact support. An event caused by an API request names it in request.id, with the Idempotency-Key the request sent.
Idempotent requests
Every POST accepts an Idempotency-Key of up to 255 characters (Stripe). For 24 hours a repeat of the same request returns the first response, with Idempotent-, whatever it was, including a 500, so a retry never runs a request twice; a repeat with another request is 400 idempotency_, and one while the first still runs is 409 idempotency_. A request that did not execute is not saved and runs again on a retry: one that failed validation (parameter_*), was rate limited (429), or met 503 unavailable.
Pagination
Lists are newest first with Stripe's cursor pagination (Stripe): limit (1 to 100, default 10), and starting_after or ending_before, the id of an object of the list; has_more says whether more follow. Lists with a created filter take created[gt], created[gte], created[lt], and created[lte] in Unix seconds.
Events
Every change is an event, delivered to your webhook endpoints and listed by GET /v1/events (Stripe). data.object is the object as it was when the event happened, rendered with the change and never changed afterwards; *.updated events add data.. Deliveries are retried until delivered and never given up on; a webhook endpoint's pending_, oldest_, and last_attempt show whether it is keeping up, and GET /v1/ lists what it has not received.
Errors
Errors are Stripe's error object (Stripe): {"error": {"type", "code", "message", "param", "doc_url"}}. 400 means the request cannot succeed as sent or in the objects' current state, 401 authentication, 403 permission, 404 a missing object, 409 only an Idempotency-Key still in use, 429 too many requests (with Retry-After in seconds), and 5xx the service. Branch on code; message may change.
refund_attachment_limit_exceeded
422. The environment has a configured attached-pending refund limit (production 2, staging 1), and permits one new attachment per rolling 24 hours, across all merchants and modes. This is not retryable; contact the operator before sending or attaching another payout. The refund stays pending and reserved. Repeating the same attachment does not consume quota; existing attachments continue verification.
address_capacity_reached
422. The chain has reached its permanent pilot limit of 1,000 issued addresses, including retired and expired addresses. This is not retryable; the operator must upgrade the scanning/
chain_unavailable
503. The requested chain is temporarily not ready. Retry after the indicated delay.
price_unavailable
503. Fresh dual-source prices are unavailable or the UTC daily snapshot cap is exhausted. Retry after the indicated delay; no stale price is returned.
parameter_invalid
400. A parameter is malformed or out of range; param names it. Fix the request.
parameter_missing
400. A required parameter is absent; param names it.
parameter_unknown
400. The operation does not take a parameter the request sent; param names it.
amount_too_small
400. The amount is below the minimum of the route, or nothing is left to refund.
amount_too_large
400. The amount is above the route's maximum deposit, or above what is left to refund.
exposure_cap_exceeded
400. The quote would take the open quotes of your account in this mode past a cap: their number (max_open_quotes of GET /v1/config), their credit (max_), or one customer's credit (max_). Wait for quotes to be paid, expire, or be canceled, or ask the operator to raise the cap.
paused
400. A pause of the account, the customer, or the route blocks the operation (quotes, settlement, or refunds). Your own quotes pause lifts with POST /v1/; the operator lifts its own.
chain_frozen
400. Reconciliation froze the chain pending the operator's review; no quote is issued on it until the operator lifts the block.
asset_not_accepted
400. Your payment settings do not accept the asset on the chain in this mode, or accept nothing yet (GET /v1/): quote an asset GET /v1/config lists, or accept it with POST /v1/.
payment_settings_unconfirmed
400. After a restore of the service, your payment settings wait for your reconfirmation: send your complete configuration with POST /v1/.
treasury_not_set
400. The account has no treasury on the chain: POST /v1/, then POST /v1/treasuries.
treasury_proof_invalid
400. The treasury proof does not prove the address: the message is not the challenge, or the signature does not verify.
treasury_challenge_expired
400. The treasury challenge expired; request a new one.
treasury_challenge_used
400. The treasury challenge was already used; request a new one.
treasury_not_deployed
400. The signature does not recover to the address and no contract is deployed there at the chain's finalized block; deploy the Safe first (ERC-6492 signatures are refused).
treasury_sanctioned
400. A sanctions list names the treasury address.
treasury_change_pending
400. A treasury change is already pending on the chain; cancel it first.
treasury_unchanged
400. The address is already the chain's treasury.
treasury_unexpected_state
400. Only a pending treasury can be canceled.
quote_payment_received
400. The quote's address already received a payment, so the quote cannot be canceled.
quote_window_closed
400. The quote's payment window has closed, so it cannot be canceled; it expires on its own.
quote_unexpected_state
400. The quote is complete or expired; only an open quote can be canceled.
deposit_unexpected_state
400. Admin: only a deposit the pump processes, detected or confirmed, can be nudged; a credited deposit waits for its sweep, which only a finalized Flushed event records.
deposit_not_refundable
400. The deposit cannot be refunded: it is not credited or rejected, or it was reversed.
deposit_not_final
400. The deposit is not final yet and could still be reversed; request the refund once final is true (about 15 minutes after its block on Ethereum).
destination_sanctioned
400. A sanctions list names the refund's destination address.
transfer_already_used
400. The transfer log already pays another refund.
refund_unexpected_state
400. The refund's status does not allow the action: it is not pending, it is already marked paid with another transaction, or, being marked paid, it cannot be canceled.
api_key_inactive
400. The API key is revoked or already rolled.
last_api_key
400. The account's last active key of the mode cannot be revoked; create or roll a key first.
deposit_address_cap_exceeded
400. The account has its maximum of active deposit addresses in the mode.
deposit_address_retired
400. The deposit address is retired; rotate the customer's active address instead.
webhook_endpoint_cap_exceeded
400. The account has 16 webhook endpoints in the mode; delete one first.
webhook_endpoint_disabled
400. The webhook endpoint is disabled; enable it before resending events to it.
idempotency_key_reused
400. The Idempotency-Key was used with a different request (type idempotency_). Use a new key for a new request.
signature_invalid
401. Admin API only: the RFC 9421 request signature did not verify.
signature_replayed
401. Admin API only: the request signature was already used; sign the request again.
api_key_missing
401. No Authorization: Bearer header with an API key (ppay_rk_… or ppay_sk_…).
api_key_invalid
401. The API key is malformed, unknown, or revoked.
api_key_expired
401. The API key was rolled and its overlap has ended; use the key it was rolled to.
permission_denied
403. The key may not make this request: a restricted key lacks the permission, or the request manages keys, treasuries, webhook endpoints, webhook keys, or account settings, which needs a secret key.
testmode_charges_only
403. The account is not enabled for live mode; use a test key until the operator enables it.
resource_missing
404. No such object in the key's account and mode.
idempotency_key_in_use
409. A request with this Idempotency-Key is still running (type idempotency_); retry shortly with the same key. The only 409.
rate_limit
429. Too many requests of the account and mode (100 per second live, 25 test), or reads of one quote's or deposit address's public view. Retry after Retry-After seconds, with exponential backoff.
customer_rate_limit
429. The customer made too many quotes in the last minute, or rotated its deposit address too often in the last hour. Retry after Retry-After seconds.
internal_error
500. The service failed. Retry with the same Idempotency-Key: a request that started executing replays this response, so it never runs twice; a new key runs it again.
unavailable
503. A dependency (pricing, sanctions screening, attestation) is temporarily unavailable. Retry with backoff; the same Idempotency-Key runs the request again.
service_maintenance
503. A planned upgrade temporarily pauses new mutations. Reads continue while the process is up. Retry after Retry-After with the same Idempotency-Key; the request has not executed. The pause expires automatically if deployment fails.
service_restoring
503. The service was restored from backup and is frozen until the operator has reconciled it with you: every request with an API key is refused, reads too, and nothing is credited or delivered meanwhile. Retry after Retry-After seconds; the operator contacts you for your records since the restore point.
restore_not_frozen
400. Admin API only: a restore reconciliation action needs the service frozen after a restore.
restore_rescan_incomplete
400. Admin API only: a chain is not rescanned since the restore (GET /v1/); the freeze cannot be lifted yet.
Quotes
A locked price and a single-use address for one payment.
List quotes
get/v1/
The account's quotes in the key's mode, newest first, with Stripe's cursor pagination.
Query parameters
client_stringreference_ id Only this customer's quotes
statusstringopen,complete,expired, orcanceledlimitinteger (int64)1 to 100, default 10
starting_stringafter qt_id: the page after itending_stringbefore qt_id: the page before it
Responses
- 200
OK
Returns Quote
List - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/quotes \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Quote
Create quote
post/v1/
Quotes amount cents payable in asset on chain_id: a locked price, the exact token amount,
and a single-use address, valid until expires_at, on the terms your payment settings set for
the asset; the quote keeps them for good. An asset your settings do not accept is
asset_.
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Create Quote Request
amountinteger (int64)requiredThe credit to quote, a positive integer in the currency's minor unit (US cents).
assetstringrequiredAsset code of the payment on that chain, such as
usdc.chain_integer (int64)requiredid EVM chain of the payment, one of
GET /v1/configassets[]..chain_ id client_stringrequiredreference_ id Your identifier of the customer to credit, 1 to 200 characters (Stripe Checkout's
client_); the customer is created on first use.reference_ id currencystringrequiredLowercase ISO currency code; only
usd.metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. A key set to""is omitted. The deposit that pays the quote starts with a copy of it. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
Responses
- 200
OK
Returns Quote
- 400
Bad Request,
asset_,not_ accepted payment_,settings_ unconfirmed amount_too_small,amount_too_large,exposure_,cap_ exceeded paused,chain_frozen, ortreasury_not_set- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 422
Permanent chain
address_; not retryablecapacity_ reached - 429
rate_limit, orcustomer_: the customer's quotes per minute; retry afterrate_ limit Retry-Afterseconds- 503
chain_,unavailable price_or Service Unavailableunavailable
curl -X POST \ https://pay-api.phala.com/v1/quotes \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 2500, "asset": "usdc", "chain_id": 1, "client_reference_id": "team-42", "currency": "usd", "metadata": { "order_id": "ord_1001" } }'Response 200: Quote
Get quote
get/v1/
One quote, for example to resume a checkout page. The payer's browser can read the quote's
public view with its client_secret instead of an API key, as Stripe.js reads a PaymentIntent.
Path parameters
idstringrequiredQuote id,
qt_…
Query parameters
expand[]array of stringdeposit; API key requests onlyclient_stringsecret The quote's
client_secret, to read its public view without an API key. Send the request withoutAuthorization; the response then allows any origin.
Responses
- 200
OK: a
Quoteto a request with an API key, aClientQuoteto a request byclient_secretReturns Quote
View - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found, also for a
client_secretthat is not this quote's- 429
Too Many Requests: reads by
client_secret- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/quotes/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Quote
Update quote
post/v1/
Updates a quote's metadata, in any status; parameters not sent are left unchanged.
Path parameters
idstringrequiredQuote id,
qt_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Update Metadata Request
metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. Merged into the object's: a key set to a value is set, a key set to""is unset, other keys are kept, andmetadata: ""unsets every key. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
Responses
- 200
OK
Returns Quote
- 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/quotes/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "note": "", "order_id": "ord_1001" } }'Response 200: Quote
Cancel quote
post/v1/
Cancels an open, unpaid quote; a canceled quote is returned unchanged. Later payments to its address are credited at spot.
Path parameters
idstringrequiredQuote id,
qt_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Responses
- 200
OK
Returns Quote
- 400
quote_: the address already received a payment;payment_ received quote_: pastwindow_ closed expires_at;quote_: complete or expiredunexpected_ state - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/quotes/{id}/cancel \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Quote
Submit quote transaction
post/v1/
Submit a quote transaction hint. The stored quote selects the chain and recipient.
Path parameters
idstringrequiredQuote identifier
Query parameters
client_stringsecret Object's browser client secret; omit with a merchant key
Request body · Submit Quote Transaction Request
transaction_stringrequiredhash Transaction hash, exactly 32 hexadecimal bytes prefixed by
0x.
Responses
- 202
Received, including ignored hints. No payment result is disclosed.
Returns Transaction
Submission
curl -X POST \ https://pay-api.phala.com/v1/quotes/{id}/transactions \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "transaction_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e" }'Response 202: Transaction
Deposit addresses
A customer's persistent address, credited at spot on every chain and asset.
List deposit addresses
get/v1/
The account's deposit addresses in the key's mode, newest first, with Stripe's cursor pagination.
Query parameters
client_stringreference_ id Only this customer's addresses
statusstringactiveorretiredlimitinteger (int64)1 to 100, default 10
starting_stringafter da_id: the page after itending_stringbefore da_id: the page before it
Responses
- 200
OK
Returns Deposit
Address List - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/deposit_addresses \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Deposit
Create deposit address
post/v1/
Returns the customer's active deposit address, one address for every token your payment
settings accept on every network of the key's mode where you have a treasury (an account that
accepts nothing gets asset_), issuing it if the customer has
none: the same request always returns the same address until it is rotated. It also adds the
address's network on a chain supported, or given a treasury, since it was issued, and replaces
a chain's network whose treasury changed.
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Create Deposit Address Request
client_stringrequiredreference_ id Your identifier of the customer, 1 to 200 characters; the customer is created on first use.
metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. Merged into the returned address's, asPOST /v1/does: a key set todeposit_ addresses/ {id} ""is unset. Every deposit to the address starts with a copy of it, and a rotation carries it to the next version. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
Responses
- 200
OK
Returns Deposit
Address - 400
Bad Request,
asset_(your payment settings accept nothing in this mode),not_ accepted payment_,settings_ unconfirmed deposit_,address_ cap_ exceeded paused,chain_frozen(a new address and every chain is frozen or paused), ortreasury_not_set(no treasury on any chain that takes one)- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 422
Permanent chain
address_; not retryablecapacity_ reached - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/deposit_addresses \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "client_reference_id": "team-42", "metadata": { "plan": "pro" } }'Response 200: Deposit
Get deposit address
get/v1/
One deposit address. The customer's page can read the address's public view, with the
payments seen and credited in the last 24 hours, by a client_secret instead of an API key,
as a quote's page does.
Path parameters
idstringrequiredDeposit address id,
da_…
Query parameters
client_stringsecret A
client_secretof the address, to read its public view without an API key. Send the request withoutAuthorization; the response then allows any origin.
Responses
- 200
OK: a
DepositAddressto a request with an API key, aClientto a request byDeposit Address client_secretReturns Deposit
Address View - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found, also for a
client_secretthat is not this address's- 429
Too Many Requests: reads by
client_secret- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/deposit_addresses/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Deposit
Update deposit address
post/v1/
Updates a deposit address's metadata, active or retired; parameters not sent are left
unchanged. Deposits already recorded keep their own copy.
Path parameters
idstringrequiredDeposit address id,
da_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Update Metadata Request
metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. Merged into the object's: a key set to a value is set, a key set to""is unset, other keys are kept, andmetadata: ""unsets every key. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
Responses
- 200
OK
Returns Deposit
Address - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/deposit_addresses/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "note": "", "order_id": "ord_1001" } }'Response 200: Deposit
Rotate deposit address
post/v1/
Retires an active deposit address and returns the customer's new one, a new address on every network. Payments to the retired address are still credited at spot; stop showing it.
Path parameters
idstringrequiredDeposit address id,
da_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Responses
- 200
OK: the new active address
Returns Deposit
Address - 400
deposit_: already rotated;address_ retired paused,chain_frozen(every chain is frozen or paused),asset_,not_ accepted payment_, orsettings_ unconfirmed treasury_not_set- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 422
Permanent chain
address_; not retryablecapacity_ reached - 429
rate_limit, orcustomer_: the customer's rotations per hour; retry afterrate_ limit Retry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/deposit_addresses/{id}/rotate \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Deposit
Submit deposit address transaction
post/v1/
Submit a deposit-address transaction hint on one of its issued networks.
Path parameters
idstringrequiredDeposit-address identifier
Query parameters
client_stringsecret Object's browser client secret; omit with a merchant key
Request body · Submit Deposit Address Transaction Request
chain_integer (int64)requiredid One of this object's issued network identifiers.
transaction_stringrequiredhash Transaction hash, exactly 32 hexadecimal bytes prefixed by
0x.
Responses
- 202
Received, including ignored hints. No payment result is disclosed.
Returns Transaction
Submission
curl -X POST \ https://pay-api.phala.com/v1/deposit_addresses/{id}/transactions \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "chain_id": 84532, "transaction_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e" }'Response 202: Transaction
Deposits
Payments recorded on chain: credited, rejected, or reversed, and when they are final.
List deposits
get/v1/
The account's deposits in the credential's mode, newest first, with Stripe's cursor pagination.
Query parameters
client_stringreference_ id Only this customer's deposits
quotestringOnly deposits to this quote's address
deposit_stringaddress Only deposits to this deposit address,
da_…statusstringOnly deposits in this status:
pending,credited,rejected, orreversedtx_stringhash Only deposits in this transaction
created[gt]integer (int64)Created after, Unix seconds
created[gte]integer (int64)Created at or after, Unix seconds
created[lt]integer (int64)Created before, Unix seconds
created[lte]integer (int64)Created at or before, Unix seconds
limitinteger (int64)1 to 100, default 10
starting_stringafter dep_id: the page after itending_stringbefore dep_id: the page before itexpand[]array of stringdata.quote
Responses
- 200
OK
Returns Deposit
List - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/deposits \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Deposit
Get deposit
get/v1/
One deposit.
Path parameters
idstringrequiredDeposit id,
dep_…
Query parameters
expand[]array of stringquote
Responses
- 200
OK
Returns Deposit
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/deposits/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Deposit
Update deposit
post/v1/
Updates a deposit's metadata; parameters not sent are left unchanged. The quote's metadata is
not changed.
Path parameters
idstringrequiredDeposit id,
dep_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Update Metadata Request
metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. Merged into the object's: a key set to a value is set, a key set to""is unset, other keys are kept, andmetadata: ""unsets every key. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
Responses
- 200
OK
Returns Deposit
- 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/deposits/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "note": "", "order_id": "ord_1001" } }'Response 200: Deposit
Refunds
Refunds you pay from your treasury and attach with mark_paid.
List refunds
get/v1/
The account's refunds in the key's mode, newest first, with Stripe's cursor pagination.
Query parameters
depositstringOnly this deposit's refunds,
dep_…statusstringpending,succeeded,failed, orcanceledlimitinteger (int64)1 to 100, default 10
starting_stringafter re_id: the page after itending_stringbefore re_id: the page before it
Responses
- 200
OK
Returns Refund
List - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/refunds \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Refund
Create refund
post/v1/
Creates a pending refund of a final deposit (design D5): a rejected deposit other than a
sanctioned or dust one, or a credited one. The amount, the unrefunded remainder by default, is
reserved until the refund is canceled or fails. The destination must pass sanctions screening
(400 destination_). The merchant then pays it from the refund's treasury and
attaches the transaction with mark_paid.
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Create Refund Request
amount_string or nullatomic Amount in base units, as a decimal string; the unrefunded remainder when absent.
depositstringrequireddep_id of the deposit to refund.destination_stringrequiredaddress Address the customer controls; never default it to the sender, which may be an exchange.
metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. A key set to""is omitted. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
Responses
- 200
OK
Returns Refund
- 400
Bad Request,
destination_,sanctioned amount_too_small,amount_too_large,deposit_,not_ refundable deposit_, ornot_ final paused- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
Destination screening is unavailable; retry
curl -X POST \ https://pay-api.phala.com/v1/refunds \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount_atomic": "25000000", "deposit": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "destination_address": "0x1775c1326aa633546b0b5634ae2bef0ba7cbfc9a", "metadata": { "ticket": "support-311" } }'Response 200: Refund
Get refund
get/v1/
One refund.
Path parameters
idstringrequiredRefund id,
re_…
Query parameters
expand[]array of stringdeposit
Responses
- 200
OK
Returns Refund
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/refunds/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Refund
Update refund
post/v1/
Updates a refund's metadata, in any status; parameters not sent are left unchanged.
Path parameters
idstringrequiredRefund id,
re_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Update Metadata Request
metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. Merged into the object's: a key set to a value is set, a key set to""is unset, other keys are kept, andmetadata: ""unsets every key. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
Responses
- 200
OK
Returns Refund
- 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/refunds/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "note": "", "order_id": "ord_1001" } }'Response 200: Refund
Cancel refund
post/v1/
Cancels a pending refund that has no transaction attached and releases its reservation of the
deposit; canceling a canceled refund returns it. Once mark_paid attached a transaction, the
refund cannot be canceled, so that the deposit is never paid back twice: it stays reserved
until dual-source finalized verification ends it, succeeded, or failed when the transaction
does not pay it. A transaction never seen for 24 hours raises an alert and remains pending;
contact the operator before taking any further refund action.
Path parameters
idstringrequiredRefund id,
re_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Responses
- 200
OK
Returns Refund
- 400
refund_: marked paid, succeeded, or failedunexpected_ state - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/refunds/{id}/cancel \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Refund
Mark refund paid
post/v1/
Attaches the transaction that pays a pending refund, as BTCPay's payout mark-paid. At
finalized, both providers must show a Transfer of the deposit's token from the refund's
treasury to destination_ for exactly amount_atomic, in a log no other refund uses
(receipt_, or any such log when absent). Then the refund is succeeded and
deposit.refunded is sent; otherwise it is failed with a failure_reason. Repeating the same
transaction returns the refund. From here on the refund cannot be canceled: it is failed
only when its transaction is proven not to pay it.
Each environment has a configured attached-pending refund limit (production 2, staging 1),
and permits one new attachment per
rolling 24 hours across all accounts and modes. Repeating the same attachment consumes no
quota. A limit refusal preserves the reservation; contact the operator before another payout.
Path parameters
idstringrequiredRefund id,
re_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Mark Refund Paid Request
receipt_integer (int64) or nulllog_ index Position of the
Transferlog that pays the refund among the logs of the transaction's receipt (0 for the first), not the block-widelogIndex, which changes if the transaction is re-included in another block; any matching log when absent.transaction_stringrequiredhash Hash of the transaction that pays the refund from the treasury of the deposit's address.
Responses
- 200
OK
Returns Refund
- 400
Bad Request,
refund_(not pending, or marked paid with another transaction), orunexpected_ state transfer_already_ used - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 422
Non-retryable
refund_; contact the operator. The reservation is preserved.attachment_ limit_ exceeded - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/refunds/{id}/mark_paid \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "receipt_log_index": 0, "transaction_hash": "0x4b6d8f0a2c4e6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a6c" }'Response 200: Refund
Balance
Unswept amounts per chain and token.
Get balance
get/v1/
What the account's forwarders hold in the key's mode, per chain and token: deposits not reversed minus finalized sweeps, and the part of it from final deposits, which is safe to sweep. A forwarder's funds can only ever reach its treasury.
Responses
- 200
OK
Returns Balance
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/balance \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Balance
Sweeps
Finalized sweeps of forwarders to your treasuries.
List sweeps
get/v1/
The sweeps of the key's mode, newest first: every finalized Flushed event of a forwarder of
the account, whoever sent the flush. A deposit is swept once a sweep after it moved its
forwarder's balance.
Query parameters
chain_integer (int64)id Only this chain's sweeps
forwarderstringOnly this forwarder's sweeps,
fwd_…tokenstringOnly sweeps of this token contract
limitinteger (int64)1 to 100, default 10
starting_stringafter sw_id: the page after itending_stringbefore sw_id: the page before it
Responses
- 200
OK
Returns Sweep
List - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/sweeps \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Sweep
Forwarders
Every issued address, with what you need to sweep it.
List forwarders
get/v1/
Every forwarder issued in the key's mode, for quotes and for deposit address networks
(current and superseded), with the (factory, salt, treasury) its address derives from: the
export that keeps funds recomputable and sweepable without Phala Pay (design §13). With
sweepable, the forwarders to pass to the SDK's flush_ or safe_batch, one call
per chain and treasury. Pages follow id order.
Query parameters
chain_integer (int64)id Only this chain's forwarders
quotestringOnly this quote's forwarder,
qt_…deposit_stringaddress Only this deposit address's networks,
da_…sweepablestringA token contract: only forwarders with a final unswept balance of it that may be swept, never one holding a deposit rejected as sanctioned or paying a treasury a sanctions list names
limitinteger (int64)1 to 100, default 10
starting_stringafter fwd_id: the page after itending_stringbefore fwd_id: the page before it
Responses
- 200
OK
Returns Forwarder
List - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
sweepable: treasury screening is unavailable; retry
curl \ https://pay-api.phala.com/v1/forwarders \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Forwarder
Treasuries
Where each chain's forwarders pay, proven and time-locked.
List treasuries
get/v1/
The account's treasuries in the key's mode, newest first, with Stripe's cursor pagination: each
chain's active one, any pending change, and the replaced and canceled ones.
Query parameters
chain_integer (int64)id Only this chain's treasuries
statusstringpending,active,replaced, orcanceledlimitinteger (int64)1 to 100, default 10
starting_stringafter trs_id: the page after itending_stringbefore trs_id: the page before it
Responses
- 200
OK
Returns Treasury
List - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/treasuries \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Treasury
Create treasury
post/v1/
Sets the chain's treasury with a signed challenge. An EOA's personal_sign signature must
recover to the address; otherwise a contract deployed at the address must return 0x1626ba7e
from EIP-1271 isValidSignature for the message's EIP-191 hash at the chain's finalized
block on both of the service's RPC providers. The address is screened against sanctions lists.
The chain's first treasury, and any test-mode change, applies at once. A later live change is
pending for 48 hours, then applies (treasury.updated) unless canceled first: new quotes and
deposit address networks then pay it, while addresses issued before keep paying the former
treasury, which becomes replaced (treasury.updated), and are still credited. Every new
treasury is announced as treasury.created; treasury events go to every enabled webhook
endpoint of the mode.
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Create Treasury Request
chain_integer (int64)requiredid The treasury's chain, the challenge's.
messagestringrequiredThe challenge's
message, unchanged.signaturestringrequiredHex signature of the message: an EOA's 65-byte
personal_signsignature, or what a deployed contract'sisValidSignatureaccepts (for a Safe, the owners' signatures of the Safe message, or0xafterSignMessageLibapproved it). ERC-6492 signatures are refused.
Responses
- 200
OK:
active, orpendingfor a later live changeReturns Treasury
- 400
treasury_,proof_ invalid treasury_,challenge_ expired treasury_,challenge_ used treasury_(no contract at the address at the chain's finalized block; ERC-6492 signatures are refused),not_ deployed treasury_,sanctioned treasury_, orchange_ pending treasury_unchanged - 401
Unauthorized
- 403
Forbidden
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
The chain or sanctions screening could not be read; retry
curl -X POST \ https://pay-api.phala.com/v1/treasuries \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "chain_id": 1, "message": "pay-api.phala.com wants you to sign in with your Ethereum account:\n0x936c1991f8dA9a919fa11b557a3514719f5A4504\n\nSet this address as the test mode treasury of acct_0c6e1d0a9b3f4c2e8d7a6b5c4d3e2f10 on Phala Pay.\n\nURI: https://pay-api.phala.com\nVersion: 1\nChain ID: 1\nNonce: Kq3nV8xZt2mP6wRa\nIssued At: 2026-09-28T12:00:00Z\nExpiration Time: 2026-09-28T12:10:00Z", "signature": "0x5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e1b" }'Response 200: Treasury
Create treasury challenge
post/v1/
Issues the EIP-4361 message that proves address as your treasury on chain_id in the key's
mode. Sign it and send it to POST /v1/treasuries before expires_at: 10 minutes for an EOA,
24 hours for an address that holds code (a Safe, whose owners sign it as a Safe message); it
can be used once.
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Create Treasury Challenge Request
addressstringrequiredThe treasury address to prove: an EOA, or a contract deployed on the chain such as a Safe.
chain_integer (int64)requiredid The chain of the treasury: a chain of the key's mode (
GET /v1/config).
Responses
- 200
OK
Returns Treasury
Challenge - 400
Bad Request
- 401
Unauthorized
- 403
Forbidden
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/treasuries/challenge \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "address": "0x936c1991f8da9a919fa11b557a3514719f5a4504", "chain_id": 1 }'Response 200: Treasury
Get treasury
get/v1/
One treasury.
Path parameters
idstringrequiredTreasury id,
trs_…
Responses
- 200
OK
Returns Treasury
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/treasuries/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Treasury
Cancel treasury
post/v1/
Cancels a pending treasury change before it applies (treasury.); the
chain's current treasury stays. If you did not request the change, also roll your keys.
Path parameters
idstringrequiredTreasury id,
trs_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Responses
- 200
OK:
canceledReturns Treasury
- 400
treasury_: not pendingunexpected_ state - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/treasuries/{id}/cancel \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Treasury
Pause treasury
post/v1/
Pauses crediting of deposits to every forwarder over the treasury's address, for an incident
such as a compromised former treasury: new deposits stay pending, uncredited, and no
deposit.credited is sent until you resume; nothing already credited changes. Announced as
treasury.updated. Pausing a paused treasury returns it unchanged.
Path parameters
idstringrequiredTreasury id,
trs_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Responses
- 200
OK:
crediting_pausedReturns Treasury
- 401
Unauthorized
- 403
Forbidden
- 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/treasuries/{id}/pause \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Treasury
Resume treasury
post/v1/
Lifts your crediting pause of the treasury: the deposits it held are credited, each with its
deposit.credited. An operator's pause stays in crediting_ until the operator lifts
it. Announced as treasury.updated.
Path parameters
idstringrequiredTreasury id,
trs_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Responses
- 200
OK
Returns Treasury
- 401
Unauthorized
- 403
Forbidden
- 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/treasuries/{id}/resume \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Treasury
Events
Notifications and the audit log: every change, as it was when it happened.
List events
get/v1/
The account's events in the key's mode, newest first, with Stripe's cursor pagination: the
notifications webhooks deliver, and the audit log of every key, endpoint, and account change
with its actor. An event stays listed whether or not any endpoint received it.
Query parameters
typestringOnly events of this type, such as
deposit.credited, or of a group, such asdeposit.*types[]array of stringOnly events of these types, up to 20, each a type or a group; not with
type(https://docs. )stripe. com/ api/ events/ list delivery_booleansuccess false: only events with a delivery to a webhook endpoint that has not succeeded, pending or stopped;true: only events whose every delivery succeededcreated[gt]integer (int64)Created after, Unix seconds
created[gte]integer (int64)Created at or after, Unix seconds
created[lt]integer (int64)Created before, Unix seconds
created[lte]integer (int64)Created at or before, Unix seconds
limitinteger (int64)1 to 100, default 10
starting_stringafter evt_id: the page after itending_stringbefore evt_id: the page before it
Responses
- 200
OK
Returns Event
List - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/events \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Event
Get event
get/v1/
One event.
Path parameters
idstringrequiredEvent id,
evt_…
Responses
- 200
OK
Returns Event
Object Response - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/events/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Event
Resend event
post/v1/
Delivers an event again to one enabled endpoint, whether it was delivered there, stopped, or
never sent there (the Stripe CLI's events resend), with the same webhook-id and body. Use
it after re-enabling an endpoint for the events it missed.
Path parameters
idstringrequiredEvent id,
evt_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Resend Event Request
webhook_stringrequiredendpoint The enabled endpoint to deliver the event to again,
we_….
Responses
- 200
OK: queued for delivery
Returns Event
Object Response - 400
Bad Request, or
webhook_: enable the endpoint firstendpoint_ disabled - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found: no such event or endpoint
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/events/{id}/resend \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "webhook_endpoint": "we_9e1c3a5b7d2f40c6e8a0b2d4f6a8c0e1" }'Response 200: Event
Webhook endpoints
Where events are delivered, with delivery health.
List webhook endpoints
get/v1/
The key's mode's webhook endpoints, newest first, with Stripe's cursor pagination.
Query parameters
limitinteger (int64)1 to 100, default 10
starting_stringafter we_id: the page after itending_stringbefore we_id: the page before it
Responses
- 200
OK
Returns Webhook
Endpoint List - 400
Bad Request, or
webhook_: the mode already has 16 endpointsendpoint_ cap_ exceeded - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/webhook_endpoints \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Webhook
Create webhook endpoint
post/v1/
Registers a webhook endpoint in the key's mode, at most 16 per mode. It receives the events it
subscribes to and, whatever it subscribes to, every account event (account.*, api_key.*,
webhook_), starting with webhook_ about itself. Deliveries are
signed with the account's webhook key of the mode (GET /v1/attestation).
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Create Webhook Endpoint Request
descriptionstring or nullYour description, up to 5000 characters.
enabled_array of stringrequiredevents The event types to deliver, such as
deposit.credited, or["*"]for all.metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters.urlstringrequiredWhere to deliver events, up to 2048 characters, without credentials or fragment:
httpson port 443; in test mode alsohttpon port 80. Redirects are not followed.
Responses
- 200
OK
Returns Webhook
Endpoint Object - 400
Bad Request, or
webhook_: the mode already has 16 endpointsendpoint_ cap_ exceeded - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/webhook_endpoints \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "description": "Order fulfillment", "enabled_events": [ "deposit.credited", "deposit.reversed", "refund.failed" ], "metadata": { "team": "payments" }, "url": "https://example.com/phala-pay/webhooks" }'Response 200: Webhook
Get webhook endpoint
get/v1/
One webhook endpoint.
Path parameters
idstringrequiredEndpoint id,
we_…
Responses
- 200
OK
Returns Webhook
Endpoint Object - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/webhook_endpoints/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Webhook
Update webhook endpoint
post/v1/
Updates a webhook endpoint; parameters not sent are left unchanged. A change is announced as
webhook_, with the replaced values in data., to every
enabled endpoint, and first to this endpoint at the URL it had before, even when the change
disables it. Disabling stops its pending deliveries; enabling does not restart them (resend
with POST /v1/).
Path parameters
idstringrequiredEndpoint id,
we_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Update Webhook Endpoint Request
descriptionstring or nullA new description;
""unsets it.disabledboolean or nulltruedisables the endpoint,falseenables it. A disabled endpoint receives nothing and its pending deliveries stop; resend missed events withPOST /v1/.events/ {id}/ resend enabled_array of stringevents New event types, or
["*"].metadataMetadataParam Merged into the endpoint's metadata: a key set to
""is unset, andmetadata: ""unsets every key.urlstring or nullA new URL, as on creation.
Responses
- 200
OK
Returns Webhook
Endpoint Object - 400
Bad Request, or
webhook_: the mode already has 16 endpointsendpoint_ cap_ exceeded - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/webhook_endpoints/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "disabled": false, "enabled_events": [ "*" ] }'Response 200: Webhook
Delete webhook endpoint
delete/v1/
Deletes a webhook endpoint. webhook_ goes to every enabled endpoint, and
first to the deleted one; then it receives nothing more, and its pending deliveries stop.
Path parameters
idstringrequiredEndpoint id,
we_…
Responses
- 200
OK
Returns Deleted
Webhook Endpoint - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X DELETE \ https://pay-api.phala.com/v1/webhook_endpoints/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Deleted
Test webhook endpoint
post/v1/
Sends a webhook_ event about the endpoint to this endpoint only, enabled or
not, signed like every delivery: check your receiver and its signature verification with it.
There is no URL challenge.
Path parameters
idstringrequiredEndpoint id,
we_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Responses
- 200
OK: the test event, queued
Returns Event
Object Response - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/webhook_endpoints/{id}/test \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Event
Api keys
Your secret and restricted keys: create, roll, revoke.
List api keys
get/v1/
The keys of the requesting key's account and mode, newest first, without their secrets, with Stripe's cursor pagination.
Query parameters
limitinteger (int64)1 to 100, default 10
starting_stringafter key_id: the page after itending_stringbefore key_id: the page before it
Responses
- 200
OK
Returns Api
Key List - 400
Bad Request
- 401
Unauthorized
- 403
Forbidden
- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/api_keys \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Api
Create api key
post/v1/
Creates a key in the requesting key's mode: a secret key, or with type: restricted a
restricted key (ppay_rk_…) holding only permissions, Stripe's restricted keys. Run
production servers with a restricted key and keep secret keys for administration. The
response is the only time its secret is shown; a replay of the request (Idempotency-Key)
returns the key without it.
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Create Api Key Request
namestringThe key's label, at most 200 characters.
permissionsarray of stringA restricted key's permissions, required with
type: restricted: codes such asquotes.writeordeposits.read, where awriteincludes itsread. Grantable:account.read,api_keys.read,quotes.*,deposit_,addresses. * deposits.*,refunds.*,events.read,endpoints.read,treasury.read,sweeps.read,forwarders.read. Keys, treasuries, webhook endpoints, webhook keys, and account settings are managed only with a secret key.typestring or nullsecret, the default, orrestricted(Stripe's restricted keys): a key that holds onlypermissions.
Responses
- 200
OK: the key with its
secret, shown onceReturns Api
Key Object - 400
Bad Request
- 401
Unauthorized
- 403
Forbidden
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/api_keys \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "fulfillment worker", "permissions": [ "quotes.write", "deposit_addresses.write", "deposits.read", "events.read", "refunds.read" ], "type": "restricted" }'Response 200: Api
Get api key
get/v1/
One key of the requesting key's account and mode, without its secret.
Path parameters
idstringrequiredKey id,
key_…
Responses
- 200
OK
Returns Api
Key Object - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/api_keys/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Api
Revoke api key
delete/v1/
Revokes a key at once. The mode's last key that is neither revoked nor expiring cannot be revoked, so the account always keeps a working key; to replace a leaked last key, roll it and revoke it with the new key.
Path parameters
idstringrequiredKey id,
key_…
Responses
- 200
OK: revoked, or already revoked
Returns Api
Key Object - 400
last_api_key: the mode's last key that is neither revoked nor expiring; create or roll a key first- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X DELETE \ https://pay-api.phala.com/v1/api_keys/{id} \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Api
Roll api key
post/v1/
Rolls a key: returns a new key of the same type, name, and permissions, and the old key keeps
working for expires_in seconds (at most 7 days), Stripe's roll; 0, the default, revokes it
at once. A secret key may roll itself, keeping itself working for at least an hour
(expires_in ≥ 3600): the new key's secret is shown only in this response, and a replay omits
it, so if the response is lost, roll the new key (its id is in the replay) with the old key
while it still works. To stop the old key sooner, revoke it with the new key.
Path parameters
idstringrequiredKey id,
key_…
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Roll Api Key Request
expires_integer (int32)in Seconds the old key keeps working, up to 604800 (7 days); 0, the default, revokes it at once. A key rolling itself needs at least 3600.
Responses
- 200
OK: the new key with its
secret, shown onceReturns Api
Key Object - 400
Bad Request,
parameter_for a key rolling itself withinvalid expires_inunder 3600, orapi_key_inactive: revoked or already rolled- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 404
Not Found
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/api_keys/{id}/roll \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "expires_in": 86400 }'Response 200: Api
Account
Your account: settings, pauses, webhook signing keys.
Get account
get/v1/
The account the API key belongs to, in the key's mode.
Responses
- 200
OK
Returns Account
Object - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/account \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Account
Pause account
post/v1/
Pauses the account's quotes in both modes, for an emergency such as a leaked key during a
treasury time-lock (design §12): no quote, deposit address, or network is issued until you
resume. Payments to existing addresses keep being credited. Announced as account.updated.
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Account Self Pause Request
scopesarray of stringrequired["quotes"], the one scope a merchant pauses itself: no quote, deposit address, or network is issued while it is paused. Existing addresses keep being credited.
Responses
- 200
OK
Returns Account
Object - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/account/pause \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "scopes": [ "quotes" ] }'Response 200: Account
Resume account
post/v1/
Resumes the quotes you paused. A pause the operator set stays in paused_scopes until the
operator lifts it. Announced as account.updated.
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Account Self Pause Request
scopesarray of stringrequired["quotes"], the one scope a merchant pauses itself: no quote, deposit address, or network is issued while it is paused. Existing addresses keep being credited.
Responses
- 200
OK
Returns Account
Object - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/account/resume \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "scopes": [ "quotes" ] }'Response 200: Account
Roll webhook key
post/v1/
Rolls the webhook signing key of the key's mode: the next version signs every delivery from
now on, and the current one keeps signing beside it for expires_in seconds, so every
delivery carries one v1a signature per key until then. The overlap is 48 hours (the default,
the treasury time-lock) to 7 days in live mode, so a leaked key cannot cut off the key you
pinned; test mode also accepts 0, which stops it at once. The roll is announced as
account.updated, signed by the retiring key as well even after its overlap. Fetch and verify
the new public key with GET /v1/attestation, pin it next to the old one, and drop the old one
when it expires.
Headers
Idempotency-string or nullKey Up to 255 characters. A retry with the same key and request within 24 hours returns the first response, and of another request is
400 idempotency_.error
Request body · Roll Webhook Key Request
expires_integer (int32)in Seconds the current key keeps signing beside the new one: 172800 (48 hours, the treasury time-lock) to 604800 (7 days) in live mode, 0 to 604800 in test mode, where 0 stops it at once. Default 172800.
Responses
- 200
OK: the account with its new webhook key version
Returns Account
Object - 400
Bad Request
- 401
Unauthorized
- 403
Forbidden
- 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/account/webhook_keys/roll \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "expires_in": 172800 }'Response 200: Account
Attestation
TDX evidence binding your webhook signing keys.
Get attestation
get/v1/
TDX evidence binding a fresh nonce to the webhook public keys of the key's account and mode
(design D11). Verify the quote once with the dstack verifier, check that its report data is
report_data zero-padded to 64 bytes and that report_data binds your nonce, account, mode,
and the listed keys, then pin the public keys: they are stable across releases.
Query parameters
noncestringrequiredNon-empty hexadecimal nonce of at most 32 bytes.
Responses
- 200
OK
Returns Attestation
Response - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
Service Unavailable
curl \ https://pay-api.phala.com/v1/attestation \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Attestation
Config
The payable assets and their terms in the key's mode.
Get config
get/v1/
Your effective payment config in the key's mode: every asset your payment settings accept on a
chain where you have a treasury, with its terms (GET /v1/), and your
open-quote caps. An account that accepts nothing yet gets no asset.
Responses
- 200
OK
Returns Config
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/config \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Config
Payment settings
Get payment settings
get/v1/
Your payment settings in the key's mode: the chains and assets you accept and your terms on
each, with the operator's catalog of the mode in available. A new account accepts nothing
until you configure it.
Responses
- 200
OK
Returns Payment
Settings Object - 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl \ https://pay-api.phala.com/v1/payment_settings \ -H "Authorization: Bearer $PHALA_PAY_API_KEY"Response 200: Payment
Update payment settings
post/v1/
Updates your payment settings in the key's mode. A parameter not sent is unchanged; chains,
when sent, replaces the whole list, and a term an element does not send takes the operator's
default. Writes are last-write-wins. The settings govern quotes and deposit addresses issued
from now on, and every payment recorded after the change; a quote keeps the terms it was
issued with. After a restore of the service the settings are held until a POST with your
complete configuration, even unchanged, reconfirms them: chains is then required, and a
parameter not sent takes its default. Announced as payment_.
Headers
Idempotency-string or nullKey Up to 255 characters; for 24 hours a repeat of the same request returns the first response, and of another request is
400 idempotency_.error
Request body · Update Payment Settings Request
chainsarray of PaymentSettings Chain The chains to accept, replacing the list: each chain and asset of the key's mode once. An element's term not sent resets to the operator's default.
[]accepts nothing.quote_integer (int64) or nullcreations_ per_ customer_ per_ minute One customer's quote creations in a rolling minute, from 1 to the operator's maximum;
nullrestores the default.
Responses
- 200
OK
Returns Payment
Settings Object - 400
Bad Request
- 401
Unauthorized
- 403
permission_: the key lacks this permission, ordenied testmode_: live mode is not enabledcharges_ only - 409
idempotency_: a request with thiskey_ in_ use Idempotency-Keyis still running; retry with the same key- 429
rate_limit: retry afterRetry-Afterseconds- 503
unavailable: temporary service or database unavailability;service_: restore reconciliation is in progress. Retry-After, when present, is the minimum delay in secondsrestoring
curl -X POST \ https://pay-api.phala.com/v1/payment_settings \ -H "Authorization: Bearer $PHALA_PAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "chains": [ { "assets": [ { "asset": "usdc", "quote_spread_bps": 0 }, { "asset": "usdt" } ], "chain_id": 1, "confirmations": "12" } ] }'Response 200: Payment
Objects
Account Object
The account of the request's API key (GET /v1/account), in the key's mode.
charges_booleanrequiredenabled Whether the operator enabled live mode.
createdinteger (int64)requiredCreation time, Unix seconds.
idstringrequiredAccount id,
acct_….livemodebooleanrequiredThe mode of the key that reads it.
namestringrequiredDisplay name.
object"account"requiredAlways
account.paused_array of stringrequiredscopes Active account-level pause scopes, the operator's and your own (
POST /v1/): whileaccount/ pause quotesis listed, no quote, deposit address, or network is issued. Your resume lifts only your own pause.webhook_array of Webhookkeys Key Versionrequired The keys that sign this mode's webhooks: the current one first, then any previous one still signing during a rotation. Their public keys come from
GET /v1/attestation.
{ "charges_enabled": true, "created": 1787961600, "id": "acct_0c6e1d0a9b3f4c2e8d7a6b5c4d3e2f10", "livemode": false, "name": "Example Cloud", "object": "account", "paused_scopes": [], "webhook_keys": [ { "expires_at": null, "version": 2 }, { "expires_at": 1790640000, "version": 1 } ]}Account Self Pause Request
POST /v1/ and POST /v1/ body.
scopesarray of stringrequired["quotes"], the one scope a merchant pauses itself: no quote, deposit address, or network is issued while it is paused. Existing addresses keep being credited.
{ "scopes": [ "quotes" ]}Api Key List
GET /v1/api_keys response.
dataarray of ApiKey Objectrequired The mode's keys, newest first.
has_booleanrequiredmore Always
false: every key of the mode is listed.object"list"requiredAlways
list.urlstringrequiredThe list's path,
/v1/api_keys.
{ "data": [ { "created": 1790467200, "expires_at": null, "id": "key_6a8c0e2b4d1f43a5c7e9b1d3f5a7c9e1", "last_used": 1790553600, "livemode": false, "name": "fulfillment worker", "object": "api_key", "permissions": [ "account.read", "deposit_addresses.read", "deposit_addresses.write", "deposits.read", "events.read", "quotes.read", "quotes.write", "refunds.read" ], "redacted": "ppay_rk_test_…Yz4x", "status": "active", "type": "restricted" } ], "has_more": false, "object": "list", "url": "/v1/api_keys"}Api Key Object
An API key (design D7). secret is present only in the response that created it.
createdinteger (int64)requiredCreation time, Unix seconds.
expires_integer (int64) or nullat When a rolled key stops working, Unix seconds.
idstringrequiredKey id,
key_….last_integer (int64) or nullused Last use, Unix seconds, to the minute.
livemodebooleanrequiredThe key's mode.
namestringrequiredThe key's label.
object"api_key"requiredAlways
api_key.permissionsarray of stringA restricted key's permissions, such as
quotes.write;nullfor a secret key.redactedstringrequiredThe key's prefix and last four characters, such as
ppay_.sk_ test_ …a1B2 secretstring or nullThe whole key,
ppay_sk_…orppay_rk_…, shown once. Store it in a secret manager.statusstringrequiredactive;expiringfor a rolled key that still works untilexpires_at;expired;revoked.typestringrequiredsecret, holding every permission, orrestricted, holding onlypermissions.
{ "created": 1790467200, "expires_at": null, "id": "key_6a8c0e2b4d1f43a5c7e9b1d3f5a7c9e1", "last_used": 1790553600, "livemode": false, "name": "fulfillment worker", "object": "api_key", "permissions": [ "account.read", "deposit_addresses.read", "deposit_addresses.write", "deposits.read", "events.read", "quotes.read", "quotes.write", "refunds.read" ], "redacted": "ppay_rk_test_…Yz4x", "status": "active", "type": "restricted"}Attestation Response
TDX evidence binding a nonce to the webhook public keys of the caller's account in the caller's mode (design D11).
accountstringrequiredThe caller's account,
acct_….livemodebooleanrequiredThe caller's mode; each mode has its own key.
object"attestation"requiredAlways
attestation.report_stringrequireddata sha256(len(nonce) ‖ nonce ‖ len(account) ‖ account ‖ livemode ‖ (version ‖ public_key)*)as lowercase hexadecimal: lengths are one byte,accountis UTF-8,livemodeis one byte (1live,0test), and each key ofwebhook_keys, in order, is its version as 4 big-endian bytes and its 32 raw public-key bytes.tdx_stringrequiredquote Versioned dstack TDX quote bytes as lowercase hexadecimal.
webhook_array of Webhookkeys Key Objectrequired The keys that sign the account's deliveries in this mode: the current one first, then any previous one still signing during a rotation.
{ "account": "acct_0c6e1d0a9b3f4c2e8d7a6b5c4d3e2f10", "livemode": false, "object": "attestation", "report_data": "9f2c4e6a8b0d1f3a5c7e9b1d3f5a7c9e2b4d6f8a0c1e3a5b7d9f1c3e5a7b9d1f", "tdx_quote": "040002008100000000000000939a7233f79c4ca9940a0db3957f0607", "webhook_keys": [ { "expires_at": null, "public_key": "whpk_O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik=", "version": 1 } ]}Available Asset
An asset of the operator's catalog and its bounds.
acceptedbooleanrequiredWhether your settings list it.
assetstringrequiredAsset code.
contractstringrequiredToken contract address.
decimalsinteger (int32)requiredToken decimals.
enabledbooleanrequiredWhether its terms, each clamped to its bounds, can be used together; an accepted asset that is not enabled is not offered or credited until you or the operator change it.
max_Boundsdeposit_ atomic Atomicrequired Bounds of
max_.deposit_ atomic min_Boundsamount U64required Bounds of
min_amount.min_Boundsdeposit_ atomic Atomicrequired Bounds of
min_.deposit_ atomic min_Boundsrefund_ atomic Atomicrequired Bounds of
min_.refund_ atomic pricingstringrequiredspotorstablecoin.quote_integer (int32)requiredamount_ decimals Token decimals a quote's amount is rounded up to (the operator's).
quote_Boundsspread_ bps U64required Bounds of
quote_spread_bps.quote_Boundstolerance_ bps U64required Bounds of
quote_.tolerance_ bps quote_Boundsttl_ seconds U64required Bounds of
quote_.ttl_ seconds
Available Chain
A chain of the operator's catalog in the key's mode.
assetsarray of AvailableAssetrequired The chain's assets.
chain_integer (int64)requiredid EVM chain identifier.
confirmationsAvailableConfirmationsrequired The chain's confirmation floor, also its default.
statusstringrequiredactive(accepted, with a treasury),treasury_not_set(accepted, without a treasury: nothing is offered on it until you set one), ornot_configured.
Available Confirmations
A chain's confirmation floor.
defaultstringrequiredThe confirmation of a chain whose
confirmationsisnull: the floor.floorstringrequiredThe weakest confirmation you may require, a depth,
safe, orfinalized.
Balance
The account's balance held in its forwarders, in the key's mode (Stripe's Balance): what
payments put there and no finalized Flushed event has moved to a treasury yet.
livemodebooleanrequiredThe mode.
object"balance"requiredAlways
balance.unsweptarray of BalanceAmountrequired One entry per chain and token held.
{ "livemode": false, "object": "balance", "unswept": [ { "amount_atomic": "25000000", "asset": "usdc", "chain_id": 1, "final_amount_atomic": "25000000", "token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } ]}Balance Amount
A token's unswept amounts on one chain.
amount_stringrequiredatomic Every deposit not reversed, minus finalized sweeps, in base units, as a decimal string.
assetstring or nullAsset code;
nullfor a token without a route.chain_integer (int64)requiredid EVM chain identifier.
final_stringrequiredamount_ atomic The part of
amount_atomicfrom final deposits, which can no longer be reversed: what is safe to sweep.tokenstringrequiredThe token contract.
Bounds Atomic
The operator's default of a base-unit amount and its inclusive bounds, as decimal strings.
defaultstringrequiredThe value when you set none.
maxstringrequiredThe highest value you may set.
minstringrequiredThe lowest value you may set.
Bounds U64
The operator's default of an integer term and its inclusive bounds.
defaultinteger (int64)requiredThe value when you set none.
maxinteger (int64)requiredThe highest value you may set.
mininteger (int64)requiredThe lowest value you may set.
Client Deposit Address
The public view of a deposit address, read with its client_secret and without an API key,
for the customer's page. It has no account, customer, treasury, or metadata fields.
addressstring or nullThe address shared by every network, or
nullwhen a network's differs.idstringrequiredda_id.livemodebooleanrequiredWhether the address is in live mode; a test-mode page should say so.
networksarray of ClientDeposit Address Networkrequired The address on each supported network, with the tokens it takes there.
object"deposit_address"requiredAlways
deposit_address.paymentsarray of ClientDeposit Address Paymentrequired Payments to the address in the last 24 hours, newest first, at most 10: display only, never a reason to deliver anything.
statusstringrequiredactive, orretiredby a rotation (still credited; show the new address instead).
Client Deposit Address Network
A deposit address on one network, as its public view shows it.
addressstringrequiredThe forwarder address to pay on this chain.
assetsarray of DepositAddress Assetrequired The tokens accepted on this chain.
chain_integer (int64)requiredid EVM chain identifier.
typical_integer (int64)requiredcredit_ seconds Typical time from payment to credit, in seconds, at the confirmation this chain's payments are credited at, as
GET /v1/configreports it for each of the chain's tokens, which share the chain's one floor.
Client Deposit Address Payment
A payment to a deposit address, as its public view shows it.
amount_stringrequiredatomic Token amount in base units, as a decimal string.
assetstring or nullAsset code;
nullfor a token without a route.chain_integer (int64)requiredid EVM chain identifier.
confirmationsinteger (int64) or nullrequiredWhile
seen: blocks on top of and including the payment's block; otherwisenull.createdinteger (int64)requiredWhen the payment was first seen or recorded, Unix seconds.
decimalsinteger (int32) or nullThe token's decimals, to display
amount_atomic;nullwithasset.statusstringrequiredseen(in a block, below the route's confirmation, and may still disappear);confirming(at the confirmation, being valued and screened);credited;rejected(not credited; the payer should contact the merchant's support); orreversed(its transaction left the chain before finality: the payment did not happen).tx_stringrequiredhash Transaction hash.
Client Quote
The public view of a quote, read with its client_secret and without a signature, for the
payer's checkout page. It has no account or internal fields.
addressstringrequiredSingle-use forwarder address to pay.
amountinteger (int64)requiredCredit in the currency's minor unit.
amount_stringrequiredatomic The exact token amount to pay, in base units, as a decimal string.
amount_integer (int64) or nullrequiredcredited While
credited: the credit of the payment in the currency's minor unit, the credited deposit'samount. It differs fromamountfor a payment valued at spot (another amount, or paid late); otherwisenull.assetstringrequiredAsset code.
cancel_integer (int64) or nullrequested_ at Deferred cancellation request time, Unix seconds.
chain_integer (int64)requiredid EVM chain identifier.
confirmationsinteger (int64) or nullrequiredWhile
seen: blocks on top of and including the payment's block; otherwisenull.currencystringrequiredusd.decimalsinteger (int32)requiredThe token's decimals, to display
amount_atomic.expires_integer (int64)requiredat End of the payment window, Unix seconds.
idstringrequiredqt_id.livemodebooleanrequiredWhether the quote is in live mode; a test-mode page should say so.
object"quote"requiredAlways
quote.payment_stringrequiredstatus Progress of the payment shown on the page; display only, never a reason to deliver anything:
none;seen(in a block, below the route's confirmation, and may still disappear);confirming(at the route's confirmation, being valued and screened);credited;rejected(not credited; the payer should contact the merchant's support); orreversed(credited, then its transaction left the chain before finality: the payment did not happen, and the credit is taken back).payment_stringrequireduri EIP-681 URI carrying the token, chain, address, and amount.
statusstringrequiredopen,complete,expired, orcanceled, as onQuote; hide the address onceexpires_athas passed.typical_integer (int64)requiredcredit_ seconds Typical time from payment to credit, in seconds, at the confirmation the quote's payments are credited at, as
GET /v1/configreports it.
{ "address": "0x2f3e91325b2288bce392711f85f5359661062a91", "amount": 2500, "amount_atomic": "25000000", "amount_credited": null, "asset": "usdc", "chain_id": 1, "confirmations": 1, "currency": "usd", "decimals": 6, "expires_at": 1790554500, "id": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10", "livemode": false, "object": "quote", "payment_status": "seen", "payment_uri": "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48@1/transfer?address=0x2f3e91325b2288bce392711f85f5359661062a91&uint256=25000000", "status": "open", "typical_credit_seconds": 30}Config
What a product's UI reads instead of hardcoding: assets, limits, and quote terms.
assetsarray of ConfigAssetrequired One entry per asset your payment settings accept on a chain where you have a treasury, with its terms: your effective payment config. Empty until you configure
POST /v1/.payment_ settings currencystringrequiredCredit currency,
usd.livemodebooleanrequiredThe mode of the key that reads it:
assetslists that mode's routes.max_integer (int64)requiredopen_ amount_ per_ account Cap on the credit of your open quotes in this mode, in cents. Test-mode quotes never count against live mode's cap.
max_integer (int64)requiredopen_ amount_ per_ customer Cap on the credit of one customer's open quotes, in cents; no single quote can exceed it.
max_integer (int64)requiredopen_ quotes Cap on the number of your open quotes in this mode.
object"config"requiredAlways
config.quote_integer (int64)requiredcreations_ per_ customer_ per_ minute One customer's quote creations in a rolling minute, from your payment settings.
{ "assets": [ { "asset": "usdc", "chain_id": 1, "confirmations": "12", "contract": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "decimals": 6, "max_deposit_atomic": "10000000000", "min_amount": 100, "min_deposit_atomic": "0", "min_refund_atomic": "1000000", "pricing": "stablecoin", "quote_amount_decimals": 4, "quote_spread_bps": 0, "quote_tolerance_bps": 100, "quote_ttl_seconds": 900, "typical_credit_seconds": 150, "typical_finality_seconds": 900 } ], "currency": "usd", "livemode": false, "max_open_amount_per_account": 1000000, "max_open_amount_per_customer": 500000, "max_open_quotes": 100, "object": "config", "quote_creations_per_customer_per_minute": 10}Config Asset
A payable asset and its terms.
assetstringrequiredAsset code.
chain_integer (int64)requiredid EVM chain identifier.
confirmationsstringrequiredThe confirmation a payment's block must reach before it is credited: a depth (
"2": the block and one more),"safe", or"finalized": the stricter of the chain's floor and theconfirmationsof your payment settings. A credit before finality can still be reversed (deposit.reversed).contractstringrequiredToken contract address.
decimalsinteger (int32)requiredToken decimals.
max_stringrequireddeposit_ atomic Maximum creditable deposit in base units, as a decimal string.
min_integer (int64)requiredamount Minimum credit in cents, for quotes and deposits; smaller deposits are not credited.
min_stringrequireddeposit_ atomic Minimum creditable deposit in base units, as a decimal string.
min_stringrequiredrefund_ atomic Minimum refundable amount in base units, as a decimal string.
pricingstringrequiredspotorstablecoin.quote_integer (int32)requiredamount_ decimals Token decimals a quote's amount is rounded up to (the operator's).
quote_integer (int32)requiredspread_ bps A quote's price is spot / (1 + spread_bps / 10 000); spot-valued payments carry no spread.
quote_integer (int32)requiredtolerance_ bps A payment within this many basis points of the quoted amount, either way, completes the quote at its amount.
quote_integer (int64)requiredttl_ seconds Payment window of a quote, in seconds.
typical_integer (int64)requiredcredit_ seconds Typical time from payment to the
deposit.creditedevent, in seconds, atconfirmations.typical_integer (int64)requiredfinality_ seconds Typical time from payment to finality, in seconds; refunds wait for it.
Create Api Key Request
POST /v1/api_keys body.
namestringThe key's label, at most 200 characters.
permissionsarray of stringA restricted key's permissions, required with
type: restricted: codes such asquotes.writeordeposits.read, where awriteincludes itsread. Grantable:account.read,api_keys.read,quotes.*,deposit_,addresses. * deposits.*,refunds.*,events.read,endpoints.read,treasury.read,sweeps.read,forwarders.read. Keys, treasuries, webhook endpoints, webhook keys, and account settings are managed only with a secret key.typestring or nullsecret, the default, orrestricted(Stripe's restricted keys): a key that holds onlypermissions.
{ "name": "fulfillment worker", "permissions": [ "quotes.write", "deposit_addresses.write", "deposits.read", "events.read", "refunds.read" ], "type": "restricted"}Create Deposit Address Request
POST /v1/ body.
client_stringrequiredreference_ id Your identifier of the customer, 1 to 200 characters; the customer is created on first use.
metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. Merged into the returned address's, asPOST /v1/does: a key set todeposit_ addresses/ {id} ""is unset. Every deposit to the address starts with a copy of it, and a rotation carries it to the next version. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
{ "client_reference_id": "team-42", "metadata": { "plan": "pro" }}Create Quote Request
POST /v1/quotes body.
amountinteger (int64)requiredThe credit to quote, a positive integer in the currency's minor unit (US cents).
assetstringrequiredAsset code of the payment on that chain, such as
usdc.chain_integer (int64)requiredid EVM chain of the payment, one of
GET /v1/configassets[]..chain_ id client_stringrequiredreference_ id Your identifier of the customer to credit, 1 to 200 characters (Stripe Checkout's
client_); the customer is created on first use.reference_ id currencystringrequiredLowercase ISO currency code; only
usd.metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. A key set to""is omitted. The deposit that pays the quote starts with a copy of it. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
{ "amount": 2500, "asset": "usdc", "chain_id": 1, "client_reference_id": "team-42", "currency": "usd", "metadata": { "order_id": "ord_1001" }}Create Refund Request
POST /v1/refunds body.
amount_string or nullatomic Amount in base units, as a decimal string; the unrefunded remainder when absent.
depositstringrequireddep_id of the deposit to refund.destination_stringrequiredaddress Address the customer controls; never default it to the sender, which may be an exchange.
metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. A key set to""is omitted. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
{ "amount_atomic": "25000000", "deposit": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "destination_address": "0x1775c1326aa633546b0b5634ae2bef0ba7cbfc9a", "metadata": { "ticket": "support-311" }}Create Treasury Challenge Request
POST /v1/ body.
addressstringrequiredThe treasury address to prove: an EOA, or a contract deployed on the chain such as a Safe.
chain_integer (int64)requiredid The chain of the treasury: a chain of the key's mode (
GET /v1/config).
{ "address": "0x936c1991f8da9a919fa11b557a3514719f5a4504", "chain_id": 1}Create Treasury Request
POST /v1/treasuries body.
chain_integer (int64)requiredid The treasury's chain, the challenge's.
messagestringrequiredThe challenge's
message, unchanged.signaturestringrequiredHex signature of the message: an EOA's 65-byte
personal_signsignature, or what a deployed contract'sisValidSignatureaccepts (for a Safe, the owners' signatures of the Safe message, or0xafterSignMessageLibapproved it). ERC-6492 signatures are refused.
{ "chain_id": 1, "message": "pay-api.phala.com wants you to sign in with your Ethereum account:\n0x936c1991f8dA9a919fa11b557a3514719f5A4504\n\nSet this address as the test mode treasury of acct_0c6e1d0a9b3f4c2e8d7a6b5c4d3e2f10 on Phala Pay.\n\nURI: https://pay-api.phala.com\nVersion: 1\nChain ID: 1\nNonce: Kq3nV8xZt2mP6wRa\nIssued At: 2026-09-28T12:00:00Z\nExpiration Time: 2026-09-28T12:10:00Z", "signature": "0x5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e1b"}Create Webhook Endpoint Request
POST /v1/ body.
descriptionstring or nullYour description, up to 5000 characters.
enabled_array of stringrequiredevents The event types to deliver, such as
deposit.credited, or["*"]for all.metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters.urlstringrequiredWhere to deliver events, up to 2048 characters, without credentials or fragment:
httpson port 443; in test mode alsohttpon port 80. Redirects are not followed.
{ "description": "Order fulfillment", "enabled_events": [ "deposit.credited", "deposit.reversed", "refund.failed" ], "metadata": { "team": "payments" }, "url": "https://example.com/phala-pay/webhooks"}Deleted Webhook Endpoint
A deleted webhook endpoint.
deletedbooleanrequiredAlways
true.idstringrequiredEndpoint id,
we_….object"webhook_endpoint"requiredAlways
webhook_endpoint.
{ "deleted": true, "id": "we_9e1c3a5b7d2f40c6e8a0b2d4f6a8c0e1", "object": "webhook_endpoint"}Delivery Attempt
A delivery attempt to a webhook endpoint.
atinteger (int64)requiredWhen it was made, Unix seconds.
status_integer (int32) or nullcode The endpoint's HTTP status;
nullwhen no response arrived (a timeout, a refused connection, or a URL the egress proxy refused).
Deposit
A transfer to a quote's address or a deposit address at the route's confirmation: valued,
screened, and credited, or rejected; reversed if its transaction left the chain before
finality.
Every deposit.* event carries the whole deposit, with cumulative amounts, so the customer's
balance can be recomputed from the latest snapshot whatever order events arrive in: the
deposit contributes amount - amount_refunded - amount_reversed cents while its status is
credited or reversed, and nothing while it is pending or rejected. status only moves
forward (pending, then credited or rejected, then possibly reversed) and
amount_refunded only grows, so of two snapshots the later one has the later status or, for
the same status, the larger amount_refunded.
addressstringrequiredReceiving forwarder address.
adminDepositAdmin or any or null amountinteger (int64) or nullCredit in the currency's minor unit (cents), once valued.
amount_stringrequiredatomic Token amount in base units, as a decimal string.
amount_integer (int64)requiredrefunded Cents of
amountits succeeded refunds take back:amounttimesamount_overrefunded_ atomic amount_atomic, rounded down, so it never exceeds the refunded share, and all ofamountonce fully refunded. Computed from the cumulative refunded amount, it only grows.0withoutamount.amount_stringrequiredrefunded_ atomic Refunded token amount in base units, as a decimal string: the sum of succeeded refunds.
amount_integer (int64)requiredreversed Cents of
amountthe reversal takes back:amountoncestatusisreversed(a deposit is refunded only once final, and a final deposit is never reversed, so a reversed deposit has no refunds);0otherwise.assetstring or nullAsset code;
nullfor a token without a route.asset_stringrequiredcontract Token contract address.
block_stringrequiredhash Hash of the block the transfer is in; it changes if the transaction is re-included.
block_integer (int64)requirednumber Number of the block the transfer is in; it changes if the transaction is re-included.
block_integer (int64)requiredtime Time of the block the transfer is in, Unix seconds.
chain_integer (int64)requiredid EVM chain identifier.
client_stringrequiredreference_ id Your identifier of the customer the deposit is credited to.
createdinteger (int64)requiredDetection time, Unix seconds.
currencystringrequiredusd.deposit_string or nulladdress The deposit address that received the transfer,
da_…, onchain_idataddress;nullfor a quote's address. Payments to a deposit address, active or retired, are credited at spot.exchange_string or nullrate USD per token, a decimal string with 8 places, once valued.
finalbooleanrequiredWhether the deposit's block is final on both providers: a final deposit can no longer be reversed, and only a final deposit can be refunded. A deposit is credited at the route's confirmation, before it is final (
GET /v1/configtypical_).finality_ seconds final_integer (int64) or nullat When the finality watch found the deposit's block final on both providers, Unix seconds;
nulluntilfinal.from_stringrequiredaddress Sender of the transfer.
idstringrequireddep_and the hex of the deposit's deterministic UUID,uuid_, wherev5(DEPOSIT_ NAMESPACE, "{chain_ id}: {tx_ hash}: {receipt_ log_ index}") receipt_is the transfer's position among its transaction's receipt logs. A deposit recorded at a position after the deposit there was reversed (log_ index replaces) has another id,uuid_.v5(DEPOSIT_ NAMESPACE, "{chain_ id}: {tx_ hash}: {receipt_ log_ index}: {revision}") livemodebooleanrequiredWhether the deposit is on a live-mode route.
log_integer (int64)requiredindex Block-wide log index of the transfer; it changes if the transaction is re-included.
metadatamap of stringrequiredYour key/value pairs (metadata): a copy of the quote's or the deposit address's when the deposit is recorded, independent of it afterwards;
{}when none.object"deposit"requiredAlways
deposit.price_string or nullsource quote(the quoted price) orspot, once valued.quoteExpandableQuote or any or null receipt_integer (int64)requiredlog_ index Position of the transfer among its transaction's receipt logs: with
chain_id,tx_hash, andrevision, whatidis derived from.refundedbooleanrequiredWhether the deposit is fully refunded.
rejection_string or nullreason Why the deposit was rejected:
unsupported_,asset asset_(a routed asset the payment settings the deposit is bound to do not accept),not_ accepted below_minimum,out_of_bounds,out_of_range, orsanctioned.replaced_string or nullby The deposit that took this reversed deposit's place,
dep_…(itsreplacesis this one);nullotherwise, or when that deposit is in another account or mode.replacesstring or nullThe reversed deposit whose place this one took,
dep_…: its transaction was re-included before finality against other state (a router or swap output), and the transfer at the same position in the final chain is this deposit.nullotherwise, or when that deposit is in another account or mode.revisioninteger (int64)requiredHow many deposits at the same receipt position were reversed before this one:
0, or the revision of the deposit itreplacesplus one.statusstringrequiredpending(recorded at the route's confirmation and being valued and screened, or held while the account's or customer'ssettlementis paused),credited,rejected(seerejection_reason), orreversed(its transaction is not in the final chain: its credit is taken back,amount_reversed). New values may be added.sweptbooleanrequiredWhether a finalized
Flushedevent after the deposit moved its forwarder's balance of its token to the treasury (GET /v1/sweeps), whoever sent the flush.tx_stringrequiredhash Transaction hash.
valued_integer (int64) or nullat Valuation time, Unix seconds.
{ "address": "0x2f3e91325b2288bce392711f85f5359661062a91", "amount": 2500, "amount_atomic": "25000000", "amount_refunded": 0, "amount_refunded_atomic": "0", "amount_reversed": 0, "asset": "usdc", "asset_contract": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "block_hash": "0x9a1c3e5b7d0f2a4c6e8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c", "block_number": 21000000, "block_time": 1790553612, "chain_id": 1, "client_reference_id": "team-42", "created": 1790553624, "currency": "usd", "deposit_address": null, "exchange_rate": "1.00000000", "final": true, "final_at": 1790554572, "from_address": "0x1775c1326aa633546b0b5634ae2bef0ba7cbfc9a", "id": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "livemode": false, "log_index": 212, "metadata": { "order_id": "ord_1001" }, "object": "deposit", "price_source": "quote", "quote": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10", "receipt_log_index": 0, "refunded": false, "rejection_reason": null, "replaced_by": null, "replaces": null, "revision": 0, "status": "credited", "swept": false, "tx_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e", "valued_at": 1790553630}Deposit Address
A customer's persistent deposit address, like a bank-transfer virtual account: one address for every supported token on every supported network. Eligible transfers within the account’s per-transfer deposit bounds are credited at the market (spot) price after confirmation. Rotation retires it and issues a new one on every network; eligible payments to a retired address are still credited.
addressstring or nullThe address shared by every network, when all of
networkshave the same one;nullwhen a network's treasury differs, and so its address (seenetworks), or when there is no network.client_stringrequiredreference_ id Your identifier of the customer.
client_string or nullsecret Lets the customer's page read the address's public view,
Client, fromDeposit Address GET /v1/without an API key, to show a payment as soon as it is seen. Returned only bydeposit_ addresses/ {id}? client_ secret= … POST /v1/anddeposit_ addresses …/rotate, each time a new one; only its hash is stored, and the newest 10 of an address stay valid. Give it only to the customer's page, and do not log it.createdinteger (int64)requiredCreation time, Unix seconds.
idstringrequiredda_id.livemodebooleanrequiredWhether the address is in live mode.
metadatamap of stringrequiredYour key/value pairs (metadata);
{}when none. Each deposit to the address starts with a copy.networksarray of DepositAddress Networkrequired The address on each supported network of the mode it was issued on, by
chain_id, with the tokens it takes there.object"deposit_address"requiredAlways
deposit_address.paymentsarray of PaymentrequiredPayments to the address in the last 24 hours, newest first, at most 10, as a quote's
payment:seenin a block within about a block time of arriving, thenrecordedas a deposit. Display only; credit fromdeposit.credited.retired_integer (int64) or nullat Retirement time, Unix seconds;
nullwhile active.saltstringrequiredCREATE2 salt, 32 bytes of hex; the same on every network.
statusstringrequiredactive, orretiredby a rotation; payments to either are credited.versioninteger (int64)requiredThe address's version among the customer's addresses, from 1. The salt is
keccak256(abi., with the typesencode(account, livemode, client_ reference_ id, "deposit_ address", version)) (string, bool, string, string, uint256)andaccountyouracct_id; it names no chain or asset. On each network the address is the factory'sCREATE2over that network's treasury and the salt.
{ "address": "0x0f45147a02e4c9d91aff20024e22095536fd5053", "client_reference_id": "team-42", "client_secret": "da_7b2e9c4a1f6d48b3a5c0e2d4f6a8b1c3_secret_0a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9", "created": 1790553600, "id": "da_7b2e9c4a1f6d48b3a5c0e2d4f6a8b1c3", "livemode": false, "metadata": { "plan": "pro" }, "networks": [ { "address": "0x0f45147a02e4c9d91aff20024e22095536fd5053", "assets": [ { "asset": "usdc", "contract": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "decimals": 6, "payment_uri": "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48@1/transfer?address=0x0f45147a02e4c9d91aff20024e22095536fd5053" } ], "chain_id": 1, "treasury": "0x936c1991f8da9a919fa11b557a3514719f5a4504" } ], "object": "deposit_address", "payments": [], "retired_at": null, "salt": "0x4e9767dd0c2ab5b953a305c3f10dc1e0d1f7c9d3cbab8463509d2edb06ca4b52", "status": "active", "version": 1}Deposit Address Asset
A token a deposit address takes on one network.
assetstringrequiredAsset code, such as
usdc.contractstringrequiredERC-20 contract address.
decimalsinteger (int32)requiredERC-20 decimal count.
max_stringdeposit_ atomic Inclusive maximum per-transfer deposit at view time, in atomic units as a decimal string. Always populated from resolved terms in merchant and client deposit-address views; omitted (never null) in the admin restore response, which ignores held payment settings.
min_integer (int64)amount Minimum per-transfer credit at view time, in the account currency's minor units (USD cents). Enforced independently of the atomic bounds; smaller spot-valued deposits are not credited. Always populated from resolved terms in merchant and client deposit-address views; omitted (never null) in the admin restore response, which ignores held payment settings.
min_stringdeposit_ atomic Inclusive minimum per-transfer deposit at view time, in atomic units as a decimal string. A zero atomic minimum does not waive the independent
min_amountcredit minimum. Always populated from resolved terms in merchant and client deposit-address views; omitted (never null) in the admin restore response, which ignores held payment settings.payment_stringrequireduri EIP-681 ERC-20 transfer URI carrying the token, chain, and address, and no amount: the payer chooses it.
Deposit Address List
A page of deposit addresses, newest first (https://
dataarray of DepositAddressrequired The deposit addresses.
has_booleanrequiredmore Whether more addresses follow in the direction of this page.
object"list"requiredAlways
list.urlstringrequiredThe list's path,
/v1/.deposit_ addresses
{ "data": [ { "address": "0x0f45147a02e4c9d91aff20024e22095536fd5053", "client_reference_id": "team-42", "client_secret": "da_7b2e9c4a1f6d48b3a5c0e2d4f6a8b1c3_secret_0a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9", "created": 1790553600, "id": "da_7b2e9c4a1f6d48b3a5c0e2d4f6a8b1c3", "livemode": false, "metadata": { "plan": "pro" }, "networks": [ { "address": "0x0f45147a02e4c9d91aff20024e22095536fd5053", "assets": [ { "asset": "usdc", "contract": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "decimals": 6, "payment_uri": "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48@1/transfer?address=0x0f45147a02e4c9d91aff20024e22095536fd5053" } ], "chain_id": 1, "treasury": "0x936c1991f8da9a919fa11b557a3514719f5a4504" } ], "object": "deposit_address", "payments": [], "retired_at": null, "salt": "0x4e9767dd0c2ab5b953a305c3f10dc1e0d1f7c9d3cbab8463509d2edb06ca4b52", "status": "active", "version": 1 } ], "has_more": false, "object": "list", "url": "/v1/deposit_addresses"}Deposit Address Network
A deposit address on one network (EVM chain).
addressstringrequiredThe forwarder address to pay on this chain.
assetsarray of DepositAddress Assetrequired The tokens your payment settings accept on this chain; any other token sent to the address is not credited.
chain_integer (int64)requiredid EVM chain identifier.
treasurystringrequiredThe treasury the forwarder pays. The address is the same on every network whose treasury is the same address.
Deposit Address View
GET /v1/ returns a DepositAddress to a request with an API key and a
Client to a request by client_secret.
{ "address": "0x0f45147a02e4c9d91aff20024e22095536fd5053", "client_reference_id": "team-42", "client_secret": "da_7b2e9c4a1f6d48b3a5c0e2d4f6a8b1c3_secret_0a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9", "created": 1790553600, "id": "da_7b2e9c4a1f6d48b3a5c0e2d4f6a8b1c3", "livemode": false, "metadata": { "plan": "pro" }, "networks": [ { "address": "0x0f45147a02e4c9d91aff20024e22095536fd5053", "assets": [ { "asset": "usdc", "contract": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "decimals": 6, "payment_uri": "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48@1/transfer?address=0x0f45147a02e4c9d91aff20024e22095536fd5053" } ], "chain_id": 1, "treasury": "0x936c1991f8da9a919fa11b557a3514719f5a4504" } ], "object": "deposit_address", "payments": [], "retired_at": null, "salt": "0x4e9767dd0c2ab5b953a305c3f10dc1e0d1f7c9d3cbab8463509d2edb06ca4b52", "status": "active", "version": 1}Deposit Admin
The operator's view of a deposit's internals (GET /v1/), returned as the
admin field of the deposit; never present in a merchant response or an event.
accountstringrequiredThe merchant account the deposit belongs to,
acct_….block_string (date-time)requiredtime Including block time.
eventsarray of DepositEvent Deliveryrequired Events about the deposit in ascending creation order, with their delivery.
final_string (date-time) or nullat When both providers showed the transfer at or below
finalized.price_string or nullscaled Eight-decimal scaled price as a decimal string.
receipt_integer (int64)requiredlog_ index Position of the transfer log in its transaction's receipt; with the chain and transaction, the deposit's identity.
routestring or nullSelected route.
route_integer (int64) or nullversion Selected route version.
settings_string (uuid) or nullhold The restore whose hold the deposit was recorded under; it waits for the merchant's reconfirmation, which binds it.
nullwhensettings_is set.revision settings_string or nullrevision The payment settings revision the deposit is bound to (
psrev_…): current in the snapshot of the statement that recorded it.nullwhilesettings_holdis set.statestringrequiredThe processing state:
detected,confirmed,credited,swept,rejected, orreversed(statusis its merchant view).transitionsarray of DepositTransitionrequired Transitions in ascending creation order.
updated_string (date-time)requiredat Last processing update time.
Deposit Event Delivery
An event about a deposit and its delivery state.
created_string (date-time)requiredat Event creation time.
delivered_string (date-time) or nullat When the last of the account's webhook endpoints accepted the event, or
nullwhile one has not or the account has none.idstringrequiredEvent id,
evt_….typestringrequiredEvent type, such as
deposit.credited.
Deposit List
A page of a list, newest first (https://
dataarray of DepositrequiredThe deposits.
has_booleanrequiredmore Whether more deposits follow in the direction of this page.
object"list"requiredAlways
list.urlstringrequiredThe list's path,
/v1/deposits.
{ "data": [ { "address": "0x2f3e91325b2288bce392711f85f5359661062a91", "amount": 2500, "amount_atomic": "25000000", "amount_refunded": 0, "amount_refunded_atomic": "0", "amount_reversed": 0, "asset": "usdc", "asset_contract": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "block_hash": "0x9a1c3e5b7d0f2a4c6e8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c", "block_number": 21000000, "block_time": 1790553612, "chain_id": 1, "client_reference_id": "team-42", "created": 1790553624, "currency": "usd", "deposit_address": null, "exchange_rate": "1.00000000", "final": true, "final_at": 1790554572, "from_address": "0x1775c1326aa633546b0b5634ae2bef0ba7cbfc9a", "id": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "livemode": false, "log_index": 212, "metadata": { "order_id": "ord_1001" }, "object": "deposit", "price_source": "quote", "quote": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10", "receipt_log_index": 0, "refunded": false, "rejection_reason": null, "replaced_by": null, "replaces": null, "revision": 0, "status": "credited", "swept": false, "tx_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e", "valued_at": 1790553630 } ], "has_more": false, "object": "list", "url": "/v1/deposits"}Deposit Transition
One immutable state transition of a deposit.
attemptinteger (int32)requiredRetry attempt recorded for this transition.
created_string (date-time)requiredat Transition creation time.
evidenceanyrequiredDurable transition evidence.
from_stringrequiredstate State before the transition attempt.
idstring (uuid)requiredTimeline row identifier.
to_stringrequiredstate State after the transition attempt.
Error Detail
Stable error fields safe to expose to callers.
codestringrequiredStable machine-readable error code; clients accept future codes.
doc_stringurl The documentation of
codein the API reference. Every error of this service carries it; it is optional in the schema, as in Stripe's, so a client never fails on an error without it.messagestringrequiredHuman-readable summary without internal details; it may change.
paramstring or nullThe request parameter the error is about, when there is one.
typeErrorTyperequired Error category:
invalid_for any 4xx except an idempotency conflict,request_ error idempotency_for anerror Idempotency-Keyreused with another request or still in use,api_errorfor 5xx.
Error Response
Machine-readable error envelope returned by every API failure, Stripe's error object
(https://
errorErrorDetailrequired Error details.
{ "error": { "code": "deposit_not_final", "doc_url": "https://phala-network.github.io/phala-pay/#section/Errors/deposit_not_final", "message": "the deposit is not final yet; request the refund once it is (about 15 minutes after its block on Ethereum)", "type": "invalid_request_error" }}Error Type
Error category of [ErrorDetail].
Event Data
An event's data.
objectobjectrequiredThe object's API representation when the event was created: a deposit, quote, refund, API key, treasury, webhook endpoint, or the account, as its
GETreturned it then.previous_object or nullattributes On
*.updatedevents: the fields that changed, with their values before the change (a changedmetadataholds only its changed keys; a field that was added isnull).
Event List
A page of events, newest first (https://
dataarray of EventObject Responserequired The events.
has_booleanrequiredmore Whether more events follow in the direction of this page.
object"list"requiredAlways
list.urlstringrequiredThe list's path,
/v1/events.
{ "data": [ { "account": "acct_0c6e1d0a9b3f4c2e8d7a6b5c4d3e2f10", "actor": "system", "created": 1790553630, "data": { "object": { "address": "0x2f3e91325b2288bce392711f85f5359661062a91", "amount": 2500, "amount_atomic": "25000000", "amount_refunded": 0, "amount_refunded_atomic": "0", "amount_reversed": 0, "asset": "usdc", "asset_contract": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "block_hash": "0x9a1c3e5b7d0f2a4c6e8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c", "block_number": 21000000, "block_time": 1790553612, "chain_id": 1, "client_reference_id": "team-42", "created": 1790553624, "currency": "usd", "deposit_address": null, "exchange_rate": "1.00000000", "final": false, "final_at": 1790554572, "from_address": "0x1775c1326aa633546b0b5634ae2bef0ba7cbfc9a", "id": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "livemode": false, "log_index": 212, "metadata": { "order_id": "ord_1001" }, "object": "deposit", "price_source": "quote", "quote": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10", "receipt_log_index": 0, "refunded": false, "rejection_reason": null, "replaced_by": null, "replaces": null, "revision": 0, "status": "credited", "swept": false, "tx_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e", "valued_at": 1790553630 } }, "id": "evt_2b4d6f8a0c1e43b5d7f9a1c3e5b7d9f0", "livemode": false, "object": "event", "pending_webhooks": 0, "request": null, "type": "deposit.credited" } ], "has_more": false, "object": "list", "url": "/v1/events"}Event Object Response
An event (https://GET /v1/events is also the account's audit log.
accountstringrequiredThe account,
acct_….actorstringrequiredWho caused it: an API key id (
key_…),admin(the operator), orsystem.createdinteger (int64)requiredCreation time, Unix seconds.
dataEventDatarequired The object when the event happened:
objectis its API representation, rendered in the same transaction as the change and never changed afterwards, so it can differ from a laterGETof the object.*.updatedevents addprevious_, the former values of the fields that changed (https://attributes docs. ).stripe. com/ api/ events/ object idstringrequiredEvent id,
evt_…, also thewebhook-idheader of its deliveries.livemodebooleanrequiredThe event's mode.
object"event"requiredAlways
event.pending_integer (int64)requiredwebhooks Deliveries to webhook endpoints that are neither delivered nor stopped.
requestEventRequest or any or null typestringrequiredEvent type, such as
deposit.credited.
{ "account": "acct_0c6e1d0a9b3f4c2e8d7a6b5c4d3e2f10", "actor": "system", "created": 1790553630, "data": { "object": { "address": "0x2f3e91325b2288bce392711f85f5359661062a91", "amount": 2500, "amount_atomic": "25000000", "amount_refunded": 0, "amount_refunded_atomic": "0", "amount_reversed": 0, "asset": "usdc", "asset_contract": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "block_hash": "0x9a1c3e5b7d0f2a4c6e8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c", "block_number": 21000000, "block_time": 1790553612, "chain_id": 1, "client_reference_id": "team-42", "created": 1790553624, "currency": "usd", "deposit_address": null, "exchange_rate": "1.00000000", "final": false, "final_at": 1790554572, "from_address": "0x1775c1326aa633546b0b5634ae2bef0ba7cbfc9a", "id": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "livemode": false, "log_index": 212, "metadata": { "order_id": "ord_1001" }, "object": "deposit", "price_source": "quote", "quote": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10", "receipt_log_index": 0, "refunded": false, "rejection_reason": null, "replaced_by": null, "replaces": null, "revision": 0, "status": "credited", "swept": false, "tx_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e", "valued_at": 1790553630 } }, "id": "evt_2b4d6f8a0c1e43b5d7f9a1c3e5b7d9f0", "livemode": false, "object": "event", "pending_webhooks": 0, "request": null, "type": "deposit.credited"}Event Request
The API request that caused an event (https://
idstringrequiredThe request's
Request-Id,req_….idempotency_string or nullkey The
Idempotency-Keythe request sent;nullwhen it sent none.
Expandable Deposit
A deposit id, or the deposit with expand[].
Expandable Quote
A quote id, or the quote with expand[].
Forwarder
A forwarder the account was issued (design §13): everything needed to recompute its address
and sweep it without Phala Pay. The address is factory's CREATE2 clone of the pinned
implementation over treasury and salt.
addressstringrequiredThe forwarder address.
chain_integer (int64)requiredid EVM chain identifier.
deposit_string or nulladdress The deposit address it is a network of,
da_…;nullfor a quote's.factorystringrequiredThe forwarder factory.
idstringrequiredForwarder id,
fwd_….livemodebooleanrequiredThe mode.
object"forwarder"requiredAlways
forwarder.quotestring or nullThe quote it was issued for,
qt_…;nullfor a deposit address's network.saltstringrequiredThe
CREATE2salt, 32 bytes of hex.superseded_integer (int64) or nullat When a treasury change replaced this deposit address network, Unix seconds; it is still watched and credited, and pays its own treasury.
treasurystringrequiredThe treasury the forwarder pays, fixed in its address.
Forwarder List
A page of forwarders (https://id order.
dataarray of ForwarderrequiredThe forwarders.
has_booleanrequiredmore Whether more forwarders follow in the direction of this page.
object"list"requiredAlways
list.urlstringrequiredThe list's path,
/v1/forwarders.
{ "data": [ { "address": "0x2f3e91325b2288bce392711f85f5359661062a91", "chain_id": 1, "deposit_address": null, "factory": "0x9e5f1d3c7a2b4e6f8a0c1d3e5f7a9b2c4d6e8f01", "id": "fwd_5c7e9a1b3d2f44c6e8a0b2d4f6c8e0a2", "livemode": false, "object": "forwarder", "quote": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10", "salt": "0x4e9767dd0c2ab5b953a305c3f10dc1e0d1f7c9d3cbab8463509d2edb06ca4b52", "superseded_at": null, "treasury": "0x936c1991f8da9a919fa11b557a3514719f5a4504" } ], "has_more": false, "object": "list", "url": "/v1/forwarders"}Known Error Code
Mark Refund Paid Request
POST /v1/ body: the merchant's refund transaction.
receipt_integer (int64) or nulllog_ index Position of the
Transferlog that pays the refund among the logs of the transaction's receipt (0 for the first), not the block-widelogIndex, which changes if the transaction is re-included in another block; any matching log when absent.transaction_stringrequiredhash Hash of the transaction that pays the refund from the treasury of the deposit's address.
{ "receipt_log_index": 0, "transaction_hash": "0x4b6d8f0a2c4e6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a6c"}Metadata Clear
"": unset every key of the object's metadata.
Metadata Param
A metadata parameter: an object of string values, where "" unsets the key, or "" to
unset every key.
Payment
A payment observed at a quote's address or a deposit address. Display only: while status is
seen it may still disappear in a reorg, and nothing has been credited.
amount_stringrequiredatomic Token amount in base units, as a decimal string.
assetstring or nullAsset code;
nullfor a token without a route.chain_integer (int64)requiredid EVM chain identifier.
confirmationsinteger (int64) or nullBlocks on top of and including the transfer's block at the last head scan;
seenonly.depositstringrequireddep_id the deposit has, or will have once recorded.estimated_integer (int64) or nullfinal_ at Estimated finality time, Unix seconds: block time plus 15 minutes;
seenonly.matches_boolean or nullquote On a quote: whether the payment is the quote's asset, in time, and within tolerance, so it is credited at the quoted price (otherwise at spot).
nullon a deposit address, whose payments are all credited at spot.statusstringrequiredseen(in a block, not recorded as a deposit yet) orrecorded(recorded as a deposit at the route's confirmation; follow it asdeposit). New values may be added.tx_stringrequiredhash Canonical transaction hash.
Payment Settings Asset
An accepted asset and your terms on it; a term null or not sent takes the operator's default.
assetstringrequiredAn asset code routed on the chain, such as
usdc.max_string or nulldeposit_ atomic The maximum creditable deposit in base units, a decimal string.
min_integer (int64) or nullamount The minimum credit in cents of a quote or a deposit valued at spot.
min_string or nulldeposit_ atomic The minimum creditable deposit in base units, a decimal string.
min_string or nullrefund_ atomic The refund dust floor in base units, a decimal string.
quote_integer (int32) or nullspread_ bps A quote's spread below spot, in basis points.
quote_integer (int32) or nulltolerance_ bps A quote's two-sided payment tolerance, in basis points.
quote_integer (int64) or nullttl_ seconds A quote's payment window, in seconds.
Payment Settings Chain
An accepted chain of your payment settings.
assetsarray of PaymentSettings Assetrequired The accepted assets of the chain, at least one.
chain_integer (int64)requiredid A chain of the key's mode.
confirmationsstring or nullThe confirmation you require on the chain: a depth (
"12": the block and eleven more),"safe", or"finalized", never weaker than the chain's floor and of the chain's kind (a depth orfinalizedon Ethereum; a depth,safe, orfinalizedon an OP-stack chain).nullfor the floor.
Payment Settings Object
Your payment settings in the key's mode (GET /v1/): what you accept and on
what terms, chosen from the operator's catalog within its bounds
(docs/
availablearray of AvailableChainrequired The operator's catalog of the mode: every chain and asset you may accept, with its defaults and bounds, and whether you accept it.
chainsarray of PaymentSettings Chainrequired The chains you accept, each with its accepted assets. A chain or asset not listed is not accepted.
livemodebooleanrequiredThe mode of the key that reads it.
object"payment_settings"requiredAlways
payment_settings.quote_integer (int64) or nullrequiredcreations_ per_ customer_ per_ minute One customer's quote creations in a rolling minute;
nullfor the default.revisionstringrequiredThe current revision,
psrev_…: unique and never reused, with no order.statusstringrequiredunconfigured(accepts nothing: never configured),configured, orheld(after a restore of the service, until you reconfirm withPOST /v1/; nothing is accepted meanwhile, and payments recorded wait).payment_ settings updatedinteger (int64)requiredWhen the current revision was written, Unix seconds.
{ "available": [ { "assets": [ { "accepted": true, "asset": "usdc", "contract": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "decimals": 6, "enabled": true, "max_deposit_atomic": { "default": "10000000000", "max": "10000000000", "min": "0" }, "min_amount": { "default": 100, "max": 18446744073709552000, "min": 100 }, "min_deposit_atomic": { "default": "0", "max": "115792089237316195423570985008687907853269984665640564039457584007913129639935", "min": "0" }, "min_refund_atomic": { "default": "1000000", "max": "1000000", "min": "1000000" }, "pricing": "stablecoin", "quote_amount_decimals": 4, "quote_spread_bps": { "default": 0, "max": 500, "min": 0 }, "quote_tolerance_bps": { "default": 100, "max": 500, "min": 0 }, "quote_ttl_seconds": { "default": 900, "max": 3600, "min": 30 } }, { "accepted": true, "asset": "usdt", "contract": "0xdac17f958d2ee523a2206206994597c13d831ec7", "decimals": 6, "enabled": true, "max_deposit_atomic": { "default": "10000000000", "max": "10000000000", "min": "0" }, "min_amount": { "default": 100, "max": 18446744073709552000, "min": 100 }, "min_deposit_atomic": { "default": "0", "max": "115792089237316195423570985008687907853269984665640564039457584007913129639935", "min": "0" }, "min_refund_atomic": { "default": "1000000", "max": "1000000", "min": "1000000" }, "pricing": "stablecoin", "quote_amount_decimals": 4, "quote_spread_bps": { "default": 0, "max": 500, "min": 0 }, "quote_tolerance_bps": { "default": 100, "max": 500, "min": 0 }, "quote_ttl_seconds": { "default": 900, "max": 3600, "min": 30 } } ], "chain_id": 1, "confirmations": { "default": "2", "floor": "2" }, "status": "active" } ], "chains": [ { "assets": [ { "asset": "usdc", "quote_spread_bps": 0 }, { "asset": "usdt" } ], "chain_id": 1, "confirmations": "12" } ], "livemode": false, "object": "payment_settings", "quote_creations_per_customer_per_minute": null, "revision": "psrev_5b0e4f1a9c3d4e7f8a2b6c1d0e9f8a7b", "status": "configured", "updated": 1790467200}Quote
A quote: a locked price, an exact token amount, and a single-use address to pay it to.
addressstringrequiredSingle-use forwarder address to pay.
amountinteger (int64)requiredCredit in the currency's minor unit.
amount_stringrequiredatomic The exact token amount to pay, in base units, as a decimal string.
assetstringrequiredAsset code.
cancel_integer (int64) or nullrequested_ at Deferred cancellation request time, Unix seconds; null before a request.
chain_integer (int64)requiredid EVM chain identifier.
client_stringrequiredreference_ id Your identifier of the customer the quote credits.
client_string or nullsecret Lets the payer's browser read the quote's public view,
ClientQuote, fromGET /v1/without an API key. Returned only byquotes/ {id}? client_ secret= … POST /v1/quotes; a repeat with the sameIdempotency-Keywithin 24 hours replays the first response, the same secret included. Give it only to the paying customer's page, and do not log it.createdinteger (int64)requiredCreation time, Unix seconds.
currencystringrequiredusd.depositExpandableDeposit or any or null exchange_stringrequiredrate The locked price in USD per token, a decimal string with 8 decimal places.
expires_integer (int64)requiredat End of the payment window, Unix seconds.
idstringrequiredqt_id. The quote's address salt iskeccak256(abi.with the typesencode(account, client_ reference_ id, "quote", id)) (string, string, string, string), whereaccountis youracct_id.livemodebooleanrequiredWhether the quote was created with a live key.
metadatamap of stringrequiredYour key/value pairs (metadata);
{}when none.object"quote"requiredAlways
quote.paymentPayment or any or nullpayment_stringrequireduri EIP-681 URI carrying the token, chain, address, and amount.
statusstringrequiredopen,complete(a matching payment consumed it),expired, orcanceled. A quote staysopenafterexpires_atuntil the finalized chain passes it, so a payment mined in time is never reported as expired; hide the address onceexpires_athas passed.termsQuoteTermsrequired The terms the quote was issued with, from your payment settings then; the quote keeps them whatever the settings say later.
treasurystringrequiredThe treasury the address pays: your treasury of the chain when the quote was created. The address is the factory's
CREATE2over it and the salt.
{ "address": "0x2f3e91325b2288bce392711f85f5359661062a91", "amount": 2500, "amount_atomic": "25000000", "asset": "usdc", "chain_id": 1, "client_reference_id": "team-42", "client_secret": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10_secret_9f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a4938271605f4e3d2c1b0a", "created": 1790553600, "currency": "usd", "deposit": null, "exchange_rate": "1.00000000", "expires_at": 1790554500, "id": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10", "livemode": false, "metadata": { "order_id": "ord_1001" }, "object": "quote", "payment": { "amount_atomic": "25000000", "asset": "usdc", "chain_id": 1, "confirmations": 1, "deposit": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "estimated_final_at": 1790554572, "matches_quote": true, "status": "seen", "tx_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e" }, "payment_uri": "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48@1/transfer?address=0x2f3e91325b2288bce392711f85f5359661062a91&uint256=25000000", "status": "open", "terms": { "confirmations": "12", "max_deposit_atomic": "10000000000", "min_amount": 100, "min_deposit_atomic": "0", "min_refund_atomic": "1000000", "quote_amount_decimals": 4, "quote_spread_bps": 0, "quote_tolerance_bps": 100, "quote_ttl_seconds": 900 }, "treasury": "0x936c1991f8da9a919fa11b557a3514719f5a4504"}Quote List
A page of quotes, newest first (https://
dataarray of QuoterequiredThe quotes.
has_booleanrequiredmore Whether more quotes follow in the direction of this page.
object"list"requiredAlways
list.urlstringrequiredThe list's path,
/v1/quotes.
{ "data": [ { "address": "0x2f3e91325b2288bce392711f85f5359661062a91", "amount": 2500, "amount_atomic": "25000000", "asset": "usdc", "chain_id": 1, "client_reference_id": "team-42", "client_secret": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10_secret_9f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a4938271605f4e3d2c1b0a", "created": 1790553600, "currency": "usd", "deposit": null, "exchange_rate": "1.00000000", "expires_at": 1790554500, "id": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10", "livemode": false, "metadata": { "order_id": "ord_1001" }, "object": "quote", "payment": { "amount_atomic": "25000000", "asset": "usdc", "chain_id": 1, "confirmations": 1, "deposit": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "estimated_final_at": 1790554572, "matches_quote": true, "status": "seen", "tx_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e" }, "payment_uri": "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48@1/transfer?address=0x2f3e91325b2288bce392711f85f5359661062a91&uint256=25000000", "status": "open", "terms": { "confirmations": "12", "max_deposit_atomic": "10000000000", "min_amount": 100, "min_deposit_atomic": "0", "min_refund_atomic": "1000000", "quote_amount_decimals": 4, "quote_spread_bps": 0, "quote_tolerance_bps": 100, "quote_ttl_seconds": 900 }, "treasury": "0x936c1991f8da9a919fa11b557a3514719f5a4504" } ], "has_more": false, "object": "list", "url": "/v1/quotes"}Quote Terms
The terms a quote was issued with.
confirmationsstringrequiredThe confirmation the quote required when issued: a depth,
safe, orfinalized. A payment not credited yet waits for the stricter of it and the chain's current floor.max_stringrequireddeposit_ atomic Maximum creditable deposit in base units, as a decimal string.
min_integer (int64)requiredamount Minimum credit in cents of a payment valued at spot.
min_stringrequireddeposit_ atomic Minimum creditable deposit in base units, as a decimal string.
min_stringrequiredrefund_ atomic Refund dust floor in base units, as a decimal string.
quote_integer (int32)requiredamount_ decimals Token decimals
amount_atomicwas rounded up to.quote_integer (int32)requiredspread_ bps The spread below spot of the locked price, in basis points.
quote_integer (int32)requiredtolerance_ bps A payment within this many basis points of
amount_atomic, either way, completes the quote at itsamount.quote_integer (int64)requiredttl_ seconds The payment window, in seconds.
Quote View
GET /v1/quotes/{id} returns a Quote to a signed request and a ClientQuote to a request by
client_secret.
{ "address": "0x2f3e91325b2288bce392711f85f5359661062a91", "amount": 2500, "amount_atomic": "25000000", "asset": "usdc", "chain_id": 1, "client_reference_id": "team-42", "client_secret": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10_secret_9f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b5a4938271605f4e3d2c1b0a", "created": 1790553600, "currency": "usd", "deposit": null, "exchange_rate": "1.00000000", "expires_at": 1790554500, "id": "qt_5f1c0b6a2d9e4f3a8b7c6d5e4f3a2b10", "livemode": false, "metadata": { "order_id": "ord_1001" }, "object": "quote", "payment": { "amount_atomic": "25000000", "asset": "usdc", "chain_id": 1, "confirmations": 1, "deposit": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "estimated_final_at": 1790554572, "matches_quote": true, "status": "seen", "tx_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e" }, "payment_uri": "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48@1/transfer?address=0x2f3e91325b2288bce392711f85f5359661062a91&uint256=25000000", "status": "open", "terms": { "confirmations": "12", "max_deposit_atomic": "10000000000", "min_amount": 100, "min_deposit_atomic": "0", "min_refund_atomic": "1000000", "quote_amount_decimals": 4, "quote_spread_bps": 0, "quote_tolerance_bps": 100, "quote_ttl_seconds": 900 }, "treasury": "0x936c1991f8da9a919fa11b557a3514719f5a4504"}Refund
A refund of (part of) a deposit to the customer, which the merchant pays from the treasury of
the deposit's address and attaches with mark_paid (design D5).
amount_stringrequiredatomic Token amount in base units, as a decimal string.
createdinteger (int64)requiredRequest time, Unix seconds.
depositExpandableDepositrequired The refunded deposit: its
dep_id, or the object withexpand[]=deposit.destination_stringrequiredaddress Destination address.
failure_string or nullreason Why the refund failed:
transaction_,failed transfer_,not_ found sender_mismatch,destination_,mismatch amount_mismatch, ortransfer_. Historical refunds may retainalready_ used transaction_ordropped transaction_; missing transactions now remain pending with their reservation and alert after 24 hours. New values may be added.not_ found idstringrequiredre_id.livemodebooleanrequiredWhether the refund was requested with a live key.
metadatamap of stringrequiredYour key/value pairs (metadata);
{}when none.object"refund"requiredAlways
refund.receipt_integer (int64) or nulllog_ index Position of the paying
Transferlog among the logs of the transaction's receipt: as named when marked paid, or found at verification.statusstringrequiredpending(awaiting payment, or its transaction's finality),succeeded(the transfer is final),failed(the attached transaction does not pay the refund; seefailure_reason), orcanceled(only before a transaction is attached). A refund marked paid stayspending, reserving its amount of the deposit, until it issucceededorfailed.transaction_string or nullhash The attached refund transaction, once marked paid.
treasurystringrequiredThe treasury the refund must be paid from: the one the deposit's address pays, which may differ from the account's current treasury.
{ "amount_atomic": "25000000", "created": 1790557200, "deposit": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "destination_address": "0x1775c1326aa633546b0b5634ae2bef0ba7cbfc9a", "failure_reason": null, "id": "re_3c9e7a1b5d2f4a6c8e0b1d3f5a7c9e02", "livemode": false, "metadata": { "ticket": "support-311" }, "object": "refund", "receipt_log_index": 0, "status": "pending", "transaction_hash": "0x4b6d8f0a2c4e6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a6c", "treasury": "0x936c1991f8da9a919fa11b557a3514719f5a4504"}Refund List
A page of refunds, newest first (https://
dataarray of RefundrequiredThe refunds.
has_booleanrequiredmore Whether more refunds follow in the direction of this page.
object"list"requiredAlways
list.urlstringrequiredThe list's path,
/v1/refunds.
{ "data": [ { "amount_atomic": "25000000", "created": 1790557200, "deposit": "dep_8a1f4e2b6c3d49e0a7b5c1d2e3f40516", "destination_address": "0x1775c1326aa633546b0b5634ae2bef0ba7cbfc9a", "failure_reason": null, "id": "re_3c9e7a1b5d2f4a6c8e0b1d3f5a7c9e02", "livemode": false, "metadata": { "ticket": "support-311" }, "object": "refund", "receipt_log_index": 0, "status": "pending", "transaction_hash": "0x4b6d8f0a2c4e6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a6c", "treasury": "0x936c1991f8da9a919fa11b557a3514719f5a4504" } ], "has_more": false, "object": "list", "url": "/v1/refunds"}Resend Event Request
POST /v1/ body.
webhook_stringrequiredendpoint The enabled endpoint to deliver the event to again,
we_….
{ "webhook_endpoint": "we_9e1c3a5b7d2f40c6e8a0b2d4f6a8c0e1"}Roll Api Key Request
POST /v1/ body.
expires_integer (int32)in Seconds the old key keeps working, up to 604800 (7 days); 0, the default, revokes it at once. A key rolling itself needs at least 3600.
{ "expires_in": 86400}Roll Webhook Key Request
POST /v1/ body.
expires_integer (int32)in Seconds the current key keeps signing beside the new one: 172800 (48 hours, the treasury time-lock) to 604800 (7 days) in live mode, 0 to 604800 in test mode, where 0 stops it at once. Default 172800.
{ "expires_in": 172800}Submit Deposit Address Transaction Request
The deposit address must already have a network for this chain.
chain_integer (int64)requiredid One of this object's issued network identifiers.
transaction_stringrequiredhash Transaction hash, exactly 32 hexadecimal bytes prefixed by
0x.
{ "chain_id": 84532, "transaction_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e"}Submit Quote Transaction Request
A quote's chain is derived from its stored terms.
transaction_stringrequiredhash Transaction hash, exactly 32 hexadecimal bytes prefixed by
0x.
{ "transaction_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e"}Sweep
A sweep: one finalized Flushed event of the factory, which moved a forwarder's whole balance
of a token to its treasury (Stripe's Payout). Anyone can send the flush; the merchant usually
does, with the SDK's flush_ or safe_batch.
addressstringrequiredThe forwarder's address.
amount_stringrequiredatomic Amount moved, in base units, as a decimal string.
assetstring or nullAsset code;
nullfor a token without a route.block_integer (int64)requirednumber Block number of the transaction.
chain_integer (int64)requiredid EVM chain identifier.
createdinteger (int64)requiredWhen the finalized event was indexed, Unix seconds.
forwarderstringrequiredThe forwarder swept,
fwd_….idstringrequiredSweep id,
sw_….livemodebooleanrequiredThe mode.
log_integer (int64)requiredindex Block-wide index of the
Flushedlog.object"sweep"requiredAlways
sweep.tokenstringrequiredThe token contract.
treasurystringrequiredThe treasury paid, fixed in the forwarder's address.
tx_stringrequiredhash The flush transaction.
Sweep List
A page of sweeps, newest first (https://
dataarray of SweeprequiredThe sweeps.
has_booleanrequiredmore Whether more sweeps follow in the direction of this page.
object"list"requiredAlways
list.urlstringrequiredThe list's path,
/v1/sweeps.
{ "data": [ { "address": "0x2f3e91325b2288bce392711f85f5359661062a91", "amount_atomic": "25000000", "asset": "usdc", "block_number": 21000420, "chain_id": 1, "created": 1790560800, "forwarder": "fwd_5c7e9a1b3d2f44c6e8a0b2d4f6c8e0a2", "id": "sw_1e3c5a7b9d0f42e4c6a8b0d2f4e6a8c0", "livemode": false, "log_index": 7, "object": "sweep", "token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "treasury": "0x936c1991f8da9a919fa11b557a3514719f5a4504", "tx_hash": "0x4b6d8f0a2c4e6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a6c" } ], "has_more": false, "object": "list", "url": "/v1/sweeps"}Transaction Submission
A quiet acknowledgement, including for ignored hints.
objectTransactionSubmission Objectrequired Object discriminator.
statusTransactionSubmission Statusrequired Acknowledgement only; poll the original object for payment status.
transaction_stringrequiredhash Hash echoed from the submission.
{ "object": "transaction_submission", "status": "received", "transaction_hash": "0x7d3c1e5a9b2f4d6c8e0a1b3d5f7c9e2a4b6d8f0c1e3a5b7d9f1c3e5a7b9d1f3e"}Transaction Submission Object
Constant submission object discriminator.
Transaction Submission Status
Received acknowledges submission only, never detection or verification.
Treasury
An account's treasury of one chain and mode (design D10): the only address the forwarders issued over it can pay.
addressstringrequiredThe treasury address.
canceled_integer (int64) or nullat When it was canceled, Unix seconds.
cancellation_string or nullreason Why it was canceled:
requested(you canceled it) orsanctioned(a sanctions list named the address when the change was due to apply, so it never applied).chain_integer (int64)requiredid EVM chain identifier.
createdinteger (int64)requiredWhen it was proven, Unix seconds.
crediting_booleanrequiredpaused Whether crediting of deposits to forwarders over this address is paused: they stay
pendingand nodeposit.creditedis sent until it resumes.crediting_array of stringrequiredpaused_ by Who paused crediting:
merchant(POST /v1/) and, or,treasuries/ {id}/ pause operator. Each lifts only its own pause.effective_integer (int64)requiredat When the treasury applies or applied, Unix seconds: at once for a chain's first treasury and in test mode, 48 hours after the proof for a later live change.
idstringrequiredTreasury id,
trs_….kindstringrequiredeoa(an EIP-191 signature recovered to the address) orcontract(a deployed contract's EIP-1271 approval).livemodebooleanrequiredThe mode.
object"treasury"requiredAlways
treasury.replaced_integer (int64) or nullat When a later treasury replaced it, Unix seconds.
statusstringrequiredpending(a live change waiting foreffective_at; cancel it withPOST /v1/),treasuries/ {id}/ cancel active(the chain's current treasury: new quotes and deposit address networks pay it),replaced(a former treasury; addresses issued over it still pay it), orcanceled.
{ "address": "0x936c1991f8da9a919fa11b557a3514719f5a4504", "canceled_at": null, "cancellation_reason": null, "chain_id": 1, "created": 1790467200, "crediting_paused": false, "crediting_paused_by": [], "effective_at": 1790467200, "id": "trs_4d8a2c6e0b1f47a3c5e7d9b1a3c5e7f9", "kind": "contract", "livemode": false, "object": "treasury", "replaced_at": null, "status": "active"}Treasury Challenge
An EIP-4361 (Sign-In with Ethereum) message proving a treasury, usable once: valid for 10 minutes for an EOA, 24 hours for an address that holds code (a Safe).
addressstringrequiredThe treasury address, as sent.
chain_integer (int64)requiredid The treasury's chain.
expires_integer (int64)requiredat When the message stops being accepted, Unix seconds.
livemodebooleanrequiredThe key's mode.
messagestringrequiredThe EIP-4361 message to sign, exactly as given:
domainandURIare the API's origin, the statement names your account and mode, andChain IDischain_id. An EOA signs it withpersonal_sign(EIP-191); a Safe's owners sign it as a Safe message (EIP-1271).noncestringrequiredThe message's single-use nonce.
object"treasury_challenge"requiredAlways
treasury_.challenge
{ "address": "0x936c1991f8da9a919fa11b557a3514719f5a4504", "chain_id": 1, "expires_at": 1790554200, "livemode": false, "message": "pay-api.phala.com wants you to sign in with your Ethereum account:\n0x936c1991f8dA9a919fa11b557a3514719f5A4504\n\nSet this address as the test mode treasury of acct_0c6e1d0a9b3f4c2e8d7a6b5c4d3e2f10 on Phala Pay.\n\nURI: https://pay-api.phala.com\nVersion: 1\nChain ID: 1\nNonce: Kq3nV8xZt2mP6wRa\nIssued At: 2026-09-28T12:00:00Z\nExpiration Time: 2026-09-28T12:10:00Z", "nonce": "Kq3nV8xZt2mP6wRa", "object": "treasury_challenge"}Treasury List
GET /v1/treasuries response.
dataarray of TreasuryrequiredThe mode's treasuries, newest first.
has_booleanrequiredmore Whether more treasuries match than
limit.object"list"requiredAlways
list.urlstringrequiredThe list's path,
/v1/treasuries.
{ "data": [ { "address": "0x936c1991f8da9a919fa11b557a3514719f5a4504", "canceled_at": null, "cancellation_reason": null, "chain_id": 1, "created": 1790467200, "crediting_paused": false, "crediting_paused_by": [], "effective_at": 1790467200, "id": "trs_4d8a2c6e0b1f47a3c5e7d9b1a3c5e7f9", "kind": "contract", "livemode": false, "object": "treasury", "replaced_at": null, "status": "active" } ], "has_more": false, "object": "list", "url": "/v1/treasuries"}Update Metadata Request
POST /v1/quotes/{id}, POST /v1/, POST /v1/refunds/{id}, and
POST /v1/ body: the
object's updatable parameters, of which metadata is the one.
metadataMetadataParam Stripe's
metadata: up to 50 string key/value pairs for your own use, keys of up to 40 characters without square brackets, values of up to 500 characters. Merged into the object's: a key set to a value is set, a key set to""is unset, other keys are kept, andmetadata: ""unsets every key. Phala Pay never reads it. Do not store sensitive information in it, such as personal or payment details.
{ "metadata": { "note": "", "order_id": "ord_1001" }}Update Payment Settings Request
POST /v1/ body. A parameter not sent is unchanged; chains, when sent,
replaces the whole list. Writes are last-write-wins.
chainsarray of PaymentSettings Chain The chains to accept, replacing the list: each chain and asset of the key's mode once. An element's term not sent resets to the operator's default.
[]accepts nothing.quote_integer (int64) or nullcreations_ per_ customer_ per_ minute One customer's quote creations in a rolling minute, from 1 to the operator's maximum;
nullrestores the default.
{ "chains": [ { "assets": [ { "asset": "usdc", "quote_spread_bps": 0 }, { "asset": "usdt" } ], "chain_id": 1, "confirmations": "12" } ]}Update Webhook Endpoint Request
POST /v1/ body; parameters not sent are left unchanged.
descriptionstring or nullA new description;
""unsets it.disabledboolean or nulltruedisables the endpoint,falseenables it. A disabled endpoint receives nothing and its pending deliveries stop; resend missed events withPOST /v1/.events/ {id}/ resend enabled_array of stringevents New event types, or
["*"].metadataMetadataParam Merged into the endpoint's metadata: a key set to
""is unset, andmetadata: ""unsets every key.urlstring or nullA new URL, as on creation.
{ "disabled": false, "enabled_events": [ "*" ]}Webhook Endpoint List
A page of webhook endpoints, newest first (https://
dataarray of WebhookEndpoint Objectrequired The endpoints.
has_booleanrequiredmore Whether more endpoints follow in the direction of this page.
object"list"requiredAlways
list.urlstringrequiredThe list's path,
/v1/.webhook_ endpoints
{ "data": [ { "created": 1790467200, "description": "Order fulfillment", "disabled_reason": null, "enabled_events": [ "deposit.credited", "deposit.reversed", "refund.failed" ], "id": "we_9e1c3a5b7d2f40c6e8a0b2d4f6a8c0e1", "last_attempt": { "at": 1790553695, "status_code": 503 }, "livemode": false, "metadata": { "team": "payments" }, "object": "webhook_endpoint", "oldest_pending_at": 1790553630, "pending_deliveries": 2, "status": "enabled", "url": "https://example.com/phala-pay/webhooks" } ], "has_more": false, "object": "list", "url": "/v1/webhook_endpoints"}Webhook Endpoint Object
A webhook endpoint (design D11): where the account's events of one mode are delivered, signed with the account's webhook key of that mode.
createdinteger (int64)requiredCreation time, Unix seconds.
deletedboolean or nulltruein thewebhook_event; absent otherwise.endpoint. deleted descriptionstring or nullYour description.
disabled_string or nullreason gonewhen Phala Pay disabled the endpoint because it answered410 Gone;nullotherwise. Failing deliveries never disable an endpoint: they are retried until delivered.enabled_array of stringrequiredevents The event types delivered, or
["*"]for all. Account events (account.*,api_key.*,treasury.*,webhook_) are delivered to every enabled endpoint whatever this lists.endpoint. * idstringrequiredEndpoint id,
we_….last_Deliveryattempt Attempt or any or null livemodebooleanrequiredThe endpoint's mode.
metadatamap of stringrequiredYour key/value pairs (metadata);
{}when none.object"webhook_endpoint"requiredAlways
webhook_endpoint.oldest_integer (int64) or nullpending_ at Creation time of the oldest event not delivered to the endpoint yet, Unix seconds;
nullwhen none is pending.pending_integer (int64)requireddeliveries Deliveries to the endpoint not delivered yet. They are retried with backoff (capped at an hour) until delivered and are never given up on; a count that keeps growing means the endpoint is failing: fix it, then list what it missed with
GET /v1/.events? delivery_ success= false statusstringrequiredenabledordisabled.urlstringrequiredWhere events are delivered.
{ "created": 1790467200, "description": "Order fulfillment", "disabled_reason": null, "enabled_events": [ "deposit.credited", "deposit.reversed", "refund.failed" ], "id": "we_9e1c3a5b7d2f40c6e8a0b2d4f6a8c0e1", "last_attempt": { "at": 1790553695, "status_code": 503 }, "livemode": false, "metadata": { "team": "payments" }, "object": "webhook_endpoint", "oldest_pending_at": 1790553630, "pending_deliveries": 2, "status": "enabled", "url": "https://example.com/phala-pay/webhooks"}Webhook Key Object
One version of an account's webhook signing key, with its public key.
expires_integer (int64) or nullat When a rolled key stops signing, Unix seconds;
nullfor the current key.public_stringrequiredkey The ed25519 public key in Standard Webhooks' form:
whpk_and the standard base64 of its 32 raw bytes, whichreport_databinds. Pin it after verifying the attestation: every delivery carries av1asignature by it.versioninteger (int32)requiredKey version, from 1; it grows by one per roll.
Webhook Key Version
One version of an account's webhook signing key; its public key comes from
GET /v1/attestation.
expires_integer (int64) or nullat When a rolled key stops signing, Unix seconds;
nullfor the current key.versioninteger (int32)requiredKey version, from 1.