> ## Documentation Index
> Fetch the complete documentation index at: https://conto.finance/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Admin SDK

> Manage agents, wallets, and policies programmatically with organization API keys

# Admin SDK

The Admin SDK lets you programmatically create agents, provision wallets, and configure spending policies. It uses **organization API keys** (`conto_xxx...`) which operate at the org level, separate from the agent SDK keys used for payments.

```typescript theme={null}
import { ContoAdmin } from '@conto_finance/sdk';

const admin = new ContoAdmin({
  orgApiKey: process.env.CONTO_ORG_API_KEY!,
});

// Create a policy, create an agent, wire them together
const policy = await admin.policies.create({
  name: 'Daily $500 limit',
  policyType: 'SPEND_LIMIT',
  rules: [{ ruleType: 'DAILY_LIMIT', operator: 'LTE', value: '500' }],
});

const agent = await admin.agents.create({
  name: 'billing-agent',
  agentType: 'CUSTOM',
});

await admin.agents.assignPolicy(agent.id, policy.id);
```

## Organization API Keys

Organization API keys authenticate at the org level and can manage all agents, wallets, and policies in the organization. They are different from agent SDK keys.

|               | Org API Key         | Agent SDK Key        |
| ------------- | ------------------- | -------------------- |
| **Format**    | `conto_xxx...`      | `conto_agent_xxx...` |
| **Scope**     | Entire organization | Single agent         |
| **Used for**  | Admin operations    | Payment operations   |
| **SDK class** | `ContoAdmin`        | `Conto`              |

### Creating an Org API Key

<Steps>
  <Step title="Go to Settings">
    Navigate to **Settings** > **API Keys** in the dashboard.
  </Step>

  <Step title="Create Key">
    Click **Create API Key**, enter a name, and select a scope preset:

    * **Read Only** - View agents, wallets, policies
    * **Standard** - Read + write agents, wallets, policies, plus membership lookup for ownership flows
    * **Admin** - Full access (Owner only)
  </Step>

  <Step title="Copy the Key">
    <Warning>
      Copy and save the key immediately. It is only shown once.
    </Warning>
  </Step>
</Steps>

### Scopes

Org API keys use fine-grained scopes that control what the key can access:

| Scope                  | Description                                                     |
| ---------------------- | --------------------------------------------------------------- |
| `agents:read`          | List and view agents                                            |
| `agents:write`         | Create, update, delete, freeze/unfreeze agents                  |
| `wallets:read`         | List and view wallets                                           |
| `wallets:write`        | Create, update, delete, provision wallets                       |
| `policies:read`        | List and view policies and rules                                |
| `policies:write`       | Create, update, delete policies, manage rules, assign to agents |
| `transactions:read`    | View transaction history                                        |
| `counterparties:read`  | View counterparties                                             |
| `counterparties:write` | Create and update counterparties                                |
| `alerts:read`          | View alerts                                                     |
| `alerts:write`         | Acknowledge and resolve alerts                                  |
| `analytics:read`       | View spending analytics                                         |
| `audit:read`           | View audit logs                                                 |
| `team:read`            | List organization memberships and ownership candidates          |

## Initialization

```typescript theme={null}
import { ContoAdmin } from '@conto_finance/sdk';

const admin = new ContoAdmin({
  orgApiKey: process.env.CONTO_ORG_API_KEY!,
  timeout: 30000,  // Optional, 30s default
});
```

<Info>
  `ContoAdmin` rejects agent SDK keys (`conto_agent_xxx`). If you pass one, it throws an error telling you to use the `Conto` class instead.
</Info>

## admin.agents

Manage the lifecycle of AI agents in your organization.

### agents.list()

List agents with optional filters.

```typescript theme={null}
const { agents, total } = await admin.agents.list({
  status: 'ACTIVE',
  search: 'billing',
  limit: 20,
  offset: 0,
});
```

| Parameter | Type   | Description                                                  |
| --------- | ------ | ------------------------------------------------------------ |
| `status`  | string | Filter by status: ACTIVE, PAUSED, SUSPENDED, REVOKED, FROZEN |
| `search`  | string | Search by name or description                                |
| `limit`   | number | Results per page (1-100, default 50)                         |
| `offset`  | number | Pagination offset                                            |

