Internal Transfer

Internal transfers allow you to send funds to another Sznd user or business using their Sznd tag. This uses the Quotes API with quote_type: "TRANSFER".

Overview

  1. Look Up Recipient — Find the Sznd user or business by their tag
  2. Create a TRANSFER Quote — Lock in rates and fees using the recipient's user_tag
  3. Accept the Quote — Execute the transfer
  4. Webhook Notification — Receive confirmation when the transfer completes

For same-currency transfers between wallets, there's also a Direct Transfer shortcut that skips the quote step.


Step 1: Look Up Recipient

Verify the recipient exists and is active by looking up their Sznd tag.

Endpoint

GET /api/v1/client/users/by-tag

Authentication

http
x-api-key: <your-api-key>x-timestamp: <rfc3339-timestamp>x-signature: <hmac-sha256-signature>

Query Parameters

ParameterRequiredDescription
tagYesThe recipient's Sznd tag (4-20 alphanumeric characters)

Success Response (200 OK)

json
{  "user_id": "123e4567-e89b-12d3-a456-426614174000",  "first_name": "Jane",  "last_name": "Doe",  "email": "jane.doe@example.com",  "user_tag": "janedoe123",  "type_name": "Individual",  "status": "ACTIVE"}

Confirm the recipient details before proceeding.


Step 2: Create a TRANSFER Quote

Create a quote using the recipient's user_tag instead of a beneficiary_id.

Endpoint

POST /api/v1/client/quotes

Authentication

http
x-api-key: <your-api-key>x-timestamp: <rfc3339-timestamp>x-signature: <hmac-sha256-signature>

Request Body (Transfer)

json
{  "source_currency": "USD",  "target_currency": "USD",  "source_amount": "250.00",  "quote_type": "TRANSFER",  "user_tag": "janedoe123",  "tz": "UTC",  "narration": "Payment for services rendered",  "origin_reference": "TRANSFER-2026-001",  "on_behalf_of": "660e8400-e29b-41d4-a716-446655440001"}

Required Parameters

ParameterTypeDescription
source_currencystringCurrency to send from
target_currencystringCurrency to deliver
source_amountstringAmount to send (decimal string)
quote_typestring"TRANSFER"
user_tagstringRecipient's Sznd tag
tzstringTimezone (IANA format)
origin_referencestringYour unique reference (max 40 chars)
on_behalf_ofstring (UUID)Customer ID

Optional Parameters

ParameterTypeDescription
narrationstringDescription for the transfer

Success Response (201 Created)

json
{  "quote_id": "789e4567-e89b-12d3-a456-426614174000",  "source": {    "currency": "USD",    "amount": "100.00"  },  "target": {    "currency": "USD",    "amount": "250.00"  },  "exchange_rate": "1.00",  "fees": {    "processing_fee": "2.00",    "deposit_fee": "0.00",    "payout_fee": "0.00",    "total_fees": "2.00",    "fee_currency": "USD"  },  "total_payable": "250.00",  "valid_until": "2026-03-16T15:40:53Z",  "quote_status": "CREATED",  "type": "TRANSFER"}

Step 3: Accept the Quote

Accept the quote to execute the transfer. The funds are transferred immediately.

Endpoint

POST /api/v1/client/quotes/{id}/accept

Authentication

http
x-api-key: <your-api-key>x-timestamp: <rfc3339-timestamp>x-signature: <hmac-sha256-signature>

Success Response (200 OK)

json
{  "quote_id": "789e4567-e89b-12d3-a456-426614174000",  "transaction_id": "321e6543-e21b-43d2-b456-426614174111",  "transaction_reference": "TRX-20260316-XYZ123",  "total_payable": "102.00",  "timeline": {    "created_at": "2026-03-16T15:10:53Z",    "accepted_at": "2026-03-16T15:12:00Z"  },  "status": "PROCESSING"}

Step 4: Webhook Notification

You'll receive a transaction webhook when the transfer completes:

json
{  "event_type": "transaction",  "transaction_id": "321e6543-e21b-43d2-b456-426614174111",  "transaction_type": "TRANSFER",  "status": "COMPLETED",  "reference": "TRX-20260316-XYZ123",  "user_id": "660e8400-e29b-41d4-a716-446655440001",  "created_at": "2026-03-16T15:10:53Z",  "timestamp": "2026-03-16T15:12:30Z",  "source_currency": "USD",  "source_amount": "250.00",  "target_currency": "USD",  "target_amount": "250.00",  "amount_with_fees": "250.00",  "fee": "0.00",  "fee_currency": "USD",  "exchange_rate": "1.00",  "sender": {    "email": "sender@example.com",    "account_name": "Your Business"  },  "recipient": {    "email": "jane.doe@example.com",    "account_name": "Jane Doe"  }}

Example: Complete Internal Transfer Flow

cURL

Step 1: Look up recipient

bash
curl -X GET "https://api.transfaar.com/api/v1/client/users/by-tag?tag=janedoe123" \  -H "x-api-key: your-api-key" \  -H "x-timestamp: 2026-03-16T10:30:00Z" \  -H "x-signature: your-hmac-signature"

Step 2: Create transfer quote

bash
curl -X POST https://api.transfaar.com/api/v1/client/quotes \  -H "Content-Type: application/json" \  -H "x-api-key: your-api-key" \  -H "x-timestamp: 2026-03-16T10:31:00Z" \  -H "x-signature: your-hmac-signature" \  -d '{    "source_currency": "USD",    "target_currency": "USD",    "source_amount": "250.00",    "quote_type": "TRANSFER",    "user_tag": "janedoe123",    "tz": "UTC",    "narration": "Payment for services",    "origin_reference": "TRANSFER-2026-001",    "on_behalf_of": "660e8400-e29b-41d4-a716-446655440001"  }'

Step 3: Accept the quote

bash
curl -X POST https://api.transfaar.com/api/v1/client/quotes/789e4567-e89b-12d3-a456-426614174000/accept \  -H "Content-Type: application/json" \  -H "x-api-key: your-api-key" \  -H "x-timestamp: 2026-03-16T10:32:00Z" \  -H "x-signature: your-hmac-signature"

JavaScript

javascript
async function processInternalTransfer(apiKey, secretKey, transferData) {  // Step 1: Look up recipient by tag  const recipient = await apiCall(    "GET",    `/api/v1/client/users/by-tag?tag=${transferData.recipientTag}`,    apiKey,    secretKey,  );
  if (recipient.status !== "ACTIVE") {    throw new Error("Recipient account is not active");  }
  // Step 2: Create transfer quote  const quote = await apiCall(    "POST",    "/api/v1/client/quotes",    apiKey,    secretKey,    {      source_currency: transferData.sourceCurrency,      target_currency: transferData.targetCurrency,      source_amount: transferData.amount,      quote_type: "TRANSFER",      user_tag: transferData.recipientTag,      tz: "UTC",      narration: transferData.narration,      origin_reference: transferData.reference,      on_behalf_of: transferData.customerId,    },  );
  // Step 3: Accept the quote  const result = await apiCall(    "POST",    `/api/v1/client/quotes/${quote.quote_id}/accept`,    apiKey,    secretKey,  );
  return {    transactionId: result.transaction_id,    transactionReference: result.transaction_reference,    status: result.status,  };}

Same-Currency Shortcut: Direct Transfer

For same-currency transfers, you can skip the quote step and use the direct transfer endpoint:

POST /api/v1/client/transactions/transfer

json
{  "source_wallet_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",  "target_wallet_id": "b2c3d4e5-f6a7-8901-2345-678901bcdefg",  "amount": "100.00",  "idempotency_key": "550e8400-e29b-41d4-a716-446655440000"}

This is simpler but requires both wallets to use the same currency and you need the recipient's wallet ID. When you only have the recipient's tag, use the quote-based flow above.

For full details, see the Transfers API — Direct Transfer.


Best Practices

  1. Verify Recipient First — Always look up the user tag before creating a transfer to confirm the recipient is active.
  2. Use Unique Origin References — Each origin_reference must be unique to prevent duplicate transfers.
  3. Immediate Processing — Internal transfers are processed immediately upon quote acceptance, unlike external payouts which may take time.
  4. Store References — Save transaction_id and transaction_reference for reconciliation.