Skip to main content
This endpoint requires authentication via cron secret or Vercel cron headers. See Authentication for details.
curl -X POST "https://api.payonproof.com/api/anchors/ops?action=sync&secret=YOUR_CRON_SECRET"

POST /api/anchors/ops

Administrative endpoint for managing the anchor catalog. Supports three actions:
  1. sync - Import anchors and refresh capabilities (automated cron job)
  2. refresh_capabilities - Re-check SEP capabilities for existing anchors
  3. import_directory - Import anchors from external directory or JSON file

Authentication

Requires one of:
  • Query parameter ?secret=... matching CRON_SECRET env var
  • Vercel cron header x-vercel-cron (for scheduled jobs)
  • NODE_ENV !== "production" (development only)

Request body

action
string
required
Operation to perform: sync, refresh_capabilities, or import_directory

Common parameters

limit
number
default:"100"
Maximum number of anchors to process (for refresh_capabilities)
country
string
Filter by country code for refresh_capabilities (e.g., “US”)

Import directory parameters

downloadUrl
string
URL to download anchor directory JSON/CSV
anchors
array
Array of anchor objects to import directly (alternative to downloadUrl)
dryRun
boolean
default:"true"
If true, validate without writing to database
active
boolean
default:"true"
Mark imported anchors as active
allowedDomains
array
Whitelist of allowed anchor domains
useEnvAllowedDomains
boolean
default:"false"
Use ANCHOR_DIRECTORY_ALLOWED_DOMAINS from environment

Sync parameters

mode
string
default:"directory"
Discovery mode: directory or horizon
sourceUrl
string
Override anchor directory URL (default: STELLAR_ANCHOR_DIRECTORY_URL env)
directoryHome
string
default:"https://anchors.stellar.org/"
Base URL for auto-discovery
allowHorizon
boolean
default:"false"
Allow fallback to Horizon-based anchor discovery
refreshLimit
number
default:"300"
Number of anchors to refresh after import
sep1DisableThreshold
number
default:"3"
Auto-disable anchor after N consecutive SEP-1 404 errors

Response (sync)

status
string
“ok” on success
action
string
“sync”
triggeredAt
string
ISO 8601 timestamp
import
object
source
string
Data source URL
totalNormalized
number
Anchors normalized from source
written
number
Anchors written to database
skippedSourceRows
number
Invalid rows skipped
refresh
object
requested
number
Refresh limit
processed
number
Anchors processed
ok
number
Successful refreshes
errors
number
Failed refreshes
results
array
Per-anchor refresh results

Response example (sync)

{
  "status": "ok",
  "action": "sync",
  "triggeredAt": "2026-03-03T10:00:00.000Z",
  "sourceUrl": "https://anchors.stellar.org/anchors.json",
  "discoveryMode": "directory",
  "import": {
    "source": "https://anchors.stellar.org/anchors.json",
    "totalNormalized": 42,
    "written": 38,
    "skippedSourceRows": 4
  },
  "refresh": {
    "requested": 300,
    "processed": 38,
    "ok": 35,
    "errors": 3
  },
  "results": [
    {
      "id": "anchor_moneygram",
      "domain": "stellar.moneygram.com",
      "status": "ok"
    },
    {
      "id": "anchor_defunct",
      "domain": "defunct.example.com",
      "status": "error",
      "error": "stellar.toml not found (404)",
      "autoDisabled": true,
      "sep1_404_count": 3
    }
  ]
}

Response (refresh_capabilities)

{
  "status": "ok",
  "action": "refresh_capabilities",
  "refreshed": 100,
  "ok": 94,
  "errors": 6,
  "results": [
    {
      "id": "anchor_example",
      "domain": "anchor.example.com",
      "status": "ok"
    }
  ]
}

Response (import_directory)

{
  "status": "ok",
  "action": "import_directory",
  "dryRun": false,
  "source": "https://anchors.stellar.org/anchors.json",
  "totalNormalized": 42,
  "skippedSourceRows": 2,
  "rejectedByDomain": 0,
  "allowedDomainsCount": 0,
  "provenance": "directory",
  "written": 42,
  "sample": [
    {
      "id": "anchor_moneygram",
      "name": "MoneyGram",
      "domain": "stellar.moneygram.com",
      "country": "US",
      "currency": "USD",
      "type": "on-ramp"
    }
  ]
}

Error responses

401 Unauthorized
{
  "error": "Unauthorized cron request. Provide ?secret=... matching CRON_SECRET or configure Vercel cron header."
}
400 Bad Request
{
  "error": "Invalid action. Use: import_directory | refresh_capabilities | sync"
}
502 Bad Gateway
{
  "status": "error",
  "action": "sync",
  "error": "Anchor directory home unavailable (404)"
}

Use cases

Scheduled sync (Vercel cron)

vercel.json
{
  "crons": [
    {
      "path": "/api/anchors/ops?action=sync",
      "schedule": "0 */6 * * *"
    }
  ]
}

Manual capability refresh

curl -X POST https://api.payonproof.com/api/anchors/ops \
  -H "Content-Type: application/json" \
  -d '{"action": "refresh_capabilities", "country": "US", "limit": 50}' \
  "?secret=$CRON_SECRET"

Import custom anchor list

curl -X POST https://api.payonproof.com/api/anchors/ops \
  -H "Content-Type: application/json" \
  -d '{
    "action": "import_directory",
    "anchors": [
      {
        "domain": "custom.anchor.com",
        "name": "Custom Anchor",
        "country": "BR",
        "currency": "BRL"
      }
    ],
    "dryRun": false
  }' \
  "?secret=$CRON_SECRET"

Implementation notes

  • Auto-disables anchors after sep1DisableThreshold consecutive SEP-1 404 errors
  • Applies trust policies (requires SEP-10, signing key, SEP-24/SEP-31)
  • Supports auto-discovery from anchors.stellar.org via HTML scraping and __NEXT_DATA__ extraction
  • Horizon fallback discovers anchors from asset issuers on the Stellar network
  • Domain allowlists prevent importing untrusted anchors

Build docs developers (and LLMs) love