OTC requester flows
OTC requester flows combine gating checks, request creation, offer acceptance, escrow signatures, and proof exchange. Prefer the Vudy OTC capability for creation; use the dedicated requester endpoints for the follow-up actions returned by status.
Prerequisites#
- Session or header profile context
- Passing OTC gating (
canCreate: true) - Team bank account (
userBankId) and tax info as required - KYC or KYB approved for the acting profile/team
- Compliance fee paid when gating reports
missing.complianceFee
Flow overview#
- Check OTC gating
- Create an OTC request
- Poll for offers
- Accept or reject an offer
- Complete escrow / proof steps
- Poll until
completed,cancelled, orconflict
1. Check gating#
curl -sS https://api-stg.vudy.app/v1/otc/gating \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "x-profile-id: PROFILE_UUID" \
-H "x-team-id: TEAM_UUID"const { data } = await fetch("https://api-stg.vudy.app/v1/otc/gating", {
headers: {
"x-api-key": process.env.VUDY_API_KEY,
"x-profile-id": profileId,
"x-team-id": teamId,
// or Authorization: Bearer <session>
},
}).then((r) => r.json());
// {
// canCreate, amountAvailableWithoutFee, gate,
// missing: { kyc?, kyb?, taxInfo?, bank?, complianceFee? },
// warnings?
// }
Resolve each missing item via KYC/KYB, team banks/tax info, or compliance checkout before create.
2. Validate and create an OTC request#
Preferred: discover a Vudy OTC capability, validate every parameter marked signedInput, then POST /channels/quote and POST /channels/execute (session required for execute). For example, validate userBankId when discovery requires it:
curl -sS https://api-stg.vudy.app/channels/vudy/validate \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "x-profile-id: PROFILE_UUID" \
-H "x-team-id: TEAM_UUID" \
-H "Content-Type: application/json" \
-d '{
"paramKey": "userBankId",
"value": "BANK_UUID",
"capabilityId": "CAPABILITY_UUID"
}'Pass the result to execute as "userBankId": ["BANK_UUID", "SIGNATURE"]. Validate taxId the same way when it is present and marked as signed. See Channel payments for quote/execute examples.
The provider-specific create route below is retained for integrations using the classic Vudy payload:
curl -sS https://api-stg.vudy.app/channel/vudy/otc/create \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "x-profile-id: PROFILE_UUID" \
-H "x-team-id: TEAM_UUID" \
-H "Content-Type: application/json" \
-d '{
"targetAddress": "0xRECEIVER",
"amount": 1000,
"channelParams": {
"requestType": "buy",
"amountType": "buy",
"buyCurrency": "USDC",
"sellCurrency": "USD",
"buyChain": "ethereum",
"sellChain": "ethereum",
"userBankId": "BANK_UUID",
"taxId": "AB-123"
}
}'const { data } = await fetch(
"https://api-stg.vudy.app/channel/vudy/otc/create",
{
method: "POST",
headers: {
"x-api-key": process.env.VUDY_API_KEY,
"x-profile-id": profileId,
"x-team-id": teamId,
"Content-Type": "application/json",
},
body: JSON.stringify({
targetAddress: receiverAddress,
amount: 1000,
channelParams: {
requestType: "buy", // or "sell"
amountType: "buy", // which side `amount` refers to
buyCurrency: "USDC",
sellCurrency: "USD",
buyChain: "ethereum",
sellChain: "ethereum",
userBankId: bankUuid,
taxId: "AB-123", // optional; alphanumeric + dashes
},
}),
},
).then((r) => r.json());
// Prefer capability execute for new integrations; response includes
// { txId, channelTableType: "ch_vudy_otc_request", recordId, walletFlow }
// Provider-specific create returns the OTC request record (use its id as OTC_REQUEST_ID).
3. Poll for offers#
curl -sS https://api-stg.vudy.app/channels/vudy/TX_OR_RECORD_ID \
-H "x-api-key: vudy_sandbox_YOUR_KEY"Response includes detail (OTC status), offers, actionNeeded, and steps. Use the recordId returned by capability execute as the OTC request id for requester actions.
4. Accept or reject an offer#
Generate the requester’s escrow signature with the active session:
curl -sS https://api-stg.vudy.app/v1/otc/signatures/create-escrow/user \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "Authorization: Bearer SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"otcRequestId":"OTC_REQUEST_ID","offerId":"OFFER_ID"}'For a managed wallet, use returned data.signature. If the response instead contains typed data, sign it with the request wallet. Then call the exact action URL returned by status:
# Accept
curl -sS -X POST \
https://api-stg.vudy.app/channels/vudy/action/accept_offer/OTC_REQUEST_ID \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"offerId":"OFFER_ID","userSignature":"0x..."}'
# Reject
curl -sS -X POST \
https://api-stg.vudy.app/channels/vudy/action/reject_offer/OTC_REQUEST_ID \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"offerId":"OFFER_ID"}'await fetch(
`https://api-stg.vudy.app/channels/vudy/action/accept_offer/${otcRequestId}`,
{
method: "POST",
headers: {
"x-api-key": process.env.VUDY_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ offerId, userSignature }),
},
);Offer statuses: new → accepted | rejected | expired.
5. Proof steps#
When status reaches proof phases, follow the role-specific steps:
- Buy request: requester submits proof; accepted provider verifies or denies it.
- Sell request: accepted provider submits proof; requester verifies or denies it.
Dedicated requester routes are:
| Step | Path |
|---|---|
| Prepare upload | POST /channel/vudy/otc/{id}/prepare-proof |
| Submit proof | POST /channel/vudy/otc/{id}/submit-proof |
| Verify proof | POST /channel/vudy/otc/{id}/verify-proof |
| Deny proof | POST /channel/vudy/otc/{id}/deny-proof |
curl -sS -X POST \
https://api-stg.vudy.app/channel/vudy/otc/OTC_REQUEST_ID/prepare-proof \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"fileNames":["receipt.pdf"],"splitIndex":0}'Upload files to the returned signed URLs, generate the split signature, then submit. See Files and proofs.
6. Cancel#
curl -sS -X POST \
"https://api-stg.vudy.app/channels/vudy/action/cancel_tx/OTC_REQUEST_ID" \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"requestId":"OTC_REQUEST_ID"}'Cancelling OTC sets detail status cancelled and parent tx canceled (US spelling on parent).
Status transitions#
OTC request detail:
new → offerReceived → processing → proofRequired → proofing → proofSubmitted → verifying → completed
Terminal / exception: cancelled, conflict.
Next steps#
- Files and proofs
- KYC and KYB
- OTC reference
- OTC provider (counterpart)