Customer-signed accepts
Optional EIP-712 accept: the end-customer’s wallet authorizes each accept. Default remains empty-body{} accept.
Mental model
Two different signatures, in order:- Register once — prove ownership of an EVM wallet for a vault customer (
pcus_…). This creates an active signing key (skey_…). - Sign every accept — after quote, the customer signs Element Pay’s quote
typed_data; you submit that signature on accept.
Register signature ≠ accept signature. Reusing either in the wrong step will fail.
1. When to use
- Customer must approve accept in their wallet → enable Signed accepts for that API key.
- You accept on the customer’s behalf → leave it off; empty
{}accept still works.
2. Enable in Console
Enablement is per API key in the Console Settings column (sandbox key ≠ live key). It is not toggled withX-API-Key.
Rules:
- Default off — custodial partners leave it off; quote/accept unchanged.
- Tooltip on Signed accepts: “Requires customer EIP-712 signature on accept.”
- Turn it on in Dev Console API Keys (sandbox) or the live console equivalent. Element Pay can also flip the flag ops-side if needed.
Auth and environment
3. Register a wallet (once per customer)
Prerequisite: vault customer approved (pcus_…) — Customer KYC.
Enrollment is API-only (no Console signing-keys UI). EVM addresses only (0x + 40 hex) — not Stellar G….
1
Start challenge
POST /partner/customers/{customer_id}/signing-keys/challenge with { "address": "0x…" }2
Customer signs
Present our
typed_data to the wallet (eth_signTypedData_v4) — do not rebuild it client-side3
Complete enrollment
POST …/signing-keys/complete with { "key_id", "signature" } → key becomes active4
Optional manage
List keys, revoke, or re-challenge with a new address to replace a lost wallet
Step A — Start challenge
Challenge success — what to expect
EIP-712 domain differs sandbox vs live. Always use the
typed_data from the environment you are calling. Do not forge or rebuild it.Step B — Customer signs in wallet
Customer signs the challengetyped_data with the same address (MetaMask, WalletConnect, or equivalent → eth_signTypedData_v4).
Step C — Complete enrollment
Complete success — what to expect
Step D — Optional manage
4. Every order (after a wallet is registered)
Only when Signed accepts is on for the API key and the customer has an active signing key.1
Quote
POST /partner/orders/quote with customer_id + normal quote fields2
Customer signs quote typed_data
Use
data.signing.typed_data from the quote response (not the register typed_data)3
Accept with signature
POST /partner/orders/{quote_id}/accept with signed_accept.signature4
Settle
Deposit / settlement continues as today — webhooks unchanged
Quote
Same as Quote and accept. When signed accepts are required and a signer is enrolled, the quote response includes asigning block:
data.signing.typed_data to the enrolled wallet. Do not use the register/challenge signature here.
Accept
5. Constraints
- EVM only in v1 (not Stellar)
- Nonce = replay protection across quotes
- Never forge
typed_data— always pass Element Pay’s payload to the wallet - Provider-neutral: same flow for every corridor rail
- Desk / accept-on-behalf fails when Signed accepts is on for that key
6. Failure cheat-sheet
Next
- Quote and accept — default empty-body flow
- Customer KYC — approve
pcus_…before enrollment - Webhooks — settlement events unchanged