Workflow guide

Register contracts, publish actions, and execute them safely

Use the contracts workspace to register EVM ABIs or Solana Anchor IDLs, publish constrained actions, and let your backend query or execute only the approved surface.

Supported interaction surfaces

The operator-facing setup step and the runtime execution step are intentionally separated.

EVM contracts

Register a contract address and ABI, then expose write functions as published actions and read functions as backend queries.

Solana programs

Register a program ID and Anchor IDL, then expose instructions, decoded account queries, and unsigned simulations through the same workspace.

Controlled automation

Operators choose what becomes executable. Your backend only receives already-published actions instead of arbitrary calldata or instruction freedom.

Recommended operator and backend flow

This keeps approval, runtime control, and replay safety separated cleanly.

  1. Create a contract integration in the dashboard with either an EVM ABI or a Solana Anchor IDL.
  2. Review the discovered operations and publish only the actions your backend should be allowed to trigger.
  3. Create a contract execution API key scoped to those action IDs, then use it for runtime quotes and execution.
  4. Persist action IDs, execution IDs, your own userReference, and request IDs so retries and support flows stay traceable.
  5. Treat execute as asynchronous: quote when needed, execute once with an idempotency key, then poll execution history or fetch execution detail until terminal.

Operator vs backend boundary

Dashboard sessions create integrations, publish actions, and mint scoped contract execution keys. Backend API keys only do the runtime work later. That split is the main safety property of the contracts surface.

Solana v1 boundaries

Solana support is intentionally narrow in the first release so account resolution and signing stay predictable.

CapabilitySupported in v1Notes
Interface format`Anchor IDL only`Raw non-Anchor Solana program layouts are not supported in this release.
Signer model`Single business signer`PayChain supplies only the business treasury signer for executable Solana instructions.
Query model`Account read + instruction simulation`Queries include decoded account reads and unsigned instruction simulation.
Account mapping helpers`fixed`, `runtime`, `treasury`, `customer`, `PDA`, `ATA`The helper set is intentionally constrained so account resolution stays auditable.

What Solana v1 is not

This release does not support arbitrary multi-signer orchestration or broad custom seed scripting. If your workflow depends on those, treat it as a future expansion rather than an implicit guarantee.

Endpoints involved

GET/api/v1/businesses/:id/contract-integrationsx-api-keySandbox + live

List contract integrations

List saved EVM ABI and Solana Anchor IDL integrations for the business.

When to use it

Use when rendering the contracts workspace, validating discovered operations, or selecting an integration before querying or executing actions.

FieldLocationRequiredDescriptionExample / default
idpathYesBusiness ID.-

Success behavior

  • Each integration includes discovered write and query operations so your system does not need to re-parse the ABI or IDL at runtime.

Common errors

  • 401: Missing or invalid API key.

Sample response

json
{
  "data": [
    {
      "id": "int_123",
      "businessId": "biz_123",
      "name": "Counter program",
      "chain": "sol",
      "interfaceType": "anchor_idl",
      "networkId": "sol-devnet",
      "contractAddress": "11111111111111111111111111111111",
      "programId": "11111111111111111111111111111111",
      "abiHash": "idl_hash_123",
      "status": "active",
      "turnkeySmartContractInterfaceId": null,
      "turnkeyLastSyncedAt": null,
      "turnkeySyncError": null,
      "availableWriteOperations": [
        { "operationKind": "solana_instruction", "name": "increment", "signature": "increment", "selector": "0102030405060708", "stateMutability": "nonpayable", "inputs": [{ "name": "amount", "type": "u32" }], "outputs": [] }
      ],
      "availableQueryOperations": [
        { "operationKind": "solana_account_query", "name": "counterAccount", "signature": "account:counterAccount", "selector": "0807060504030201", "stateMutability": "view", "inputs": [{ "name": "address", "type": "pubkey" }], "outputs": [{ "name": "count", "type": "u32" }] }
      ]
    }
  ]
}

cURL example

bash
curl -X GET "https://api.paychainhq.io/api/v1/businesses/biz_123/contract-integrations" \
  -H "Content-Type: application/json" \
  -H "x-api-key: pk_live_your_business_key"

Node.js example

ts
const response = await fetch('https://api.paychainhq.io/api/v1/businesses/biz_123/contract-integrations', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'pk_live_your_business_key',
  },
});
const payload = await response.json();
console.log(payload);
POST/api/v1/businesses/:id/contract-integrationsDashboard UI onlySandbox + live

