API Reference
Relayer API

Relayer API Reference

The Veridex Relayer provides a REST API for gasless transactions and cross-chain messaging.

Base URL

EnvironmentURL
Testnethttps://relayer.veridex.network
Localhttp://localhost:3001

Authentication

Most endpoints are public. Rate limiting is applied per IP address.


Health & Info

GET /health

Health check endpoint.

Response:

{
  "status": "healthy",
  "version": "1.0.0",
  "uptime": 3600,
  "chains": {
    "base-sepolia": "connected",
    "optimism-sepolia": "connected",
    "arbitrum-sepolia": "connected",
    "solana-devnet": "connected"
  }
}

Status Codes:

CodeDescription
200Healthy or degraded
503Unhealthy

GET /api/v1/info

Get relayer configuration.

Response:

{
  "relayer": {
    "address": "0x...",
    "version": "1.0.0"
  },
  "hub": {
    "chainId": 84532,
    "wormholeChainId": 10004,
    "contracts": {
      "hub": "0x..."
    }
  },
  "supportedChains": [
    {
      "name": "optimism-sepolia",
      "chainId": 11155420,
      "wormholeChainId": 10005,
      "type": "evm"
    }
  ],
  "capabilities": {
    "gaslessSubmit": true,
    "vaaRelay": true,
    "balanceQueries": true
  }
}

Transactions

POST /api/v1/submit

Submit a gasless transaction.

Request Body:

{
  "action": "transfer",
  "targetChain": 10005,
  "payload": {
    "token": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "recipient": "0x742d35Cc6634C0532925a3b844Bc9e7595f5A234",
    "amount": "1000000"
  },
  "signature": {
    "r": "0x...",
    "s": "0x...",
    "authenticatorData": "0x...",
    "clientDataJSON": "..."
  },
  "credentialId": "base64-encoded-id",
  "vaultAddress": "0x..."
}

Parameters:

FieldTypeDescription
actionstringAction type (transfer, bridge, createVault)
targetChainnumberWormhole chain ID
payloadobjectAction-specific payload
signatureobjectWebAuthn signature
credentialIdstringBase64 credential ID
vaultAddressstringUser's vault address

Response:

{
  "success": true,
  "txHash": "0x...",
  "targetChain": 10005,
  "estimatedConfirmation": 60
}

Error Response:

{
  "error": "INVALID_SIGNATURE",
  "message": "WebAuthn signature verification failed",
  "details": {
    "expected": "0x...",
    "received": "0x..."
  }
}

POST /api/v1/submit/query

Submit via Wormhole Query path (faster).

Request Body: Same as /api/v1/submit

Response:

{
  "success": true,
  "txHash": "0x...",
  "path": "query",
  "confirmationTime": 5200
}

GET /api/v1/status/:txHash

Get transaction status.

Parameters:

ParameterTypeDescription
txHashstringTransaction hash

Response:

{
  "txHash": "0x...",
  "status": "confirmed",
  "blockNumber": 12345678,
  "timestamp": 1704067200,
  "action": "transfer",
  "targetChain": 10005,
  "destinationTxHash": "0x..."
}

Status Values:

StatusDescription
pendingSubmitted, awaiting confirmation
attestedVAA attested by guardians
confirmedConfirmed on destination chain
failedTransaction failed

Fees

GET /api/v1/fee

Get fee quote.

Query Parameters:

ParameterTypeDescription
targetChainnumberWormhole chain ID (optional)
actionstringAction type (optional)

Response:

{
  "fees": {
    "wormhole": "100000000000000",
    "relayer": "500000000000000",
    "total": "600000000000000"
  },
  "targetChain": 10005,
  "estimatedTimeSeconds": 60,
  "currency": "ETH"
}

Balances

GET /api/v1/balances/:address

Get multi-chain balances for an address.

Parameters:

ParameterTypeDescription
addressstringVault address

Query Parameters:

ParameterTypeDescription
chainsstringComma-separated chain names (optional)

Response:

{
  "address": "0x...",
  "balances": {
    "base-sepolia": {
      "native": "100000000000000000",
      "tokens": {
        "0x036CbD53842c5426634e7929541eC2318f3dCF7e": {
          "symbol": "USDC",
          "decimals": 6,
          "balance": "100000000"
        }
      }
    },
    "optimism-sepolia": {
      "native": "50000000000000000",
      "tokens": {}
    }
  },
  "timestamp": 1704067200
}

