SDK

Install the TypeScript SDK, call every Arc0 resource from your own backend, and handle approval-held calls.

@arc0/sdk wraps the REST API in a typed client. Use it from any backend that needs to create connect links, run actions, or read the audit log without going through an MCP client.

Install

npm install @arc0/sdk

The client

client.ts
import { Arc0 } from '@arc0/sdk';

const arc0 = new Arc0({ apiKey: process.env.ARC0_API_KEY });

The key decides what the client can do — an API key with Read-only access will fail on anything that writes. See API keys.

const link = await arc0.connect.createLink({
  user: 'u_8f2',
  customer: 'northwind',
  app: 'salesforce',
});
// { id: 'rc_...', url: 'https://connect.arc0.ai/c/...', expiresAt: '2026-10-04T12:00:00Z' }

Send link.url to the user. See Arc0 Connect.

MCP endpoints

const url = await arc0.mcp.endpoint({ user: 'u_8f2' });
// https://mcp.arc0.ai/u/u_8f2

Actions

await arc0.actions.run('gmail.send_email', {
  user: 'u_8f2',
  input: { to: 'ops@acme.com', subject: 'Refund issued', body: 'Your refund is on its way.' },
});

const actions = await arc0.actions.list({ app: 'salesforce' });
// each entry carries its `scope`: read, write or destructive

Proxy

const res = await arc0.proxy.fetch({
  user: 'u_8f2',
  app: 'stripe',
  path: '/v1/charges',
  method: 'GET',
});

Arc0 injects the user's credentials and still applies policies and audit on every call. See REST API and proxy.

Connections

await arc0.connections.list({ user: 'u_8f2' });
await arc0.connections.get('conn_z663h86fl3z2');
await arc0.connections.refresh('conn_z663h86fl3z2');
await arc0.connections.revoke('conn_z663h86fl3z2');
await arc0.connections.export('conn_z663h86fl3z2');
await arc0.connections.reconnectLink('conn_z663h86fl3z2');

.export() needs a key with Full access; see Token export.

Users

await arc0.users.list();
await arc0.users.get('u_8f2');
await arc0.users.delete('u_8f2'); // deletes the user and their tokens

Audit log

await arc0.audit.list({ user: 'u_8f2', app: 'stripe', decision: 'blocked' });

Approvals

await arc0.approvals.approve('apr_9k2m');
await arc0.approvals.reject('apr_9k2m');

Your app can decide on an approval this way, or an approver can use the Approve and Reject buttons in the email Arc0 sends. See Approvals.

Policies

const policy = await arc0.policies.get('pol_acme_support');

await arc0.policies.update('pol_acme_support', {
  defaults: { read: 'allow', write: 'ask', destructive: 'deny' },
});

For the common case of changing one app's grade rules on the default policy, use the shorthand:

await arc0.policies.set('hubspot', { read: 'allow', write: 'ask', destructive: 'deny' });

Both write a new version; see Policies.

Error handling

Every failed call throws an Arc0Error with a code, a message and a retryable flag. The full list of codes covers policy denials, missing or broken connections, and upstream failures.

An ask rule doesn't fail outright — it throws with code: 'approval_required' and an approval object:

run-with-approval.ts
import { Arc0Error } from '@arc0/sdk';

try {
  await arc0.actions.run('stripe.create_refund', {
    user: 'u_8f2',
    input: { charge: 'ch_1', amount: 2000 },
  });
} catch (err) {
  if (err instanceof Arc0Error && err.code === 'approval_required') {
    console.log('Waiting on', err.approval.requestedFrom, 'until', err.approval.expiresAt);
    return;
  }
  throw err;
}

Once an approver decides, the approval.decided webhook tells you it's safe to call arc0.actions.run again with the same input — approved calls go through, and the audit log records them as allowed with the reason Approved by {name}.

Retries

Check retryable before retrying anything. upstream_rate_limited, upstream_timeout and upstream_unavailable are worth a backoff-and-retry; denied_by_policy and invalid_api_key never are, no matter how many times you call them. approval_required is marked retryable, but only after the approval resolves — retrying immediately holds the call again instead of running it.

TypeScript types

The client is written in TypeScript and ships its own types — Connection, AuditEvent, Policy, Arc0Error and the rest are all importable:

import type { Connection, AuditEvent } from '@arc0/sdk';

On this page