Journey Docs
Members APITransfers

Create Transfer

Transfer points to another member or create pending gift transfer

POST/members/me/transfers

Overview

Transfer points to another member directly (using their member external ID) or create a pending gift transfer (using email or phone). Pending transfers can be claimed by the recipient later when they join or verify their contact information.

Authentication

Requires a valid Clerk JWT token in the Authorization header.

Security

Reverification Required: This endpoint requires reverification if the user hasn't verified their credentials within the last 10 minutes. This is a sensitive action involving point transfers.

Request Body

toMemberExternalIdbodystring

Recipient member external ID (ULID) for existing member transfer. Must be exactly 26 characters.

recipientEmailbodystring

Recipient email for gift transfer (pending). Valid email format required.

recipientPhonebodystring

Recipient phone for gift transfer (pending). Must be valid E.164 format (e.g., +1234567890).

amountbodynumberrequired

Amount of points to transfer. Must be at least 1 point and within your transfer limits.

notebodystring

Optional note/message for the transfer. Maximum 500 characters.

Note: Either toMemberExternalId OR (recipientEmail or recipientPhone) must be provided, but not both.

Request

curl -X POST "http://localhost:3000/v1/members/me/transfers" \
  -H "Authorization: Bearer YOUR_CLERK_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "toMemberExternalId": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
    "amount": 1000,
    "note": "Happy birthday!"
  }'

Response

transferIdstringrequired

Transfer ID (correlation ID) for tracking the transfer

correlationIdstringrequired

Correlation ID for the transfer transaction (same as transferId)

amountnumberrequired

Amount transferred in points

statusstringrequired

Transfer status: "COMPLETED" for direct transfers, "PENDING" for gift transfers

expiresAtstring

Expiration date for pending transfers (ISO 8601 format). Only present for gift transfers.

Response

{
  "transferId": "018f1234-5678-9abc-def0-123456789abc",
  "correlationId": "018f1234-5678-9abc-def0-123456789abc",
  "amount": 1000,
  "status": "COMPLETED"
}

Transfer Types

Direct Transfer (Member-to-Member)

  • Recipient: Existing member identified by external ID
  • Status: COMPLETED immediately
  • Points: Transferred instantly to recipient's account
  • Use Case: Send points to known members

Gift Transfer (Pending)

  • Recipient: Email or phone number (not yet a member)
  • Status: PENDING until claimed
  • Expiration: 30 days from creation
  • Points: Held in escrow until claimed or expired
  • Use Case: Gift points to friends who aren't members yet

Validation Rules

Amount Limits

  • Minimum: 1 point
  • Maximum: Based on your tier and transfer limits
  • Check: Use Get Transfer Limits to verify

Recipient Validation

  • Member External ID: Must be valid 26-character ULID
  • Email: Must be valid email format
  • Phone: Must be valid E.164 format
  • Self Transfer: Cannot transfer to yourself

Balance Check

  • Sufficient Balance: Must have enough VALID points
  • Real-time: Balance checked at transfer time
  • Type: Only VALID points can be transferred (not CREDIT points)

Error Handling

Common Errors

Error CodeDescriptionResolution
INSUFFICIENT_BALANCENot enough pointsCheck wallet balance
SELF_TRANSFERTrying to send to selfUse different recipient
TRANSFER_LIMIT_EXCEEDEDExceeds daily/monthly limitsWait or contact support
TRANSFER_FAILEDGeneric transfer failureRetry or contact support

Rate Limiting

  • No specific rate limit for transfers
  • Security measures in place for fraud prevention
  • Reverification required for sensitive actions

Use Cases

Send Birthday Gift

const sendBirthdayGift = async (recipientEmail: string, amount: number) => {
  try {
    const response = await fetch('/api/members/me/transfers', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        recipientEmail,
        amount,
        note: '🎂 Happy Birthday! Enjoy some points on me!'
      })
    });
    
    const result = await response.json();
    
    if (response.ok) {
      console.log(`Gift sent! Transfer ID: ${result.transferId}`);
      console.log(`Status: ${result.status}`);
      if (result.expiresAt) {
        console.log(`Expires: ${result.expiresAt}`);
      }
    } else {
      handleTransferError(result);
    }
  } catch (error) {
    console.error('Failed to send gift:', error);
  }
};

Transfer to Known Member

const transferToMember = async (memberExternalId: string, amount: number, note: string) => {
  try {
    const response = await fetch('/api/members/me/transfers', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        toMemberExternalId: memberExternalId,
        amount,
        note
      })
    });
    
    const result = await response.json();
    
    if (response.ok && result.status === 'COMPLETED') {
      console.log(`Transfer completed! ${amount} points sent.`);
    }
  } catch (error) {
    console.error('Transfer failed:', error);
  }
};

Bulk Gift Campaign

const sendBulkGifts = async (recipients: Array<{email: string, amount: number}>) => {
  const results = await Promise.allSettled(
    recipients.map(({email, amount}) => 
      fetch('/api/members/me/transfers', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          recipientEmail: email,
          amount,
          note: 'Thank you for being an amazing customer!'
        })
      })
    )
  );
  
  const successful = results.filter(r => r.status === 'fulfilled').length;
  console.log(`${successful}/${recipients.length} gifts sent successfully`);
};

Security Notes

Reverification

  • Required: For all transfer operations
  • Timeframe: Within last 10 minutes
  • Methods: SMS OTP or email verification
  • Bypass: Not possible for security reasons

Fraud Prevention

  • Monitoring: Unusual transfer patterns detected
  • Limits: Daily and monthly transfer limits enforced
  • Validation: Recipient verification for suspicious activity

On this page