Vudy sends

Sends move tokens to recipients identified by address or Vudy tag. Use the dedicated /channel/vudy/send/* routes and poll for completion.

Prerequisites#

  • API key + profile context (session or headers)
  • Sufficient token allowance and gas (see Wallets)
  • Recipient address or resolvable Vudy tag

Flow overview#

  1. Resolve tags if needed
  2. Preview the send
  3. Ensure allowances and gas
  4. Create the send
  5. Process each regular, gas, or sponsored transaction
  6. Poll parent/child transaction status until completed or expired

1. Resolve a Vudy tag (optional)#

curl -sS "https://api-stg.vudy.app/channel/vudy/tag?tag=USERNAME" \
  -H "x-api-key: vudy_sandbox_YOUR_KEY"

Use tag / tags or address (not both). Multiple tags: ?tags=alice,bob or repeated tag=.

2. Preview#

curl -sS https://api-stg.vudy.app/channel/vudy/send/preview \
  -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 '{
    "senderAddress": "0xSENDER",
    "amount": 50,
    "channelParams": {
      "chain": "ethereum",
      "token": "USDC",
      "recipients": [{ "address": "0xRECIPIENT", "amount": 50 }]
    }
  }'

Preview does not persist a send.

3. Create#

curl -sS https://api-stg.vudy.app/channel/vudy/send/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": "0xSENDER",
    "amount": 50,
    "channelParams": {
      "chain": "ethereum",
      "token": "USDC",
      "note": "Payroll",
      "recipients": [
        { "address": "0xRECIPIENT", "amount": 50 },
        { "vudyTag": "teammate", "amount": 10 }
      ]
    }
  }'
const { data } = await fetch(
	"https://api-stg.vudy.app/channel/vudy/send/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: senderAddress,
			amount: 50,
			channelParams: {
				chain: "ethereum",
				token: "USDC",
				recipients: [{ address: recipient, amount: 50 }],
				// sendAutomatically: true also requires orgId (opaque org id from verify-otp)
			},
		}),
	},
).then((r) => r.json());

// { txId, sendId, transactions, gasTransactions, allowanceOk, needsSponsorship, ... }

Each recipient must include address or vudyTag.

4. Process transactions#

Use transactions, gasTransactions, allowanceOk, and needsSponsorship from create. The route id below may be the returned sendId or txId.

StepPath
Prepare external sponsored signaturePOST /channel/vudy/send/{id}/prepare-sponsored-tx
Process sponsored transactionPOST /channel/vudy/send/{id}/process-sponsored-tx
Process regular or gas transactionPOST /channel/vudy/send/{id}/process-send-tx

Approve allowances via Wallets when allowanceOk is false.

For a regular or gas transaction, call with the session bound to the sender profile:

curl -sS https://api-stg.vudy.app/channel/vudy/send/SEND_OR_TX_ID/process-send-tx \
  -H "x-api-key: vudy_sandbox_YOUR_KEY" \
  -H "Authorization: Bearer SESSION_JWT" \
  -H "Content-Type: application/json" \
  -d '{"txType":"regular","txIndex":0}'

For a managed wallet, the response contains txHash. For an external wallet, it contains to, data, and value; sign and broadcast that payload with the sender wallet.

For an externally signed sponsored transaction:

const prepared = await fetch(
	`https://api-stg.vudy.app/channel/vudy/send/${sendId}/prepare-sponsored-tx`,
	{
		method: "POST",
		headers: {
			"x-api-key": process.env.VUDY_API_KEY,
			Authorization: `Bearer ${session}`,
			"Content-Type": "application/json",
		},
		body: JSON.stringify({ txIndex: 0 }),
	},
).then((r) => r.json());

const userSignature = await wallet.signTypedData(prepared.data.typedData);

await fetch(
	`https://api-stg.vudy.app/channel/vudy/send/${sendId}/process-sponsored-tx`,
	{
		method: "POST",
		headers: {
			"x-api-key": process.env.VUDY_API_KEY,
			Authorization: `Bearer ${session}`,
			"Content-Type": "application/json",
		},
		body: JSON.stringify({
			prepareId: prepared.data.prepareId,
			attestation: prepared.data.attestation,
			userSignature,
		}),
	},
);

5. Status transitions#

StageStatus
Createdpending
Confirmed on-chaincompleted
Processing failedfailed
Timed outexpired
Cancelledcanceled

Poll GET /channels/vudy/{txId} or GET /v1/tx/{id}. Listing txs may also expire stale pending sends.

No outbound completion event is emitted for sends. Reconcile by polling.

Next steps#