### agents.create()

Create a new agent.

```typescript theme={null}
const { members } = await admin.team.listMembers();
const serviceOwner = members.find((member) => member.user?.email === 'ops@yourcompany.com');

const agent = await admin.agents.create({
  name: 'billing-agent',
  agentType: 'CUSTOM',
  description: 'Handles vendor payments',
  ownerMembershipId: serviceOwner?.id,
});
```

| Parameter           | Type   | Required | Description                                                                                                                                                           |
| ------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`              | string | Yes      | Agent name (1-100 chars)                                                                                                                                              |
| `agentType`         | string | Yes      | OPENAI\_ASSISTANT, ANTHROPIC\_CLAUDE, LANGCHAIN, AUTOGPT, or CUSTOM                                                                                                   |
| `description`       | string | No       | Description (max 500 chars)                                                                                                                                           |
| `publicKey`         | string | No       | Ethereum address (auto-generated if omitted)                                                                                                                          |
| `externalId`        | string | No       | Your own identifier for the agent                                                                                                                                     |
| `ownerMembershipId` | string | No       | Organization member to record as the agent owner for programmatic/admin flows. Fetch this from `admin.team.listMembers()`. If omitted, Conto assigns a default owner. |

## admin.team

List organization memberships for the authenticated organization. This is useful when you need a stable `ownerMembershipId` to reuse across agent creation and repair flows.

### team.listMembers()

```typescript theme={null}
const { organizationId, members } = await admin.team.listMembers();

const dedicatedOwner = members.find((member) => member.user?.email === 'ops@yourcompany.com');

if (!dedicatedOwner) {
  throw new Error('No dedicated owner membership found');
}

await admin.agents.create({
  name: 'billing-agent',
  agentType: 'CUSTOM',
  ownerMembershipId: dedicatedOwner.id,
});
```

This method requires an org API key with the `team:read` scope or `admin`.

### agents.get()

```typescript theme={null}
const agent = await admin.agents.get('agent_id');
```

### agents.update()

```typescript theme={null}
await admin.agents.update('agent_id', {
  name: 'Updated Name',
  status: 'PAUSED',
});
```

### agents.delete()

Soft-deletes an agent. Deactivates its SDK keys and wallet/card links.

```typescript theme={null}
await admin.agents.delete('agent_id');
```

### agents.freeze() / agents.unfreeze()

Freeze blocks all transactions for an agent. Unfreeze restores them.

```typescript theme={null}
await admin.agents.freeze('agent_id', {
  reason: 'Suspicious activity detected',
  freezeWallets: true,
});

await admin.agents.unfreeze('agent_id', {
  reason: 'Investigation complete',
  unfreezeWallets: true,
  resetCounters: true,
});
```

### agents.linkWallet()

Link a wallet to an agent with spending limits.

```typescript theme={null}
await admin.agents.linkWallet('agent_id', {
  walletId: 'wallet_id',
  delegationType: 'LIMITED',
  spendLimitPerTx: 100,
  spendLimitDaily: 1000,
  spendLimitWeekly: 5000,
  allowedDays: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
});
```

| Parameter           | Type      | Default  | Description                                       |
| ------------------- | --------- | -------- | ------------------------------------------------- |
| `walletId`          | string    | Required | Wallet to link                                    |
| `delegationType`    | string    | LIMITED  | FULL, LIMITED, VIEW\_ONLY, PREAPPROVED, ALLOWLIST |
| `spendLimitPerTx`   | number    | 100      | Max per transaction                               |
| `spendLimitDaily`   | number    | 1000     | Max per day                                       |
| `spendLimitWeekly`  | number    | -        | Max per week                                      |
| `spendLimitMonthly` | number    | -        | Max per month                                     |
| `allowedHoursStart` | number    | 0        | Allowed hours start (0-23)                        |
| `allowedHoursEnd`   | number    | 24       | Allowed hours end (1-24)                          |
| `allowedDays`       | string\[] | Mon-Fri  | Allowed days of week                              |

### agents.assignPolicy() / agents.unassignPolicy()

```typescript theme={null}
await admin.agents.assignPolicy('agent_id', 'policy_id');
await admin.agents.unassignPolicy('agent_id', 'policy_id');
```

### agents.listWallets() / agents.listPolicies()

```typescript theme={null}
const { wallets } = await admin.agents.listWallets('agent_id');
const { policies } = await admin.agents.listPolicies('agent_id');
```

### agents.createSdkKey() / agents.listSdkKeys() / agents.revokeSdkKey()

Create agent SDK keys programmatically. The key value is returned only once.

```typescript theme={null}
const { key } = await admin.agents.createSdkKey('agent_id', {
  name: 'Production Key',
  keyType: 'standard',
  expiresInDays: 90,
});