Create contract integration

Create an EVM contract or Solana program integration and persist discovered operations.

When to use it

Use in the operator setup flow when registering a contract address + ABI or program ID + Anchor IDL before publishing actions.

FieldLocationRequiredDescriptionExample / default
idpathYesBusiness ID.-
namebodyYesOperator-facing integration label.-
chainbodyYesUse `eth` for EVM or `sol` for Solana.-
networkIdbodyYesSupported target network ID.-
contractAddressbodyNoRequired when `chain=eth`.-
abiJsonbodyNoRequired when `chain=eth`; raw ABI JSON array or `{ abi: [...] }` object.-
programIdbodyNoRequired when `chain=sol`.-
idlJsonbodyNoRequired when `chain=sol`; Anchor IDL JSON.-
notesbodyNoOptional operator notes.-

Cautions

  • Solana support is Anchor IDL only in v1.
  • Creating an integration stores interface metadata but does not broadcast any on-chain transaction.
  • This action is dashboard-only and requires recent step-up authentication.

Common errors

  • 401: Missing or invalid API key.
  • 403: The caller is missing the required permission or auth mode for this action.
  • 400: Payload shape, query params, or business-state validation failed.

Sample request body

json
{
  "name": "Counter program",
  "chain": "sol",
  "networkId": "sol-devnet",
  "programId": "11111111111111111111111111111111",
  "idlJson": {
    "version": "0.1.0",
    "name": "counter",
    "instructions": [
      {
        "name": "increment",
        "accounts": [
          { "name": "authority", "isMut": false, "isSigner": true },
          { "name": "counter", "isMut": true, "isSigner": false },
          { "name": "systemProgram", "isMut": false, "isSigner": false }
        ],
        "args": [{ "name": "amount", "type": "u32" }]
      }
    ],
    "accounts": [
      {
        "name": "counterAccount",
        "type": {
          "kind": "struct",
          "fields": [
            { "name": "count", "type": "u32" },
            { "name": "authority", "type": "publicKey" }
          ]
        }
      }
    ]
  }
}

Sample response

json
{
  "integration": {
    "id": "int_123",
    "businessId": "biz_123",
    "name": "Counter program",
    "chain": "sol",
    "interfaceType": "anchor_idl",
    "networkId": "sol-devnet",
    "contractAddress": "11111111111111111111111111111111",
    "programId": "11111111111111111111111111111111",
    "abiHash": "idl_hash_123",
    "notes": null,
    "status": "active",
    "turnkeySmartContractInterfaceId": null,
    "turnkeyLastSyncedAt": null,
    "turnkeySyncError": null,
    "availableFunctions": [
      {
        "operationKind": "solana_instruction",
        "name": "increment",
        "signature": "increment",
        "selector": "0102030405060708",
        "stateMutability": "nonpayable",
        "inputs": [{ "name": "amount", "type": "u32" }],
        "outputs": [],
        "accounts": [
          { "name": "authority", "isWritable": false, "isSigner": true, "optional": false, "fixedAddress": null },
          { "name": "counter", "isWritable": true, "isSigner": false, "optional": false, "fixedAddress": null }
        ]
      }
    ],
    "availableReadFunctions": [
      {
        "operationKind": "solana_account_query",
        "name": "counterAccount",
        "signature": "account:counterAccount",
        "selector": "0807060504030201",
        "stateMutability": "view",
        "inputs": [{ "name": "address", "type": "pubkey" }],
        "outputs": [
          { "name": "count", "type": "u32" },
          { "name": "authority", "type": "pubkey" }
        ]
      }
    ],
    "availableWriteOperations": [],
    "availableQueryOperations": [],
    "createdAt": "2026-04-03T00:12:00.000Z",
    "updatedAt": "2026-04-03T00:12:00.000Z"
  }
}

cURL example

bash
curl -X POST "https://api.paychainhq.io/api/v1/businesses/biz_123/contract-integrations" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Counter program",
  "chain": "sol",
  "networkId": "sol-devnet",
  "programId": "11111111111111111111111111111111",
  "idlJson": {
    "version": "0.1.0",
    "name": "counter",
    "instructions": [
      {
        "name": "increment",
        "accounts": [
          { "name": "authority", "isMut": false, "isSigner": true },
          { "name": "counter", "isMut": true, "isSigner": false },
          { "name": "systemProgram", "isMut": false, "isSigner": false }
        ],
        "args": [{ "name": "amount", "type": "u32" }]
      }
    ],
    "accounts": [
      {
        "name": "counterAccount",
        "type": {
          "kind": "struct",
          "fields": [
            { "name": "count", "type": "u32" },
            { "name": "authority", "type": "publicKey" }
          ]
        }
      }
    ]
  }
}'

