Skip to content

Relay an EVM transaction

This guide walks through submitting a gas-sponsored EVM transaction, choosing between async and sync submission, and checking its status.

Prerequisites

  • An API key — Get API access
  • The target contract address and ABI-encoded call data

1. Build the request body

POST https://photon.example.com/api/submit-tx accepts:

Field Type Required Notes
to string yes Recipient or contract address.
callData string yes Hex-encoded call data; "0x" for a plain transfer.
chainId number yes Target chain. See Supported networks.
value string no Native value in wei, as a base-10 integer string. Default "0".
gasPrice string no Legacy gas price in wei.
maxFeePerGas string no EIP-1559 max fee in wei.
maxPriorityFeePerGas string no EIP-1559 priority fee in wei.
gasLimit string no Gas limit override.
maxUSD number no Cost ceiling in USD, enforced when the transaction is picked up for relaying.
label string no Free-form tag echoed back in status lookups.
retries number no Retry budget for transient relay failures. Default 0.

Gas override rules — violations return error code TRANSACTION_005:

  • Omit all gas fields to let Photon price the transaction.
  • Legacy mode: gasPrice alone. EIP-1559 mode: maxFeePerGas and maxPriorityFeePerGas, always together.
  • gasPrice cannot be combined with either EIP-1559 field.
  • Every gas field (including gasLimit) must be a base-10 integer string.

2. Submit asynchronously

curl -X POST https://photon.example.com/api/submit-tx \
  -H "api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "0x3dbE34f2C21b3B2980d4dc53f3c7E51e39663F49",
    "callData": "0x",
    "chainId": 84532,
    "value": "1000000000000000",
    "maxUSD": 0.5,
    "label": "payout-42"
  }'
const res = await fetch("https://photon.example.com/api/submit-tx", {
  method: "POST",
  headers: { "api-key": "YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    to: "0x3dbE34f2C21b3B2980d4dc53f3c7E51e39663F49",
    callData: "0x",
    chainId: 84532,
    value: "1000000000000000",
    maxUSD: 0.5,
    label: "payout-42",
  }),
});
const { data } = await res.json();
console.log(data.txId);

A 201 response returns the transaction id:

{
  "success": true,
  "data": {
    "txId": "68fa3450539a3c9d28bbca33",
    "estimatedCostUSD": 0.0021
  },
  "timestamp": "2026-07-29T10:15:00.000Z"
}

3. Or submit synchronously

POST https://photon.example.com/api/submit-tx-sync wraps the same body in tx and waits for the result:

{
  "tx": { "to": "0x3dbE34f2C21b3B2980d4dc53f3c7E51e39663F49", "callData": "0x", "chainId": 84532, "value": "1000000000000000" },
  "timeoutMs": 30000,
  "includeReceipt": true,
  "includeRevertInfo": true,
  "returnOnTxHash": false
}
  • timeoutMs must be between 1000 and 120000 (default 30000); values outside the range return VALIDATION_004.
  • returnOnTxHash: true returns as soon as a transaction hash exists instead of waiting for a terminal status.
  • A 200 response carries the full transaction record. If the timeout elapses first, you get a 202 and the record includes "timedOut": true — the transaction keeps processing, so poll its status.

4. Check status

GET https://photon.example.com/api/tx?id=<txId> returns the record inside the standard envelope. status is one of NOT_PICKED_UP, PENDING, EXECUTED, FAILED, or NEEDS_TO_BE_RETRIED.

curl "https://photon.example.com/api/tx?id=68fa3450539a3c9d28bbca33&includeReceipt=true&includeRevertInfo=true"

Warning

includeReceipt and includeRevertInfo are query-string flags: only the strings true or 1 enable them. They add receipt and revert objects to the record when available.

Unknown transaction ids return a 404 error envelope with code TRANSACTION_003.