console.log('Save this key:', key); // conto_agent_xxx...

const keys = await admin.agents.listSdkKeys('agent_id');
await admin.agents.revokeSdkKey('agent_id', 'key_id');
```

## admin.wallets

Manage wallets across the organization.

### wallets.list()

```typescript theme={null}
const { wallets, total } = await admin.wallets.list({
  chainType: 'EVM',
  status: 'ACTIVE',
  limit: 20,
});
```

| Parameter   | Type   | Description                          |
| ----------- | ------ | ------------------------------------ |
| `status`    | string | ACTIVE, FROZEN, ARCHIVED             |
| `chainType` | string | EVM or SOLANA                        |
| `chainId`   | string | Specific chain ID                    |
| `limit`     | number | Results per page (1-100, default 50) |
| `offset`    | number | Pagination offset                    |

### wallets.create()

```typescript theme={null}
const wallet = await admin.wallets.create({
  name: 'billing-wallet',
  chainType: 'EVM',
  custodyType: 'SPONGE',
});
```

| Parameter          | Type   | Default  | Description                                                                                  |
| ------------------ | ------ | -------- | -------------------------------------------------------------------------------------------- |
| `name`             | string | Required | Wallet name (1-100 chars)                                                                    |
| `chainType`        | string | EVM      | EVM or SOLANA                                                                                |
| `chainId`          | string | -        | Chain ID (defaults to Tempo testnet)                                                         |
| `custodyType`      | string | PRIVY    | PRIVY, SPONGE, EXTERNAL, SMART\_CONTRACT                                                     |
| `walletType`       | string | EOA      | EOA, SMART\_WALLET, MULTISIG                                                                 |
| `address`          | string | -        | Required when attaching an existing wallet instead of minting a new one                      |
| `externalWalletId` | string | -        | Existing provider wallet ID. Use with `custodyType=PRIVY` to attach an existing Privy wallet |
| `importAddress`    | string | -        | Legacy watch-only import path for `EXTERNAL` custody                                         |

Attach an existing Privy-backed wallet:

```typescript theme={null}
const wallet = await admin.wallets.create({
  name: 'enchant-treasury',
  chainType: 'EVM',
  chainId: '8453',
  custodyType: 'PRIVY',
  externalWalletId: 'privy_wallet_123',
  address: '0x1234...abcd',
});
```

Register a self-custodied wallet:

```typescript theme={null}
const wallet = await admin.wallets.create({
  name: 'external-ops-wallet',
  chainType: 'EVM',
  chainId: '8453',
  custodyType: 'EXTERNAL',
  address: '0xabcd...1234',
});
```

`wallets.create()` is idempotent within the organization per `externalWalletId + chainId` or
`address + chainId`, so retrying the same request returns the existing wallet instead of creating a
duplicate.

This attach flow only works when the referenced wallet is visible to the same Privy app Conto is
configured to use for managed execution. If the signer lives in a different Privy app or an
external wallet stack, register it as `EXTERNAL` instead.

### wallets.get() / wallets.update() / wallets.delete()

```typescript theme={null}
const wallet = await admin.wallets.get('wallet_id');

await admin.wallets.update('wallet_id', {
  name: 'Renamed Wallet',
  status: 'FROZEN',
});