Node.js example

ts
const response = await fetch('https://api.paychainhq.io/api/v1/businesses/biz_123/contract-integrations', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
  "name": "Counter program",
  "chain": "sol",
  "networkId": "sol-devnet",
  "programId": "11111111111111111111111111111111",
  "idlJson": {
    "version": "0.1.0",
    "name": "counter",
    "instructions": [
      {
        "name": "increment",
        "accounts": [
          { "name": "authority", "isMut": false, "isSigner": true },
          { "name": "counter", "isMut": true, "isSigner": false },
          { "name": "systemProgram", "isMut": false, "isSigner": false }
        ],
        "args": [{ "name": "amount", "type": "u32" }]
      }
    ],
    "accounts": [
      {
        "name": "counterAccount",
        "type": {
          "kind": "struct",
          "fields": [
            { "name": "count", "type": "u32" },
            { "name": "authority", "type": "publicKey" }
          ]
        }
      }
    ]
  }
}),
});
const payload = await response.json();
console.log(payload);
GET/api/v1/businesses/:id/contract-actionsx-api-keySandbox + live

List published contract actions

List published actions that can later be quoted or executed.

When to use it

Use when selecting an action for server-side execution or when reviewing the currently active automation surface.

FieldLocationRequiredDescriptionExample / default
idpathYesBusiness ID.-

Common errors

  • 401: Missing or invalid API key.

Sample response

json
{
  "data": [
    {
      "id": "act_123",
      "businessId": "biz_123",
      "integrationId": "int_123",
      "name": "Increment counter",
      "description": "Raise the shared counter by a runtime amount.",
      "chain": "sol",
      "operationKind": "solana_instruction",
      "networkId": "sol-devnet",
      "contractAddress": "11111111111111111111111111111111",
      "functionName": "increment",
      "functionSignature": "increment",
      "functionSelector": "0102030405060708",
      "instructionName": "increment",
      "status": "active",
      "version": 1
    }
  ]
}

cURL example

bash
curl -X GET "https://api.paychainhq.io/api/v1/businesses/biz_123/contract-actions" \
  -H "Content-Type: application/json" \
  -H "x-api-key: pk_live_your_business_key"

Node.js example

ts
const response = await fetch('https://api.paychainhq.io/api/v1/businesses/biz_123/contract-actions', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'pk_live_your_business_key',
  },
});
const payload = await response.json();
console.log(payload);
POST/api/v1/businesses/:id/contract-integrations/:integrationId/actionsDashboard UI onlySandbox + live

Publish contract action

Publish a constrained action from a saved integration.

When to use it

Use after reviewing discovered methods or instructions and before handing controlled execution over to your backend.

FieldLocationRequiredDescriptionExample / default
idpathYesBusiness ID.-
integrationIdpathYesIntegration ID.-
namebodyYesAction label shown to operators and logs.-
functionSignaturebodyNoRequired for EVM integrations.-
instructionNamebodyNoRequired for Solana integrations.-
inputsbodyNoRuntime or fixed input mapping rules.-
accountMappingsbodyNoSolana-only account resolution rules.-
simulationEnabledbodyNoSolana-only preflight simulation toggle.-

Cautions

  • Solana v1 supports single-business-signer instructions only.
  • Publishing is dashboard-only; API keys execute already published actions instead of creating them.
  • Keep runtime controls narrow so backend automation cannot drift into arbitrary calldata or account resolution.

Common errors

  • 401: Missing or invalid API key.
  • 403: The caller is missing the required permission or auth mode for this action.
  • 400: Payload shape, query params, or business-state validation failed.
  • 404: Requested resource does not exist or is not owned by the business.

Sample request body

json
{
  "name": "Increment counter",
  "description": "Raise the shared counter by a runtime amount.",
  "instructionName": "increment",
  "inputs": [
    {
      "paramName": "amount",
      "mode": "runtime",
      "runtimeKey": "amount",
      "label": "Amount"
    }
  ],
  "accountMappings": [
    { "accountName": "authority", "mode": "treasury" },
    {
      "accountName": "counter",
      "mode": "runtime",
      "runtimeKey": "counterAddress",
      "label": "Counter account"
    },
    {
      "accountName": "systemProgram",
      "mode": "fixed",
      "fixedAddress": "11111111111111111111111111111111"
    }
  ],
  "simulationEnabled": true,
  "activate": true
}

