OTC
Requester OTC surfaces: create and progress requests via /channel/vudy/otc/*, inspect status, check allowances, and produce user signatures. Provider-only routes: OTC provider.
Auth labels match the endpoint index. Envelopes follow Responses and errors.
For Header context routes, profile context may come from a valid session, both profile/team headers, or an API key scoped to both. GET /v1/otc is the exception documented below: it selects only the session profile or the API key’s scoped profile.
Endpoints#
| Method | Path | Auth |
|---|---|---|
POST | /channel/vudy/otc/create | Header context |
POST | /channel/vudy/otc/{id}/accept-offer | API key |
POST | /channel/vudy/otc/{id}/deny-proof | API key |
POST | /channel/vudy/otc/{id}/prepare-proof | API key |
POST | /channel/vudy/otc/{id}/reject-offer | API key |
POST | /channel/vudy/otc/{id}/submit-proof | API key |
POST | /channel/vudy/otc/{id}/verify-proof | API key |
GET | /v1/otc | Session / scoped key |
POST | /v1/otc/allowance/check | WRITE |
GET | /v1/otc/gating | Header context |
GET | /v1/otc/proof/{requestId} | API key |
POST | /v1/otc/signatures/approve | Session + WRITE |
POST | /v1/otc/signatures/approve-split | Session + WRITE |
POST | /v1/otc/signatures/create-escrow/user | Session + WRITE |
POST | /v1/otc/signatures/deny-split | Session + WRITE |
GET | /v1/otc/txs | Header context |
Details#
POST /channel/vudy/otc/create#
Creates an OTC request for the active profile/team context.
| Auth | Session / scoped key |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key plus either Authorization: Bearer <session> or an API key scoped to a profile. This handler does not use x-profile-id / x-team-id as its profile selector.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Authorization | Required unless the API key carries profileId |
Path parameters#
None.
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
targetAddress | string | Yes |
amount | number | Yes |
channelParams | object | Yes |
channelParams.requestType | enum: buy, sell | Yes |
channelParams.amountType | enum: buy, sell | Yes |
channelParams.buyCurrency | string | Yes |
channelParams.sellCurrency | string | Yes |
channelParams.buyChain | string | Yes |
channelParams.sellChain | string | Yes |
channelParams.userBankId | UUID | Yes |
channelParams.taxId | string | No; 1–64 alphanumeric/dash characters |
Success response#
Envelope wraps the created OTC request resource (DB row). Reliably known top-level keys include:
{
"success": true,
"data": {
"id": "otc-uuid",
"txId": "tx-uuid",
"status": "new",
"type": "buy",
"buyAmount": 1000.5,
"sellAmount": 0,
"buyCurrency": "token-uuid",
"sellCurrency": "token-uuid",
"buyChainId": "chain-uuid",
"sellChainId": "chain-uuid",
"profileBankId": "bank-uuid",
"amountUsd": 1000.5,
"createdAt": "2026-07-28T12:00:00.000Z",
"updatedAt": "2026-07-28T12:00:00.000Z"
}
}Additional row fields (for example idHash, feeInfo, escrowAddress, txs) may be present depending on persistence.
Errors and statuses#
HTTP statuses used by this route: 400, 403, 500, 200, 401.
| Code | Meaning |
|---|---|
CH-01 | Invalid API key |
CH-02 | Invalid profile or team access |
CH-03 | Invalid request body |
CH-04 | Channel not available for app |
CH-06 | Invalid channel parameters |
CH-07 | Channel function execution failed |
POST /channel/vudy/otc/{id}/accept-offer#
Accepting an OTC offer. It validates the user signature and creates the escrow contract on-chain.
| Auth | API key |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key. No session required.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Path parameters#
| Param | Description |
|---|---|
id | Path parameter. |
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
offerId | string | Yes |
userSignature | string | Yes |
Success response#
{
"success": true,
"data": {
"escrowAddress": "0x...",
"txHash": "0x...",
"offerId": "offer-456",
"requestId": "otc-123"
}
}Errors and statuses#
HTTP statuses used by this route: 400, 500, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
POST /channel/vudy/otc/{id}/deny-proof#
Denies submitted OTC proof for a request (moves the flow toward conflict when denial succeeds).
| Auth | API key |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key. No session required.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Path parameters#
| Param | Description |
|---|---|
id | Path parameter. |
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
splitIndex | number | Yes |
walletAddress | string | Yes |
signature | string | Yes |
reason | string | No |
Success response#
{
"success": true,
"data": {
"requestId": "otc-123",
"splitIndex": 0,
"reason": "Invalid receipt",
"txHash": "0x...",
"status": "conflict"
}
}Errors and statuses#
HTTP statuses used by this route: 400, 500, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
POST /channel/vudy/otc/{id}/prepare-proof#
Preparing proof upload on an OTC request. It generates signed URLs for uploading proof files to cloud storage.
| Auth | API key |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key. No session required.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Path parameters#
| Param | Description |
|---|---|
id | Path parameter. |
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
fileNames | string[] | Yes — at least one supported PDF/image filename |
splitIndex | number | Yes |
Success response#
{
"success": true,
"data": {
"uploadUrls": [
{
"fileName": "receipt.pdf",
"signedUrl": "https://storage.googleapis.com/...",
"expiresAt": "2024-01-01T01:00:00Z",
"filePath": "otc-proofs/otc-123/..."
}
],
"uploadId": "upload_1234567890_abc",
"splitIndex": 0
}
}Errors and statuses#
HTTP statuses used by this route: 400, 500, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
POST /channel/vudy/otc/{id}/reject-offer#
Rejecting an OTC offer. It includes authorization checks to ensure only the OTC request creator’s team can reject offers.
| Auth | API key |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key. No session required.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Path parameters#
| Param | Description |
|---|---|
id | Path parameter. |
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
offerId | string | Yes |
Success response#
{
"success": true,
"data": {
"offerId": "offer-456",
"status": "rejected"
}
}Errors and statuses#
HTTP statuses used by this route: 400, 403, 500, 404, 200, 401.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
POST /channel/vudy/otc/{id}/submit-proof#
Submitting proof on an OTC request. It processes uploaded proof files and submits approval transaction to blockchain.
| Auth | API key |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key. No session required.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Path parameters#
| Param | Description |
|---|---|
id | Path parameter. |
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
splitIndex | number | Yes |
signature | string | Yes |
forceNewProof | boolean | No |
Success response#
{
"success": true,
"data": {
"requestId": "otc-123",
"splitIndex": 0,
"txHash": "0x..."
}
}Errors and statuses#
HTTP statuses used by this route: 400, 500, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
POST /channel/vudy/otc/{id}/verify-proof#
Verifies submitted OTC proof for a request (advances the flow when verification succeeds).
| Auth | API key |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key. No session required.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Path parameters#
| Param | Description |
|---|---|
id | Path parameter. |
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
splitIndex | number | Yes |
signature | string | Yes |
Success response#
{
"success": true,
"data": {
"requestId": "otc-123",
"splitIndex": 0,
"txHash": "0x...",
"status": "verification sent"
}
}Errors and statuses#
HTTP statuses used by this route: 400, 500, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
GET /v1/otc#
Retrieving OTC requests for a profile with comprehensive filtering options.
| Auth | Header context |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key. Use Authorization: Bearer <session> or x-profile-id + x-team-id.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Authorization | Optional (preferred) |
x-profile-id | Required when no session |
x-team-id | Required when no session |
Path parameters#
None.
Query parameters#
| Field | Type | Required |
|---|---|---|
buyCurrency | string | No |
endDate | string | No |
limit | string | No |
maxAmountUsd | string | No |
maxBuyAmount | string | No |
maxSellAmount | string | No |
minAmountUsd | string | No |
minBuyAmount | string | No |
minSellAmount | string | No |
offset | string | No |
sellCurrency | string | No |
startDate | string | No |
status | repeatable enum: new, processing, proofing, verifying, conflict, completed, cancelled | No |
type | enum: buy, sell | No |
updatedEndDate | string | No |
updatedStartDate | string | No |
Body#
Not applicable.
Success response#
{
"success": true,
"data": [
{
"otcRequest": {
"id": "otc-123",
"txId": "tx-456",
"status": "new",
"type": "buy",
"buyAmount": 1000.5,
"sellAmount": 950.0,
"createdAt": "2024-01-01T00:00:00Z"
},
"tx": {
"id": "tx-456",
"status": "pending"
},
"buyToken": {
"symbol": "USDC"
},
"sellToken": {
"symbol": "USDT"
}
}
]
}Errors and statuses#
HTTP statuses used by this route: 400, 500, 200, 401.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
POST /v1/otc/allowance/check#
Checking ERC20 token allowance for the escrow factory contract. Compares current allowance with required amount.
| Auth | API key + WRITE |
| Permissions | WRITE |
| Guide | OTC requester guide |
Requires x-api-key. No session required.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Path parameters#
None.
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
chainId | number | Yes |
tokenAddress | string | Yes |
senderAddress | string | Yes |
amountToCheck | string | Yes |
Success response#
Standard envelope: { "success": true, "data": <resource> }. See the linked guide for workflow-specific fields.
Errors and statuses#
HTTP statuses used by this route: 400, 500, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
GET /v1/otc/gating#
OTC Gating Preflight API Endpoint
| Auth | Header context |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key. Use Authorization: Bearer <session> or x-profile-id + x-team-id.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Authorization | Optional (preferred) |
x-profile-id | Required when no session |
x-team-id | Required when no session |
Path parameters#
None.
Query parameters#
None.
Body#
Not applicable.
Success response#
Standard envelope: { "success": true, "data": <resource> }. See the linked guide for workflow-specific fields.
Errors and statuses#
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
GET /v1/otc/proof/{requestId}#
Returns time-limited signed URLs for OTC proof files the caller is authorized to access.
| Auth | API key |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key. No session required.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Path parameters#
| Param | Description |
|---|---|
requestId | Path parameter. |
Query parameters#
None.
Body#
Not applicable.
Success response#
{
"success": true,
"data": {
"proofUrls": [
{
"filePath": "otc-proofs/otc-123/1234567890-receipt.pdf",
"signedUrl": "https://storage.googleapis.com/...",
"expiresAt": "2024-01-01T01:00:00Z"
}
]
}
}Errors and statuses#
HTTP statuses used by this route: 400, 403, 500, 404, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
POST /v1/otc/signatures/approve#
Approves ERC-20 tokens for the OTC escrow factory. With a session that can sign, the API may submit the approval; otherwise it returns transaction data for the client to sign and send.
| Auth | Session + WRITE |
| Permissions | WRITE |
| Guide | OTC requester guide |
Requires x-api-key and Authorization: Bearer <session>.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Authorization | Yes — Bearer <session> |
Path parameters#
None.
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
chainId | number | Yes |
tokenAddress | string | Yes |
senderWallet | string | Yes |
amount | "max", positive number, or positive numeric string | Yes |
autoSign | boolean | No — defaults to false |
Success response#
Standard envelope: { "success": true, "data": <resource> }. See the linked guide for workflow-specific fields.
Errors and statuses#
HTTP statuses used by this route: 400, 500, 200, 401.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
POST /v1/otc/signatures/approve-split#
Generates EIP-712 data for approving escrow splits. With a session that can sign, the API may complete signing; otherwise it returns EIP-712 typed data for the client wallet.
| Auth | Session + WRITE |
| Permissions | WRITE |
| Guide | OTC requester guide |
Requires x-api-key and Authorization: Bearer <session>.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Authorization | Yes — Bearer <session> |
Path parameters#
None.
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
otcRequestId | string | Yes |
splitIndex | number | Yes |
userWalletAddress | string | Yes |
Success response#
Standard envelope: { "success": true, "data": <resource> }. See the linked guide for workflow-specific fields.
Errors and statuses#
HTTP statuses used by this route: 400, 403, 500, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
POST /v1/otc/signatures/create-escrow/user#
Generates EIP-712 data for user escrow approval. With a session that can sign, the API may complete signing; otherwise it returns EIP-712 typed data for the client wallet.
| Auth | Session + WRITE |
| Permissions | WRITE |
| Guide | OTC requester guide |
Requires x-api-key and Authorization: Bearer <session>.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Authorization | Yes — Bearer <session> |
Path parameters#
None.
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
otcRequestId | string | Yes |
offerId | string | Yes |
Success response#
{
"success": true,
"data": {
"signature": "0x123abc...",
"signed": true
}
}Errors and statuses#
HTTP statuses used by this route: 400, 403, 500, 404, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
POST /v1/otc/signatures/deny-split#
Generates EIP-712 data for denying escrow splits. With a session that can sign, the API may complete signing; otherwise it returns EIP-712 typed data for the client wallet.
| Auth | Session + WRITE |
| Permissions | WRITE |
| Guide | OTC requester guide |
Requires x-api-key and Authorization: Bearer <session>.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Authorization | Yes — Bearer <session> |
Path parameters#
None.
Query parameters#
None.
Body#
| Field | Type | Required |
|---|---|---|
otcRequestId | string | Yes |
splitIndex | number | Yes |
userWalletAddress | string | Yes |
Success response#
Standard envelope: { "success": true, "data": <resource> }. See the linked guide for workflow-specific fields.
Errors and statuses#
HTTP statuses used by this route: 400, 403, 500, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).
GET /v1/otc/txs#
Retrieving OTC transactions with details and offers for profiles or teams.
| Auth | Header context |
| Permissions | Standard API-key access (no extra permission flags). |
| Guide | OTC requester guide |
Requires x-api-key. Use Authorization: Bearer <session> or x-profile-id + x-team-id.
Headers#
| Header | Required |
|---|---|
x-api-key | Yes |
Authorization | Optional (preferred) |
x-profile-id | Required when no session |
x-team-id | Required when no session |
Path parameters#
None.
Query parameters#
| Field | Type | Required |
|---|---|---|
type | string | No |
Body#
Not applicable.
Success response#
Standard envelope: { "success": true, "data": <resource> }. See the linked guide for workflow-specific fields.
Errors and statuses#
HTTP statuses used by this route: 400, 500, 200.
Typical failures: 403 (API key/permissions), 401 (session/context), 400 (validation), 429 (rate limit).