await admin.wallets.delete('wallet_id'); // Fails if linked to agents or if payment history exists
```

Organization API keys can now manage the full wallet lifecycle through `wallets.get()`,
`wallets.update()`, and `wallets.delete()` in addition to `wallets.list()` and `wallets.create()`.

### wallets.provision()

Link a wallet to its custody provider and sync its onchain state.

```typescript theme={null}
const result = await admin.wallets.provision('wallet_id');
console.log('Address:', result.wallet.address);
console.log('Balance:', result.wallet.balance, result.wallet.currency);
```

### wallets.refreshBalance()

Fetch the latest balance from the chain.

```typescript theme={null}
const result = await admin.wallets.refreshBalance('wallet_id');
console.log('Total:', result.totalBalance);
for (const balance of result.balances) {
  console.log(`${balance.chainName}:`, balance.balance, balance.symbol);
}
```

## admin.policies

Create and manage spending policies and their rules.

### policies.list()

```typescript theme={null}
const { policies, total } = await admin.policies.list({
  limit: 50,
  offset: 0,
});
```

### policies.create()

Create a policy with optional rules and agent assignments in one call.

```typescript theme={null}
const policy = await admin.policies.create({
  name: 'Conservative limits',
  policyType: 'SPEND_LIMIT',
  priority: 50,
  rules: [
    { ruleType: 'MAX_AMOUNT', operator: 'LTE', value: '200' },
    { ruleType: 'DAILY_LIMIT', operator: 'LTE', value: '1000' },
    { ruleType: 'MONTHLY_LIMIT', operator: 'LTE', value: '10000' },
  ],
  agentIds: ['cmm5a2agt003o49h7bjqza44s', 'cmm5a3agt004p49h7bjqza55t'], // Assign immediately
});
```

### Policy Types

| Type                 | Description                                |
| -------------------- | ------------------------------------------ |
| `SPEND_LIMIT`        | Transaction and periodic spending limits   |
| `APPROVAL_THRESHOLD` | Require human approval above a threshold   |
| `COUNTERPARTY`       | Allow/block specific counterparties        |
| `CATEGORY`           | Allow/block spending categories            |
| `GEOGRAPHIC`         | Country-based restrictions                 |
| `TIME_WINDOW`        | Time-of-day restrictions                   |
| `VELOCITY`           | Transaction frequency limits               |
| `WHITELIST`          | Allowlist-only recipients                  |
| `CONTRACT_ALLOWLIST` | Allowed smart contracts                    |
| `BLACKOUT_PERIOD`    | Block transactions during specific periods |
| `BUDGET_ALLOCATION`  | Budget cap enforcement                     |
| `EXPIRATION`         | Date-range validity                        |
| `COMPOSITE`          | Combine multiple rule types                |
| `MERCHANT`           | Merchant category restrictions             |

### policies.get() / policies.update() / policies.delete()

```typescript theme={null}
const policy = await admin.policies.get('policy_id');

await admin.policies.update('policy_id', {
  name: 'Updated name',
  isActive: false,
  priority: 75,
});

await admin.policies.delete('policy_id');
```

### Rule Management

Add, update, and remove individual rules on a policy.

```typescript theme={null}
// Add a single rule
await admin.policies.addRule('policy_id', {
  ruleType: 'DAILY_LIMIT',
  operator: 'LTE',
  value: '500',
  action: 'DENY',
});

// Add multiple rules at once
await admin.policies.addRules('policy_id', [
  { ruleType: 'MAX_AMOUNT', operator: 'LTE', value: '100' },
  { ruleType: 'WEEKLY_LIMIT', operator: 'LTE', value: '2000' },
]);

// Update a rule
await admin.policies.updateRule('policy_id', 'rule_id', {
  value: '750',
});

// Delete a rule
await admin.policies.deleteRule('policy_id', 'rule_id');

// Delete all rules
await admin.policies.deleteAllRules('policy_id');
```

### Rule Operators

| Operator                 | Description                 |
| ------------------------ | --------------------------- |
| `EQUALS`                 | Exact match                 |
| `NOT_EQUALS`             | Not equal                   |
| `GREATER_THAN`           | Greater than                |
| `LESS_THAN`              | Less than                   |
| `GTE`                    | Greater than or equal       |
| `LTE`                    | Less than or equal          |
| `IN` / `IN_LIST`         | Value is in list            |
| `NOT_IN` / `NOT_IN_LIST` | Value is not in list        |
| `BETWEEN`                | Value is between two bounds |
| `NOT_BETWEEN`            | Value is outside bounds     |
| `DENY`                   | Always deny                 |

### Rule Actions

| Action             | Description                             |
| ------------------ | --------------------------------------- |
| `ALLOW`            | Allow if condition met (default)        |
| `DENY`             | Block if condition met                  |
| `REQUIRE_APPROVAL` | Require human approval if condition met |

## Complete Example

Provision a new agent from scratch:

```typescript theme={null}
import { ContoAdmin } from '@conto_finance/sdk';

