Skip to main content
This endpoint uses a three-phase flow: prepare, authorize, status. Each phase requires a separate POST request.
curl -X POST https://api.payonproof.com/api/execute-transfer \
  -H "Content-Type: application/json" \
  -d '{
    "phase": "prepare",
    "route": {
      "id": "route_abc123",
      "originAnchor": {"id": "anchor_us_1"},
      "destinationAnchor": {"id": "anchor_ph_1"},
      "originCurrency": "USD",
      "destinationCurrency": "PHP",
      "available": true
    },
    "amount": 100,
    "senderAccount": "GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  }'

POST /api/execute-transfer

Executes cross-border transfers using Stellar anchors via SEP-10 authentication and SEP-24 interactive flows.

Three-phase execution flow

  1. prepare: Generate SEP-10 challenges for both anchors
  2. authorize: Exchange signed challenges for JWTs, start SEP-24 interactive flows
  3. status: Poll transaction status without exposing anchor JWTs to frontend

Phase 1: Prepare

Generates SEP-10 authentication challenges for the selected route’s origin and destination anchors.

Request body (prepare)

phase
string
required
Must be "prepare"
route
object
required
Route object from /api/compare-routes
amount
number
required
Transfer amount in origin currency (must be > 0)
senderAccount
string
required
Stellar public key (G…) of sender

Response (prepare)

status
string
“needs_signature”
meta
object
clientDomain
string
Client domain used for SEP-10
prepared
object
transactionId
string
Unique transaction ID (e.g., “POP-1234567890-ABC123”)
routeId
string
Route ID
senderAccount
string
Sender’s Stellar account
amount
number
Transfer amount
createdAt
string
ISO 8601 timestamp
anchors
array
Array of anchor auth objects (origin and destination)

Example response (prepare)

{
  "status": "needs_signature",
  "meta": {
    "clientDomain": "app.payonproof.com"
  },
  "prepared": {
    "transactionId": "POP-1709467200-A3F9B2",
    "routeId": "route_abc123",
    "senderAccount": "GXXXXXXXXXXXXXXX",
    "amount": 100,
    "createdAt": "2026-03-03T10:00:00.000Z",
    "anchors": [
      {
        "role": "origin",
        "anchorId": "anchor_us_1",
        "anchorName": "MoneyGram",
        "domain": "stellar.moneygram.com",
        "assetCode": "USDC",
        "assetIssuer": "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
        "amount": 100,
        "account": "GXXXXXXXXXXXXXXX",
        "webAuthEndpoint": "https://stellar.moneygram.com/auth",
        "transferServerSep24": "https://stellar.moneygram.com/sep24",
        "challengeXdr": "AAAAAgAAAAC...",
        "networkPassphrase": "Public Global Stellar Network ; September 2015"
      },
      {
        "role": "destination",
        "anchorId": "anchor_ph_1",
        "anchorName": "Philippines Anchor",
        "domain": "anchor.example.ph",
        "assetCode": "PHP",
        "amount": 100,
        "account": "GXXXXXXXXXXXXXXX",
        "webAuthEndpoint": "https://anchor.example.ph/auth",
        "transferServerSep24": "https://anchor.example.ph/sep24",
        "challengeXdr": "AAAAAgAAAAD...",
        "networkPassphrase": "Public Global Stellar Network ; September 2015"
      }
    ]
  }
}

Phase 2: Authorize

Exchanges signed SEP-10 challenges for JWT tokens and initiates SEP-24 interactive flows.

Request body (authorize)

phase
string
required
Must be "authorize"
prepared
object
required
The entire prepared object from the prepare phase response
signatures
object
required
Signed challenge XDRs for each anchor role

Response (authorize)

status
string
“processing”
transaction
object
id
string
Transaction ID
routeId
string
Route ID
amount
number
Transfer amount
status
string
”processing”
createdAt
string
ISO 8601 timestamp
senderAccount
string
Sender account
statusRef
string
Encrypted status reference (save this!)
callbackUrl
string
Webhook URL for anchor callbacks
popEnv
string
Environment (“staging” or “production”)
anchorFlows
object
originDeposit
object
id
string
SEP-24 transaction ID
url
string
Interactive flow URL (open in browser/iframe)
type
string
Flow type
anchorName
string
Anchor name
destinationWithdraw
object
Same structure as originDeposit