Sample response

json
{
  "action": {
    "id": "act_123",
    "businessId": "biz_123",
    "integrationId": "int_123",
    "name": "Increment counter",
    "description": "Raise the shared counter by a runtime amount.",
    "chain": "sol",
    "operationKind": "solana_instruction",
    "networkId": "sol-devnet",
    "contractAddress": "11111111111111111111111111111111",
    "functionName": "increment",
    "functionSignature": "increment",
    "functionSelector": "0102030405060708",
    "instructionName": "increment",
    "status": "active",
    "version": 1,
    "createdAt": "2026-04-03T00:14:00.000Z",
    "updatedAt": "2026-04-03T00:14:00.000Z"
  }
}

cURL example

bash
curl -X POST "https://api.paychainhq.io/api/v1/businesses/biz_123/contract-integrations/int_123/actions" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Increment counter",
  "description": "Raise the shared counter by a runtime amount.",
  "instructionName": "increment",
  "inputs": [
    {
      "paramName": "amount",
      "mode": "runtime",
      "runtimeKey": "amount",
      "label": "Amount"
    }
  ],
  "accountMappings": [
    { "accountName": "authority", "mode": "treasury" },
    {
      "accountName": "counter",
      "mode": "runtime",
      "runtimeKey": "counterAddress",
      "label": "Counter account"
    },
    {
      "accountName": "systemProgram",
      "mode": "fixed",
      "fixedAddress": "11111111111111111111111111111111"
    }
  ],
  "simulationEnabled": true,
  "activate": true
}'

Node.js example

ts
const response = await fetch('https://api.paychainhq.io/api/v1/businesses/biz_123/contract-integrations/int_123/actions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
  "name": "Increment counter",
  "description": "Raise the shared counter by a runtime amount.",
  "instructionName": "increment",
  "inputs": [
    {
      "paramName": "amount",
      "mode": "runtime",
      "runtimeKey": "amount",
      "label": "Amount"
    }
  ],
  "accountMappings": [
    { "accountName": "authority", "mode": "treasury" },
    {
      "accountName": "counter",
      "mode": "runtime",
      "runtimeKey": "counterAddress",
      "label": "Counter account"
    },
    {
      "accountName": "systemProgram",
      "mode": "fixed",
      "fixedAddress": "11111111111111111111111111111111"
    }
  ],
  "simulationEnabled": true,
  "activate": true
}),
});
const payload = await response.json();
console.log(payload);
POST/api/v1/businesses/:id/contract-api-keysDashboard UI onlySandbox + live

Create contract execution API key

Create a dedicated API key that can quote and execute only selected published contract actions.

When to use it

Use when a backend needs real-time buy/sell, approve, or other constrained contract execution without granting broad standard-key signing access.

FieldLocationRequiredDescriptionExample / default
idpathYesBusiness ID.-
namebodyYesOperator label for this contract execution key.-
actionIdsbodyYesPublished active contract action IDs this key may quote and execute.-
expiresInbodyNoOptional key lifetime in days, max 365.-

Cautions

  • The key stores a policy snapshot of action IDs, networks, contract addresses, and function signatures.
  • Standard API keys still cannot execute contract actions.
  • Rotate this key independently from standard and payout keys.

Common errors

  • 401: Missing or invalid API key.
  • 403: The caller is missing the required permission or auth mode for this action.
  • 400: Payload shape, query params, or business-state validation failed.
  • 403 CONTRACT_API_KEY_POLICY_REJECTED: A contract execution API key attempted an action outside its allowed action, network, contract, or function policy.

Sample request body

json
{
  "name": "TSTY buy/sell executor",
  "expiresIn": 30,
  "actionIds": [
    "7a8a6b0d-5a18-4c71-9478-18c6a54fe9cb",
    "11e71b3c-88bb-49b2-a1c9-ffce42d62df1"
  ]
}

Sample response

