Machine View

Connecting Agents via API

Source: https://conto.finance/docs/quickstart/connecting-agents

# Connecting Agents via API

> Integrate your AI agents with Conto using the REST API

- Human URL: https://conto.finance/docs/quickstart/connecting-agents
- Raw Markdown: https://conto.finance/docs/quickstart/connecting-agents.md
- Terminal view: https://conto.finance/ai/docs/quickstart/connecting-agents

Documentation group: Build

# Connecting Agents via API

This guide shows how to connect different AI agent frameworks to Conto, enabling them to request,
approve, execute, and confirm payments depending on the wallet model you choose.

Info:

  Before integrating, make sure you've [connected your agent, linked a wallet, and generated an SDK
  key](https://conto.finance/quickstart/setup). If you are an autonomous agent evaluating Conto without a human account,
  use the [agent sandbox quickstart](https://conto.finance/quickstart/agent-sandbox) first.

## SDK Authentication

All SDK endpoints require the agent's SDK key in the `Authorization` header:

```
Authorization: Bearer conto_agent_xxxxx...
```

Set that key as `CONTO_API_KEY`. Do not use `CONTO_ORG_API_KEY` for these `/api/sdk/*` calls.

## Choose Your Wallet Flow First

Before wiring up tool calls, decide whether your agent uses a managed wallet or an external wallet.

| Wallet model              | Who controls signing                                | Endpoints                        | What Conto can stop                                                                                |
| ------------------------- | --------------------------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------- |
| **Managed** (`MANAGED`)   | Conto, through secure managed wallet infrastructure | `request -> execute`             | Conto stays in the execution path and can block the spend                                          |
| **External** (`EXTERNAL`) | You, your agent, or your existing wallet stack      | `approve -> transfer -> confirm` | Conto can gate the Conto-routed flow, but cannot block a direct self-signed transfer outside Conto |

The framework examples below assume a managed wallet and use `request -> execute`. If your agent
holds the signing keys, switch to `approve -> transfer -> confirm` instead. See
[Custody Modes](https://conto.finance/guides/custody-modes) for the full breakdown.

## Register The Wallet The Right Way

If your integration already has a wallet, register that same address in Conto as `EXTERNAL` instead
of creating a second one. Include the address and network in the wallet request. Repeating the same
registration returns the existing wallet, so a safe retry does not create a duplicate.

Choose `MANAGED` when you want Conto to create and operate the wallet. Choose `EXTERNAL` when the
signing keys remain in your existing wallet stack and use the external
`approve -> transfer -> confirm` flow.

For a managed wallet, Privy protects the key material and Conto does not store the wallet's raw
private key. An organization-controlled wallet gives the customer owner quorum root administration
and export authority while Conto is a separate signer used after policies and approvals. A Conto-managed wallet has
no customer self-service export. See [Custody Modes](https://conto.finance/guides/custody-modes) for the enforcement
boundary of each model.

## The Shared Payment Helper

Every framework integration wraps the same two-call flow: request (policy check), then execute.
Define it once and reuse it from each framework's tool handler:

```typescript
const CONTO_API_KEY = process.env.CONTO_API_KEY;

type PaymentInput = {
  amount: number;
  recipientAddress: string;
  recipientName?: string;
  purpose: string;
  category?: string;
};

async function executeContoPayment(input: PaymentInput) {
  // Step 1: Request payment authorization (policy check)
  const request = await fetch('https://conto.finance/api/sdk/payments/request', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${CONTO_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ category: 'OPERATIONS', ...input }),
  }).then((r) => r.json());

  if (request.status === 'DENIED') {
    return { success: false, error: request.reasons.join(', ') };
  }

  if (request.status === 'REQUIRES_APPROVAL') {
    return {
      success: false,
      pending: true,
      requestId: request.requestId,
      message: 'Payment requires manual approval',
    };
  }

  // Step 2: Execute the approved payment
  const result = await fetch(
    `https://conto.finance/api/sdk/payments/${request.requestId}/execute`,
    {
      method: 'POST',
      headers: { Authorization: `Bearer ${CONTO_API_KEY}` },
    }
  ).then((r) => r.json());

  return {
    success: true,
    transactionHash: result.txHash,
    amount: result.amount,
    explorerUrl: result.explorerUrl,
  };
}
```

The Python equivalent for Python-based frameworks:

```python

CONTO_API_KEY = os.environ.get('CONTO_API_KEY')
CONTO_API_URL = 'https://conto.finance/api'

def make_payment(amount: float, recipient_address: str, purpose: str, recipient_name: str = None):
    """Make a payment via Conto API"""

    headers = {
        'Authorization': f'Bearer {CONTO_API_KEY}',
        'Content-Type': 'application/json'
    }

    # Step 1: Request authorization
    response = requests.post(
        f'{CONTO_API_URL}/sdk/payments/request',
        headers=headers,
        json={
            'amount': amount,
            'recipientAddress': recipient_address,
            'purpose': purpose,
            'recipientName': recipient_name,
            'category': 'OPERATIONS'
        }
    )
    request_data = response.json()

    if request_data.get('status') == 'DENIED':
        return {'success': False, 'error': ', '.join(request_data.get('reasons', []))}

    if request_data.get('status') == 'REQUIRES_APPROVAL':
        return {'success': False, 'pending': True, 'requestId': request_data['requestId']}

    # Step 2: Execute the payment
    result = requests.post(
        f'{CONTO_API_URL}/sdk/payments/{request_data["requestId"]}/execute',
        headers=headers
    ).json()

    return {
        'success': True,
        'transactionHash': result.get('txHash'),
        'amount': result.get('amount'),
        'explorerUrl': result.get('explorerUrl')
    }
```

## Framework Integrations

With the shared helper in place, each framework only needs its own tool wiring.

### OpenAI Assistants

```typescript

const openai = new OpenAI();

// Define the payment tool for your assistant
const tools = [
  {
    type: 'function',
    function: {
      name: 'make_payment',
      description: 'Make a payment to a recipient',
      parameters: {
        type: 'object',
        properties: {
          amount: { type: 'number', description: 'Payment amount in USD' },
          recipientAddress: { type: 'string', description: 'Recipient wallet address' },
          recipientName: { type: 'string', description: 'Recipient name' },
          purpose: { type: 'string', description: 'Payment purpose' },
        },
        required: ['amount', 'recipientAddress', 'purpose'],
      },
    },
  },
];
```

  In your OpenAI chat loop, check for `tool_calls` in the response. When the model calls
  `make_payment`, parse the arguments, pass them to `executeContoPayment`, and send the result back
  as a tool response message.

### Anthropic Claude (Tool Use)

```typescript

const anthropic = new Anthropic();

const tools = [
  {
    name: 'make_payment',
    description: 'Make a payment to a recipient using Conto',
    input_schema: {
      type: 'object',
      properties: {
        amount: { type: 'number', description: 'Payment amount in USD' },
        recipientAddress: { type: 'string', description: 'Recipient wallet address' },
        recipientName: { type: 'string', description: 'Recipient name' },
        purpose: { type: 'string', description: 'Payment purpose' },
      },
      required: ['amount', 'recipientAddress', 'purpose'],
    },
  },
];

async function runAgent(userMessage: string) {
  const response = await anthropic.messages.create({
    model: 'claude-opus-4-8',
    max_tokens: 1024,
    tools: tools,
    messages: [{ role: 'user', content: userMessage }],
  });

  for (const block of response.content) {
    if (block.type === 'tool_use' && block.name === 'make_payment') {
      const result = await executeContoPayment(block.input as PaymentInput);
      // Continue conversation with tool result...
    }
  }
}
```

### LangChain

```typescript

class ContoPaymentTool extends Tool {
  name = 'conto_payment';
  description =
    'Make a payment using Conto. Input should be JSON with amount, recipientAddress, and purpose.';

  async _call(input: string): Promise<string> {
    const params = JSON.parse(input) as PaymentInput;
    return JSON.stringify(await executeContoPayment(params));
  }
}
```

### Python (Any Framework)

Register `make_payment` (defined above) as a tool in your framework of choice (CrewAI, AutoGen,
or a custom loop) and return its dict result to the model as the tool output.

## Payment Endpoints Reference

| Endpoint                         | Method | Description                         |
| -------------------------------- | ------ | ----------------------------------- |
| `/api/sdk/payments/request`      | POST   | Request payment authorization       |
| `/api/sdk/payments/approve`      | POST   | Approve payment for external wallet |
| `/api/sdk/payments/{id}/execute` | POST   | Execute approved payment            |
| `/api/sdk/payments/{id}/confirm` | POST   | Confirm external wallet payment     |
| `/api/sdk/payments/{id}`         | GET    | Get payment status                  |

## Error Handling

| Status | Code                   | Description                                  |
| ------ | ---------------------- | -------------------------------------------- |
| 400    | `INVALID_INPUT`        | Invalid request data                         |
| 401    | `AUTH_FAILED`          | Invalid, expired, or suspended-agent SDK key |
| 400    | `INSUFFICIENT_BALANCE` | Wallet balance too low (at execution)        |
| 429    | `RATE_LIMITED`         | Too many requests                            |

Policy denials are not HTTP errors. When a payment violates a policy (spend limit, time window,
counterparty rule), `/api/sdk/payments/request` returns `200` with `status: "DENIED"` and a
`violations` array describing each violation (for example `DAILY_LIMIT` or `TIME_WINDOW`).

For full error handling patterns, see [Error Handling](https://conto.finance/sdk/error-handling).

## Next Steps

### Make Your First Payment
Link: https://conto.finance/quickstart/setup

    Detailed payment flow walkthrough

### SDK Reference
Link: https://conto.finance/sdk/payments

    Full SDK documentation

### Examples
Link: https://conto.finance/sdk/examples

    Complete integration examples

### Error Handling
Link: https://conto.finance/sdk/error-handling

    Handle errors gracefully