Vaults

POST /api/v1/vault/create

Create a vault on a spoke chain.

Request Body:

{
  "targetChain": 10005,
  "credentialId": "base64-encoded-id",
  "publicKey": {
    "x": "0x...",
    "y": "0x..."
  }
}

Response:

{
  "success": true,
  "vaultAddress": "0x...",
  "txHash": "0x...",
  "targetChain": 10005
}

Stacks Endpoints

The relayer provides Stacks-specific endpoints for direct relay (not Wormhole VAAs).

POST /api/v1/stacks/submit

Submit a sponsored Stacks transaction.

curl -X POST https://relayer.veridex.network/api/v1/stacks/submit \
  -H "Content-Type: application/json" \
  -d '{
    "transaction": "<serialized-stacks-tx-hex>",
    "keyHash": "0x..."
  }'

Response:

{
  "success": true,
  "txId": "0x7f9e5c51...",
  "status": "pending"
}

GET /api/v1/stacks/identity/:keyHash

Query identity registration status on the Stacks spoke contract.

curl https://relayer.veridex.network/api/v1/stacks/identity/0x1234...

GET /api/v1/stacks/session/:sessionHash

Query session key status.

curl https://relayer.veridex.network/api/v1/stacks/session/0xabcd...

GET /api/v1/stacks/vault/:keyHash

Query vault balance and status.

curl https://relayer.veridex.network/api/v1/stacks/vault/0x1234...

GET /api/v1/stacks/tx/:txId

Get Stacks transaction status.

curl https://relayer.veridex.network/api/v1/stacks/tx/0x7f9e5c51...

Stacks Sponsorship Limits

LimitValue
Per-identity rate50 tx/hour
Per-tx fee cap0.5 STX
Hourly budget50 STX

Cross-Domain Passkey Sharing

Endpoints for third-party app registration, origin validation, and server-side session tokens. See the Cross-Domain Passkeys Guide for full integration instructions.

POST /api/v1/apps/register

Register a new third-party application for cross-domain passkey sharing.

Request Body:

