Transaction Authorization

Sign payouts with the employee's wallet when the company runs in Signature mode.

Before You Start

Read the following guides before proceeding:

GuideWhy
Wallet SignaturesWho signs, and how each wallet type encodes the signature
PermissionsPermissions are checked before the signature
EnvironmentsGET /api/v1/config returns the company's authorization mode

Overview

A company runs in one of two transaction authorization modes:

ModePayouts
ExternalExecuted without a wallet signature. transaction_authorization is not required
SignatureEach payout carries transaction_authorization: a wallet signature over a fixed sentence that names the operation, the estimation and a nonce

The signature is checked after request validation and before anything is executed, so a refused request
moves no funds. Each signature is accepted once.

Reading the Mode

GET /api/v1/config

Send the company id as a UUID (00000000-0000-0000-0000-000000000012, not 0x…) in the X-Company-Id
header. No token is required. The full response is described in
Environments — Runtime Configuration.

{
  "defaultCompanyId": "00000000-0000-0000-0000-000000000012",
  "transactionAuthorizationMode": "External"
}
FieldDescription
transactionAuthorizationModeExternal or Signature. Only Signature requires transaction_authorization

Endpoints

MethodPathPermissions (any of)SignatureSentence
POST/api/v1/cards/transferCardSuRequired in Signature modeCard Transfer
POST/api/v3/bank/initiate/{corridor}TransactionSu, TransactionCreateRequired in Signature modeCorridor Transfers
POST/api/v1/bank/transferTransactionSu, TransactionCreateRequired in Signature modeBank Transfers
POST/api/v2/recipientsTransactionSu, TransactionManageNever required. Checked when sent, if the company is in Signature mode and is an agent company (operating mode Agentic or Mixed). Ignored otherwiseCreating a Recipient

All four accept the corporation token only. A caller without the permission receives 403 before the
signature is looked at.

Estimate, confirm and read endpoints take no signature. POST /api/v1/bulk/transfer takes none: bulk
transfers are signed on-chain by the wallet. The other recipient routes — POST /api/v1/recipients,
POST /api/v1/recipients/{recipientId}/payment_details, PUT /api/v1/recipients/{recipientId},
PUT /api/v1/recipients/{recipientId}/payment_details/{paymentDetailsId} and
PUT /api/v2/recipients/{recipientId} — take none.

The signer is the employee_address wallet, and an employee signs with their own wallet, never the
owner's. Encodings per wallet type are in Wallet Signatures.


The transaction_authorization Object

{
  "transaction_authorization": {
    "message_signature": "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac81f0c5b0e4a7c1d2e3f405162738495a6b7c8d9e0f1a2b3c4d5e6f708192a3b4c1b",
    "nonce": 1760774400
  }
}
FieldTypeRequiredDescription
transaction_authorizationobjectConditionalRequired on the three payout endpoints when the mode is Signature. Optional on POST /api/v2/recipients. When sent, its shape is validated in every mode
transaction_authorization.message_signaturestringYesSignature over the sentence
transaction_authorization.nonceinteger (int64)YesUnix time in seconds used in the sentence. Not 0, not more than 5 minutes old, not more than 60 seconds in the future
transaction_authorization.public_keystringConditionalRequired for XRPL wallets: the signer's public key in hex, ^[0-9A-Fa-f]{2,200}$, starting with ED (ed25519) or 02/03 (secp256k1). Ignored for EVM and Stellar

A signature is spent on first use, including when the execution behind it fails. Every retry needs a
new signature with a new nonce.


The Sentence

Each endpoint's page gives the sentence to sign, character for character. The placeholders follow the same
rules everywhere:

PlaceholderValue
<estimation_id>The request's estimation_id as a lowercase, hyphenated UUID, e.g. 550e8400-e29b-41d4-a716-446655440000, whatever case or format the request sends
<nonce>The same integer as transaction_authorization.nonce

Sign the sentence with the employee_address wallet — see Wallet Signatures.


Error Handling

{
  "error_reason": "ErrorPermissionDenied",
  "error_description": "transaction authorization does not authorize this request",
  "error_category": {
    "category": "CategoryUnauthorized",
    "http_status_code": 401
  }
}

Validation Errors (400)

Error ReasonError DetailsDescriptionResolution
ErrorMissingFieldfield: message_signature, issue: missingmessage signature is requiredtransaction_authorization was sent without a signature. Any mode
ErrorMissingFieldfield: nonce, issue: missingnonce is requirednonce is missing or 0. Any mode
ErrorInvalidFieldfield: public_key, issue: invalid_formatInvalid public key formatpublic_key is not hex. Any mode
ErrorInvalidFieldfield: public_key, issue: invalid_formatInvalid public keySignature mode: public_key is hex but not an XRPL public key
ErrorMissingFieldfield: public_key, issue: missingpublic key is required for Ripple signaturesSignature mode, XRPL wallet, no public_key
ErrorInvalidFieldfield: message_signature, issue: invalid_formatInvalid message signatureSignature mode: the signature cannot be decoded or recovers to no address — including a smart-contract wallet signature that the wallet declines. Fix the encoding or sign again

Authorization Errors (401)

Error ReasonError DetailsDescriptionResolution
ErrorMissingFieldfield: transaction_authorization, issue: missingtransaction authorization is requiredSignature mode, payout sent without transaction_authorization. Sign the sentence and send it
ErrorExpiredfield: nonce, issue: expiredtransaction authorization has expirednonce is more than 5 minutes old. Sign again with a new nonce
ErrorNotYetValidfield: nonce, issue: not_yet_validtransaction authorization is not yet valid, check the device clocknonce is more than 60 seconds in the future. The signing device's clock is wrong
ErrorPermissionDenied—transaction authorization does not authorize this requestAnother estimation, operation, recipient or wallet, or a sentence that differs by any character. The response does not say which. Rebuild the sentence and sign with the employee_address wallet
ErrorAlreadyExistsfield: transaction_authorization, issue: already_usedtransaction authorization has already been usedSign again with a new nonce

Server Errors

Error ReasonHTTPDescriptionResolution
ErrorNotSupported500Unsupported chain typeThe wallet's chain type cannot be verified
ErrorGeneral503Transaction authorization is temporarily unavailableNothing was executed. Retry with a new signature

Did this page help you?