Example response (authorize)

{
  "status": "processing",
  "transaction": {
    "id": "POP-1709467200-A3F9B2",
    "routeId": "route_abc123",
    "amount": 100,
    "status": "processing",
    "createdAt": "2026-03-03T10:00:00.000Z",
    "senderAccount": "GXXXXXXXXXXXXXXX",
    "statusRef": "aGVsbG8ud29ybGQ.dGFn.ZW5jcnlwdGVk",
    "callbackUrl": "https://api.payonproof.com/api/anchors/sep24/callback?transactionId=POP-1709467200-A3F9B2&callbackToken=abc123&secret=...",
    "popEnv": "production",
    "anchorFlows": {
      "originDeposit": {
        "id": "mg_dep_12345",
        "url": "https://stellar.moneygram.com/sep24/interactive?token=...",
        "type": "interactive_customer_info_needed",
        "anchorName": "MoneyGram"
      },
      "destinationWithdraw": {
        "id": "ph_wd_67890",
        "url": "https://anchor.example.ph/sep24/interactive?token=...",
        "type": "interactive_customer_info_needed",
        "anchorName": "Philippines Anchor"
      }
    }
  }
}

Phase 3: Status

Polls transaction status from both anchors without exposing JWTs to the frontend.

Request body (status)

phase
string
required
Must be "status"
transactionId
string
required
Transaction ID from authorize phase
statusRef
string
required
Encrypted status reference from authorize phase

Response (status)

status
string
“ok”
transactionId
string
Transaction ID
stellarTxHash
string
Stellar transaction hash (if available)
completed
boolean
Whether transaction is complete
source
string
“callback” if from anchor callback, otherwise from polling
anchors
array
Per-anchor status results

Example response (status)

{
  "status": "ok",
  "transactionId": "POP-1709467200-A3F9B2",
  "stellarTxHash": "3389e9f0f1a65f19736cacf544c2e825313e8447f569233bb8db39aa607c8889",
  "completed": true,
  "source": "polling",
  "anchors": [
    {
      "role": "origin",
      "anchorName": "MoneyGram",
      "interactiveId": "mg_dep_12345",
      "ok": true,
      "status": "completed",
      "stellarTxHash": "3389e9f0f1a65f19736cacf544c2e825313e8447f569233bb8db39aa607c8889",
      "externalTransactionId": "MG123456789"
    },
    {
      "role": "destination",
      "anchorName": "Philippines Anchor",
      "interactiveId": "ph_wd_67890",
      "ok": true,
      "status": "completed"
    }
  ]
}

Error responses

400 Bad Request - Invalid phase
{
  "error": "Invalid phase. Use 'prepare', 'authorize', or 'status'."
}
400 Bad Request - Route not operational
{
  "error": "Selected route is not operational. Choose an available route (anchors with valid SEP-10/SEP-24)."
}
400 Bad Request - Missing client domain
{
  "error": "Unable to resolve client_domain for SEP-10. Set SEP10_CLIENT_DOMAIN in API env."
}
502 Bad Gateway - Anchor service failure
{
  "error": "SEP-10 challenge failed at https://anchor.example.com/auth (404): Not Found"
}

Implementation notes

Security features

  • Status polling state is encrypted using EXECUTION_STATE_SECRET
  • Anchor JWTs never exposed to frontend
  • Client domain signatures handled server-side
  • SEP-24 callbacks authenticated via ANCHOR_CALLBACK_SECRET

MoneyGram-specific behavior

  • Requires MONEYGRAM_USER_ID memo for identification
  • Uses special USDC issuer: GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN
  • Automatically routes to preview/ext environments based on MONEYGRAM_USE_PREVIEW and POP_ENV

SEP-10 challenge caching

  • Challenges cached for 30 seconds to avoid duplicate requests
  • Multiple fallback strategies for challenge parameters (memo, home_domain, client_domain)

Asset resolution

  • Automatically resolves asset codes from SEP-24 /info endpoint
  • Falls back to first enabled asset if requested asset not found
  • Prefers assets with issuer over fiat-like codes

Build docs developers (and LLMs) love