json
{
  "id": "key_contract_123",
  "name": "TSTY buy/sell executor",
  "kind": "contract_execution",
  "apiKey": "biz_live_contract_1234567890abcdef",
  "permissions": ["contracts:sign", "contracts:read"],
  "expiresAt": "2026-04-11T15:35:00.000Z",
  "createdAt": "2026-03-12T15:35:00.000Z",
  "policy": {
    "allowedActionIds": [
      "7a8a6b0d-5a18-4c71-9478-18c6a54fe9cb",
      "11e71b3c-88bb-49b2-a1c9-ffce42d62df1"
    ],
    "allowedNetworkIds": ["bnb-mainnet"],
    "allowedContractAddresses": ["0x00000000000000000000000000000000000000aa"],
    "allowedFunctionSignatures": ["buyTokens(uint256)", "sellTokens(uint256)"]
  },
  "warning": "Store this contract execution API key securely. It can execute only the configured published contract actions and cannot be recovered."
}

cURL example

bash
curl -X POST "https://api.paychainhq.io/api/v1/businesses/biz_123/contract-api-keys" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "TSTY buy/sell executor",
  "expiresIn": 30,
  "actionIds": [
    "7a8a6b0d-5a18-4c71-9478-18c6a54fe9cb",
    "11e71b3c-88bb-49b2-a1c9-ffce42d62df1"
  ]
}'

Node.js example

ts
const response = await fetch('https://api.paychainhq.io/api/v1/businesses/biz_123/contract-api-keys', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
  "name": "TSTY buy/sell executor",
  "expiresIn": 30,
  "actionIds": [
    "7a8a6b0d-5a18-4c71-9478-18c6a54fe9cb",
    "11e71b3c-88bb-49b2-a1c9-ffce42d62df1"
  ]
}),
});
const payload = await response.json();
console.log(payload);
POST/api/v1/contract-integrations/:integrationId/queryx-api-keySandbox + live

Run contract query

Run a read-only query against a saved integration.

When to use it

Use from your backend for EVM view or pure reads, Solana decoded account reads, or unsigned Solana instruction simulations.

FieldLocationRequiredDescriptionExample / default
integrationIdpathYesIntegration ID.-
functionSignaturebodyNoRequired for EVM queries.-
argsbodyNoOptional argument map for EVM or Solana simulation queries.-
operationKindbodyNoUse `solana_account_query` or `solana_instruction_simulation` for Solana.-
accountNamebodyNoRequired for Solana account queries.-
addressbodyNoRequired for Solana account queries.-
instructionNamebodyNoRequired for Solana instruction simulation.-
accountsbodyNoOptional Solana instruction simulation account map.-

Cautions

  • Use the saved integration surface instead of raw arbitrary calldata so runtime behavior stays constrained.
  • Solana instruction simulation is unsigned and read-only; it does not publish a transaction.

Common errors

  • 401: Missing or invalid API key.
  • 403: The caller is missing the required permission or auth mode for this action.
  • 400: Payload shape, query params, or business-state validation failed.
  • 404: Requested resource does not exist or is not owned by the business.

Sample request body

json
{
  "operationKind": "solana_account_query",
  "accountName": "counterAccount",
  "address": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
}

Sample response

json
{
  "integration": {
    "id": "int_123",
    "businessId": "biz_123",
    "name": "Counter program",
    "chain": "sol",
    "networkId": "sol-devnet",
    "contractAddress": "11111111111111111111111111111111",
    "status": "active"
  },
  "view": {
    "operationKind": "solana_account_query",
    "name": "counterAccount",
    "signature": "account:counterAccount",
    "selector": "0807060504030201",
    "stateMutability": "view",
    "inputs": [{ "name": "address", "type": "pubkey" }],
    "outputs": [
      { "name": "count", "type": "u32" },
      { "name": "authority", "type": "pubkey" }
    ]
  },
  "request": {
    "queryType": "solana_account_query",
    "args": [],
    "accounts": [
      { "name": "address", "value": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp" }
    ]
  },
  "result": {
    "rawReturnData": "base64:AAkAAAAAAAAA",
    "outputs": [
      { "name": "count", "type": "u32", "value": 9 },
      {
        "name": "authority",
        "type": "pubkey",
        "value": "So11111111111111111111111111111111111111112"
      }
    ],
    "named": {
      "count": 9,
      "authority": "So11111111111111111111111111111111111111112"
    }
  }
}

cURL example

bash
curl -X POST "https://api.paychainhq.io/api/v1/contract-integrations/int_123/query" \
  -H "Content-Type: application/json" \
  -H "x-api-key: pk_live_your_business_key" \
  -d '{
  "operationKind": "solana_account_query",
  "accountName": "counterAccount",
  "address": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
}'

Node.js example