const admin = new ContoAdmin({
  orgApiKey: process.env.CONTO_ORG_API_KEY!,
});

// 1. Create a wallet
const wallet = await admin.wallets.create({
  name: 'ops-wallet',
  chainType: 'EVM',
  custodyType: 'SPONGE',
});
await admin.wallets.provision(wallet.id);

// 2. Create a spending policy
const policy = await admin.policies.create({
  name: 'Standard ops limits',
  policyType: 'SPEND_LIMIT',
  rules: [
    { ruleType: 'MAX_AMOUNT', operator: 'LTE', value: '500' },
    { ruleType: 'DAILY_LIMIT', operator: 'LTE', value: '2000' },
  ],
});

// 3. Create the agent
const agent = await admin.agents.create({
  name: 'ops-agent',
  agentType: 'CUSTOM',
  description: 'Handles operational payments',
});

// 4. Link wallet with per-agent limits
await admin.agents.linkWallet(agent.id, {
  walletId: wallet.id,
  delegationType: 'LIMITED',
  spendLimitPerTx: 500,
  spendLimitDaily: 2000,
});

// 5. Assign the policy
await admin.agents.assignPolicy(agent.id, policy.id);

// 6. Generate an SDK key for the agent
const { key } = await admin.agents.createSdkKey(agent.id, {
  name: 'Production',
  expiresInDays: 90,
});

console.log('Agent ready. SDK key:', key);
```

## Security

<AccordionGroup>
  <Accordion title="Org keys are powerful. Treat them accordingly.">
    Org API keys can manage all agents and wallets in the organization. Store them in a secrets manager (AWS Secrets Manager, Vault, etc.), never in source control. Use the minimum scope needed for the task.
  </Accordion>

  <Accordion title="Use scoped keys for CI/CD">
    Create keys with only the scopes your pipeline needs. A deployment script that provisions agents only needs `agents:write`, `wallets:write`, and `policies:write`.
  </Accordion>

  <Accordion title="Rotate keys regularly">
    Set expiration when creating keys. When rotating:

    1. Create a new key
    2. Update your secrets
    3. Deploy
    4. Revoke the old key
  </Accordion>

  <Accordion title="All actions are audit-logged">
    Every operation made with an org API key is recorded in the audit log with actor type `API_KEY` and the key ID. Review audit logs in the dashboard under **Audit Logs**.
  </Accordion>
</AccordionGroup>

### Limitations

Org API keys **cannot** change billing plans. Billing changes require dashboard session auth with an Owner account.

## Error Handling

```typescript theme={null}
try {
  await admin.agents.create({ name: 'test', agentType: 'CUSTOM' });
} catch (error) {
  console.error(error.code, error.status, error.message);
}
```

| Error Code           | Status | Description                |
| -------------------- | ------ | -------------------------- |
| `AUTH_FAILED`        | 401    | Invalid or revoked API key |
| `INSUFFICIENT_SCOPE` | 403    | Key lacks required scope   |
| `NOT_FOUND`          | 404    | Resource not found         |
| `VALIDATION_FAILED`  | 400    | Invalid request body       |
| `TIMEOUT`            | 0      | Request timed out          |

## Next Steps

<CardGroup cols={2}>
  <Card title="Policies" icon="shield" href="/policies/overview">
    Learn about the policy engine and all rule types
  </Card>

  <Card title="Authentication" icon="key" href="/sdk/authentication">
    Agent SDK keys and scopes
  </Card>

  <Card title="Payments" icon="credit-card" href="/sdk/payments">
    Making payments with agent SDK keys
  </Card>

  <Card title="CLI Policies" icon="terminal" href="/cli/policies">
    Manage policies from the command line
  </Card>
</CardGroup>
