Files and proofs
File handling has two integrator-facing paths:
- Generic transaction files — upload URL → client upload → confirm → signed reads under
/v1/txs/{txId}/files* - OTC proofs — prepare proof → upload → submit → counterparties verify/deny
Limits#
| Rule | Value |
|---|---|
| Extensions | .pdf, .png, .jpg, .jpeg, .gif, .bmp, .webp |
| Not allowed | TIFF (.tif / .tiff) |
| Generic tx max size | 5 MB per file at confirmation |
| File name pattern | Alphanumeric, spaces, hyphens, dots + valid extension |
Generic transaction files#
Prerequisites#
- Conditional auth with resolved profile context
- Permission to attach files to the transaction (
canAddFilesToTransaction)
1. Request upload URLs#
curl -sS https://api-stg.vudy.app/v1/txs/TX_ID/files/upload-url \
-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 '{"fileNames":["invoice.pdf","receipt.png"]}'const { data } = await fetch(
`https://api-stg.vudy.app/v1/txs/${txId}/files/upload-url`,
{
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({ fileNames: ["invoice.pdf"] }),
},
).then((r) => r.json());
// { uploadId, uploadUrls }
2. Upload bytes to each signed URL#
Use each returned uploadUrls[] entry. Upload the bytes to its signedUrl before expiresAt, and preserve its filePath for confirmation:
curl -sS -X PUT "SIGNED_UPLOAD_URL" \
-H "Content-Type: application/pdf" \
--data-binary @invoice.pdf3. Confirm#
curl -sS https://api-stg.vudy.app/v1/txs/TX_ID/files \
-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 '{
"uploadId": "UPLOAD_ID",
"filePaths": ["path/from/upload-url"],
"fileNames": ["invoice.pdf"]
}'filePaths and fileNames must be the same length.
4. Read signed URLs#
curl -sS https://api-stg.vudy.app/v1/txs/TX_ID/files \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "x-profile-id: PROFILE_UUID" \
-H "x-team-id: TEAM_UUID"OTC proofs#
Used during OTC proofRequired / proofing / verifying phases. See OTC requester.
Prepare#
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":["wire-receipt.pdf"],"splitIndex":0}'const { data } = await fetch(
`https://api-stg.vudy.app/channel/vudy/otc/${otcRequestId}/prepare-proof`,
{
method: "POST",
headers: {
"x-api-key": process.env.VUDY_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ fileNames: ["wire-receipt.pdf"], splitIndex: 0 }),
},
).then((r) => r.json());
// { uploadUrls, uploadId, splitIndex }
Upload, generate a split signature, then submit#
Upload each file using the returned signed URL. Generate the approval signature with the session of the party identified by the status steps:
curl -sS https://api-stg.vudy.app/v1/otc/signatures/approve-split \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "Authorization: Bearer SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{
"otcRequestId": "OTC_REQUEST_ID",
"splitIndex": 0,
"userWalletAddress": "0xSIGNER"
}'For a managed wallet, use returned data.signature. If typed data is returned, sign it with userWalletAddress.
curl -sS -X POST \
https://api-stg.vudy.app/channel/vudy/otc/OTC_REQUEST_ID/submit-proof \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"splitIndex": 0,
"signature": "0x..."
}'Verify or deny#
The counterparty uses approve-split to generate the verify signature. To deny, first generate a deny signature:
curl -sS https://api-stg.vudy.app/v1/otc/signatures/deny-split \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "Authorization: Bearer SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{
"otcRequestId": "OTC_REQUEST_ID",
"splitIndex": 0,
"userWalletAddress": "0xSIGNER"
}'# Verify
curl -sS -X POST \
https://api-stg.vudy.app/channel/vudy/otc/OTC_REQUEST_ID/verify-proof \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"splitIndex":0,"signature":"0x..."}'
# Deny
curl -sS -X POST \
https://api-stg.vudy.app/channel/vudy/otc/OTC_REQUEST_ID/deny-proof \
-H "x-api-key: vudy_sandbox_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"splitIndex": 0,
"walletAddress": "0x...",
"signature": "0x...",
"reason": "Amount mismatch"
}'Denial moves the OTC request toward conflict. Successful verify advances toward completed.