ts
const response = await fetch('https://api.paychainhq.io/api/v1/contract-integrations/int_123/query', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'pk_live_your_business_key',
  },
  body: JSON.stringify({
  "operationKind": "solana_account_query",
  "accountName": "counterAccount",
  "address": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
}),
});
const payload = await response.json();
console.log(payload);
GET/api/v1/businesses/:id/contract-executionsx-api-keySandbox + live

List contract executions

List execution attempts for published actions.

When to use it

Use for execution history tables, polling fallbacks, or backend reconciliation after you queue contract actions.

FieldLocationRequiredDescriptionExample / default
idpathYesBusiness ID.-
statusqueryNoOptional execution status filter.-
actionIdqueryNoOptional published action filter.-
pagequeryNoPage number.1
limitqueryNoPage size.20

Common errors

  • 401: Missing or invalid API key.

Sample response

json
{
  "data": [
    {
      "id": "exe_123",
      "businessId": "biz_123",
      "actionId": "act_123",
      "integrationId": "int_123",
      "idempotencyKey": "contract-exec-001",
      "userReference": "counter-42",
      "txHash": "solana-signature-123",
      "status": "broadcasted",
      "queuedAt": "2026-04-03T00:16:00.000Z",
      "createdAt": "2026-04-03T00:16:00.000Z",
      "updatedAt": "2026-04-03T00:16:03.000Z",
      "action": {
        "id": "act_123",
        "name": "Increment counter",
        "functionSignature": "increment",
        "contractAddress": "11111111111111111111111111111111",
        "networkId": "sol-devnet",
        "chain": "sol",
        "operationKind": "solana_instruction",
        "instructionName": "increment"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 1,
    "totalPages": 1,
    "hasMore": false
  }
}

cURL example

bash
curl -X GET "https://api.paychainhq.io/api/v1/businesses/biz_123/contract-executions?page=1&limit=20" \
  -H "Content-Type: application/json" \
  -H "x-api-key: pk_live_your_business_key"

Node.js example

ts
const response = await fetch('https://api.paychainhq.io/api/v1/businesses/biz_123/contract-executions?page=1&limit=20', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'pk_live_your_business_key',
  },
});
const payload = await response.json();
console.log(payload);
POST/api/v1/contract-actions/:actionId/quotex-api-keySandbox + live

Quote published contract action

Prepare and simulate a published action without broadcasting it.

When to use it

Use before execute when you want resolved arguments, recipient or account expansion, and chain-specific preview metadata.

FieldLocationRequiredDescriptionExample / default
actionIdpathYesPublished action ID.-
userReferencebodyNoYour traceable business reference.-
recipientAddressbodyNoRecipient address when the action requires one.-
runtimeArgsbodyNoRuntime arguments keyed by the published action schema.-
nativeValuebodyNoOptional EVM native value in wei-like raw units.-
idempotencyKeybodyNoOptional replay-safe execution key.-

Success behavior

  • The quote response returns the normalized action plus a chain-specific preview payload, including Solana simulation logs when available.

Common errors

  • 401: Missing or invalid API key.
  • 403: The caller is missing the required permission or auth mode for this action.
  • 403 CONTRACT_API_KEY_POLICY_REJECTED: A contract execution API key attempted an action outside its allowed action, network, contract, or function policy.
  • 400: Payload shape, query params, or business-state validation failed.
  • 404: Requested resource does not exist or is not owned by the business.

Sample request body

json
{
  "userReference": "counter-42",
  "runtimeArgs": {
    "amount": 7,
    "counterAddress": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
  },
  "idempotencyKey": "contract-exec-001"
}

Sample response

json
{
  "action": {
    "id": "act_123",
    "name": "Increment counter",
    "chain": "sol",
    "operationKind": "solana_instruction",
    "networkId": "sol-devnet",
    "contractAddress": "11111111111111111111111111111111",
    "functionSignature": "increment",
    "instructionName": "increment",
    "status": "active"
  },
  "quote": {
    "actionId": "act_123",
    "chain": "sol",
    "networkId": "sol-devnet",
    "programId": "11111111111111111111111111111111",
    "instructionName": "increment",
    "resolvedArgs": [
      { "name": "amount", "type": "u32", "value": 7, "source": "runtime:amount" }
    ],
    "resolvedAccounts": [
      {
        "name": "authority",
        "value": "So11111111111111111111111111111111111111112",
        "source": "treasury"
      },
      {
        "name": "counter",
        "value": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp",
        "source": "runtime:counterAddress"
      }
    ],
    "simulation": {
      "err": null,
      "unitsConsumed": 1234
    },
    "simulationLogs": ["Program log: increment"]
  }
}