{
  "name": "My DeFi App",
  "origin": "https://myapp.com",
  "description": "A decentralized exchange",
  "contactEmail": "dev@myapp.com"
}
FieldTypeRequiredDescription
namestringYesApplication name (min 2 characters)
originstringYesHTTPS origin (e.g. https://myapp.com)
descriptionstringNoShort description
contactEmailstringNoDeveloper contact email

Response (201):

{
  "success": true,
  "app": {
    "id": "app_a1b2c3d4e5f6",
    "name": "My DeFi App",
    "origin": "https://myapp.com",
    "apiKey": "vdx_sk_...",
    "trustLevel": "registered",
    "status": "active",
    "createdAt": 1738800000000
  }
}

Error Responses:

CodeDescription
400Invalid name, origin, or non-HTTPS origin
409Origin already registered

GET /api/v1/apps

List registered apps. Public access returns origins only. Admin access (with X-API-Key header matching the metrics API key) returns full details.

Public Response:

{
  "origins": ["https://myapp.com", "https://partner.io"],
  "count": 2
}

GET /api/v1/apps/:appId

Get details of a registered app. Requires the app's API key or admin key via X-API-Key header.

Response:

{
  "app": {
    "id": "app_a1b2c3d4e5f6",
    "name": "My DeFi App",
    "origin": "https://myapp.com",
    "trustLevel": "registered",
    "status": "active",
    "requestCount": 42,
    "createdAt": 1738800000000,
    "apiKey": "vdx_sk_..."
  }
}

DELETE /api/v1/apps/:appId

Delete a registered app and revoke all its active sessions. Requires the app's API key or admin key via X-API-Key header.

Response:

{
  "success": true,
  "revokedSessions": 3
}

GET /api/v1/apps/info

Get public app info by origin. Used by the Auth Portal to display app details during authentication.

Query Parameters:

ParameterTypeDescription
originstringThe origin to look up (e.g. https://myapp.com)

Response:

{
  "name": "My DeFi App",
  "origin": "https://myapp.com",
  "description": "A decentralized exchange",
  "trustLevel": "registered",
  "registered": true
}

GET /api/v1/origins/validate

Check if an origin is allowed for cross-domain passkey sharing.

Query Parameters:

ParameterTypeDescription
originstringThe origin to validate

Response:

{
  "allowed": true,
  "reason": "registered",
  "origin": "https://myapp.com"
}

Reason values: builtin, development, registered, not_registered, invalid_origin


GET /api/v1/origins/well-known

Generate the .well-known/webauthn JSON dynamically from all built-in and registered origins.

Response:

{
  "origins": [
    "https://veridex.network",
    "https://auth.veridex.network",
    "https://sera.veridex.network",
    "https://myapp.com"
  ]
}

POST /api/v1/session/create

Create a server-validated cross-origin session token.

Request Body:

{
  "keyHash": "0x1234...",
  "appOrigin": "https://myapp.com",
  "sessionPublicKey": "0xabcd...",
  "permissions": ["read", "transfer"],
  "expiresInMs": 3600000,
  "signature": { "r": "0x...", "s": "0x..." }
}
FieldTypeRequiredDescription
keyHashstringYesUser's passkey key hash (0x-prefixed)
appOriginstringYesRequesting app's origin
sessionPublicKeystringYesSession key public key
permissionsstring[]NoGranted permissions (default: ["read", "transfer"])
expiresInMsnumberNoSession duration in ms (default: 3600000 = 1 hour)
signatureobjectYesWebAuthn signature proof

Response (201):

{
  "success": true,
  "session": {
    "id": "ses_abc123",
    "keyHash": "0x1234...",
    "appOrigin": "https://myapp.com",
    "permissions": ["read", "transfer"],
    "expiresAt": 1738903600000,
    "createdAt": 1738900000000
  }
}

Error Responses:

CodeDescription
400Missing or invalid fields
403Origin not registered
404Credential not found (user must register a passkey first)

GET /api/v1/session/:sessionId

Validate a cross-origin session token.

Response (valid):

{
  "valid": true,
  "session": {
    "id": "ses_abc123",
    "keyHash": "0x1234...",
    "appOrigin": "https://myapp.com",
    "permissions": ["read", "transfer"],
    "expiresAt": 1738903600000,
    "createdAt": 1738900000000
  }
}

Response (invalid/expired):

{
  "valid": false,
  "error": "Session not found or expired"
}

DELETE /api/v1/session/:sessionId

Revoke a cross-origin session token.

Response:

{
  "success": true,
  "revoked": true
}

Error Codes

CodeHTTP StatusDescription
INVALID_SIGNATURE400WebAuthn signature verification failed
INSUFFICIENT_BALANCE400Vault doesn't have enough tokens
UNSUPPORTED_CHAIN400Target chain not configured
INVALID_PAYLOAD400Malformed request payload
NONCE_MISMATCH400Transaction nonce mismatch
ORIGIN_NOT_REGISTERED403Origin not registered for cross-domain auth
RATE_LIMITED429Too many requests
RELAYER_ERROR500Internal relayer error
CHAIN_UNAVAILABLE503Target chain not responding
VAA_NOT_FOUND404Wormhole VAA not yet available
STACKS_SPONSORSHIP_LIMIT429Stacks sponsorship rate limit exceeded
STACKS_POST_CONDITION_FAIL400Stacks post-condition validation failed

Rate Limits

EndpointLimit
All endpoints100 requests/minute per IP
/api/v1/submit10 requests/minute per vault
/api/v1/balances30 requests/minute per IP

WebSocket (Coming Soon)

Real-time transaction status updates:

const ws = new WebSocket('wss://relayer.veridex.network/ws');
 
ws.send(JSON.stringify({
  type: 'subscribe',
  txHash: '0x...'
}));
 
ws.onmessage = (event) => {
  const { type, data } = JSON.parse(event.data);
  if (type === 'status') {
    console.log('Status update:', data);
  }
};

Self-Hosting

See the Relayer README for self-hosting instructions.

# Clone and setup
git clone https://github.com/Veridex-Protocol/demo
cd demo/packages/relayer
cp .env.example .env
 
# Configure .env with your keys
# Start relayer
npm run dev