cURL example

bash
curl -X POST "https://api.paychainhq.io/api/v1/contract-actions/act_123/quote" \
  -H "Content-Type: application/json" \
  -H "x-api-key: pk_live_your_business_key" \
  -d '{
  "userReference": "counter-42",
  "runtimeArgs": {
    "amount": 7,
    "counterAddress": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
  },
  "idempotencyKey": "contract-exec-001"
}'

Node.js example

ts
const response = await fetch('https://api.paychainhq.io/api/v1/contract-actions/act_123/quote', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'pk_live_your_business_key',
  },
  body: JSON.stringify({
  "userReference": "counter-42",
  "runtimeArgs": {
    "amount": 7,
    "counterAddress": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
  },
  "idempotencyKey": "contract-exec-001"
}),
});
const payload = await response.json();
console.log(payload);
POST/api/v1/contract-actions/:actionId/executex-api-keySandbox + live

Execute published contract action

Sign and execute a published contract action asynchronously.

When to use it

Use from your backend once runtime values are ready and your application has accepted the side effect.

Idempotency: recommended

FieldLocationRequiredDescriptionExample / default
actionIdpathYesPublished action ID.-
userReferencebodyNoYour traceable business reference.-
recipientAddressbodyNoRecipient address when the action requires one.-
runtimeArgsbodyNoRuntime arguments keyed by the published action schema.-
nativeValuebodyNoOptional EVM native value in wei-like raw units.-
idempotencyKeybodyNoStrongly recommended replay-safe execution key.-

Cautions

  • Execution is asynchronous; persist the returned execution ID and treat the initial `202` response as accepted, not confirmed.
  • Use your own idempotencyKey whenever retries could otherwise duplicate a side effect.
  • Standard API keys return `HIGH_RISK_API_KEY_SCOPE_DISABLED`; use a dedicated contract execution API key scoped to the action IDs.

Common errors

  • 401: Missing or invalid API key.
  • 403: The caller is missing the required permission or auth mode for this action.
  • 403 HIGH_RISK_API_KEY_SCOPE_DISABLED: A standard API key attempted a protected operation that now requires dashboard session auth or a dedicated payout or contract execution API key.
  • 403 CONTRACT_API_KEY_POLICY_REJECTED: A contract execution API key attempted an action outside its allowed action, network, contract, or function policy.
  • 400: Payload shape, query params, or business-state validation failed.
  • 404: Requested resource does not exist or is not owned by the business.

Sample request body

json
{
  "userReference": "counter-42",
  "runtimeArgs": {
    "amount": 7,
    "counterAddress": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
  },
  "idempotencyKey": "contract-exec-001"
}

Sample response

json
{
  "execution": {
    "id": "exe_123",
    "businessId": "biz_123",
    "actionId": "act_123",
    "integrationId": "int_123",
    "apiKeyId": "key_123",
    "requestedByUserId": null,
    "idempotencyKey": "contract-exec-001",
    "userReference": "counter-42",
    "recipientAddress": null,
    "runtimeArgs": {
      "amount": 7,
      "counterAddress": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
    },
    "resolvedArgs": [
      { "name": "amount", "type": "u32", "value": 7, "source": "runtime:amount" }
    ],
    "resolvedAccounts": [
      {
        "name": "authority",
        "value": "So11111111111111111111111111111111111111112",
        "source": "treasury"
      },
      {
        "name": "counter",
        "value": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp",
        "source": "runtime:counterAddress"
      }
    ],
    "preview": {
      "instructionName": "increment"
    },
    "simulationResult": {
      "err": null,
      "unitsConsumed": 1234
    },
    "simulationLogs": ["Program log: increment"],
    "transactionEncoding": "solana_legacy",
    "unsignedTransaction": "AQABAgM=",
    "signedTransaction": null,
    "txHash": "solana-signature-123",
    "status": "broadcasted",
    "errorCode": null,
    "errorMessage": null,
    "failureDetails": null,
    "queuedAt": "2026-04-03T00:16:00.000Z",
    "simulatedAt": "2026-04-03T00:16:01.000Z",
    "signedAt": "2026-04-03T00:16:02.000Z",
    "broadcastedAt": "2026-04-03T00:16:03.000Z",
    "confirmedAt": null,
    "createdAt": "2026-04-03T00:16:00.000Z",
    "updatedAt": "2026-04-03T00:16:03.000Z",
    "action": {
      "id": "act_123",
      "name": "Increment counter",
      "functionSignature": "increment",
      "contractAddress": "11111111111111111111111111111111",
      "networkId": "sol-devnet",
      "chain": "sol",
      "operationKind": "solana_instruction",
      "instructionName": "increment"
    }
  },
  "replayed": false
}

cURL example

bash
curl -X POST "https://api.paychainhq.io/api/v1/contract-actions/act_123/execute" \
  -H "Content-Type: application/json" \
  -H "x-api-key: pk_live_your_business_key" \
  -H "Idempotency-Key: example-request-001" \
  -d '{
  "userReference": "counter-42",
  "runtimeArgs": {
    "amount": 7,
    "counterAddress": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
  },
  "idempotencyKey": "contract-exec-001"
}'

Node.js example

ts
const response = await fetch('https://api.paychainhq.io/api/v1/contract-actions/act_123/execute', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'pk_live_your_business_key',
    'Idempotency-Key': 'example-request-001',
  },
  body: JSON.stringify({
  "userReference": "counter-42",
  "runtimeArgs": {
    "amount": 7,
    "counterAddress": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
  },
  "idempotencyKey": "contract-exec-001"
}),
});
const payload = await response.json();
console.log(payload);
GET/api/v1/contract-executions/:idx-api-keySandbox + live

Get contract execution detail

Return one execution record with resolved args, preview, simulation, and final transaction metadata.

When to use it

Use after quote or execute to poll non-terminal actions or fetch logs for support, analytics, and audit trails.

FieldLocationRequiredDescriptionExample / default
idpathYesExecution ID.-

Common errors

  • 401: Missing or invalid API key.
  • 403: The caller is missing the required permission or auth mode for this action.
  • 404: Requested resource does not exist or is not owned by the business.

Sample response

json
{
  "execution": {
    "id": "exe_123",
    "businessId": "biz_123",
    "actionId": "act_123",
    "integrationId": "int_123",
    "apiKeyId": "key_123",
    "requestedByUserId": null,
    "idempotencyKey": "contract-exec-001",
    "userReference": "counter-42",
    "recipientAddress": null,
    "runtimeArgs": {
      "amount": 7,
      "counterAddress": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp"
    },
    "resolvedArgs": [
      { "name": "amount", "type": "u32", "value": 7, "source": "runtime:amount" }
    ],
    "resolvedAccounts": [
      {
        "name": "authority",
        "value": "So11111111111111111111111111111111111111112",
        "source": "treasury"
      },
      {
        "name": "counter",
        "value": "8gRLe6GiQx2jo9mQFcbwy34u2W4zD2rN5q4mmD8iP2Mp",
        "source": "runtime:counterAddress"
      }
    ],
    "preview": {
      "instructionName": "increment"
    },
    "simulationResult": {
      "err": null,
      "unitsConsumed": 1234
    },
    "simulationLogs": ["Program log: increment"],
    "transactionEncoding": "solana_legacy",
    "unsignedTransaction": "AQABAgM=",
    "signedTransaction": null,
    "txHash": "solana-signature-123",
    "status": "broadcasted",
    "errorCode": null,
    "errorMessage": null,
    "failureDetails": null,
    "queuedAt": "2026-04-03T00:16:00.000Z",
    "simulatedAt": "2026-04-03T00:16:01.000Z",
    "signedAt": "2026-04-03T00:16:02.000Z",
    "broadcastedAt": "2026-04-03T00:16:03.000Z",
    "confirmedAt": null,
    "createdAt": "2026-04-03T00:16:00.000Z",
    "updatedAt": "2026-04-03T00:16:03.000Z",
    "action": {
      "id": "act_123",
      "name": "Increment counter",
      "functionSignature": "increment",
      "contractAddress": "11111111111111111111111111111111",
      "networkId": "sol-devnet",
      "chain": "sol",
      "operationKind": "solana_instruction",
      "instructionName": "increment"
    }
  },
  "replayed": false
}

cURL example

bash
curl -X GET "https://api.paychainhq.io/api/v1/contract-executions/biz_123" \
  -H "Content-Type: application/json" \
  -H "x-api-key: pk_live_your_business_key"

Node.js example

ts
const response = await fetch('https://api.paychainhq.io/api/v1/contract-executions/biz_123', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'pk_live_your_business_key',
  },
});
const payload = await response.json();
console.log(payload);
Register contracts, publish actions, and execute them safely | PayChainHQ