Build a vault integration
A vault integration is a server process that reads and writes a customer’s vault on their behalf, without them present to sign each request. Two shapes:
- Sync in. Pull an outside source (a calendar, a CRM export, a fitness API) into the vault on a schedule, so agents can compute on it.
- Export out. Read vault data and push it to another system: a warehouse, a backup, a report.
Pick the right tool first
| The outside system | Use |
|---|---|
| Slack, Telegram, email, or an MCP server | A vault connector. No code of yours runs. |
| Can call a URL when something happens | A workflow webhook trigger. |
| Must be polled or transformed by your code | A vault integration (this page). |
How an integration is authorized
The customer grants authority once, while present; the integration then runs headless until they revoke it.
| Grant | What it allows | Who issues it |
|---|---|---|
| Vault write delegation | The vault service writes to the vault’s storage on the owner’s behalf. | Granted when the owner’s app calls vault.ensure() with a signer and chainUrl. |
| Delegation token | Your integration calls the vault API as the customer, within the token’s scope, without their key. | Minted by the customer’s wallet and handed to your backend. |
Your server never holds the customer’s key. Revoking the token locks the integration out.
Part A: in the customer’s app, once
import { VaultSDK, CereWallet } from "@cef-ai/vault-sdk";
const sdk = new VaultSDK({ endpoint: config.vaultUrl, chainUrl: config.chainUrl, signer: await CereWallet.fromMnemonic(mnemonic), // the owner, present now});
const vault = await sdk.vault.ensure();const scopes = await vault.scopes.list();if (!scopes.some((s) => s.name === "calendar")) { await vault.scopes.create({ name: "calendar", displayName: "Calendar" });}Then obtain a delegation token from the customer’s wallet, and send it with
vault.id to your backend. Store both as this customer’s integration
credential.
Part B: in the integration
import { VaultSDK, DelegationAuthProvider } from "@cef-ai/vault-sdk";
const sdk = new VaultSDK({ endpoint: config.vaultUrl, auth: new DelegationAuthProvider(delegationToken),});
const vault = await sdk.vault.byId(vaultId);if (vault.isDisconnected()) { throw new Error("vault storage credential can no longer refresh; the owner must act");}auth replaces signer: the integration uses the token for every call.
Sync in
Publish each new record as an event in the integration’s scope. Track your source’s cursor on your side and publish only what is new: the same record published twice is two events.
const calendar = vault.scope("calendar");
for (const r of await fetchNewRecords(sinceCursor)) { await calendar.publish({ type: "calendar.event", context: `calendar:${r.calendarId}`, payload: r, });}Export out
Walk the scope’s streams and page their events:
const health = vault.scope("health");let streamCursor: string | undefined;do { const streams = await health.streams.list({ cursor: streamCursor }); for (const s of streams.items) { let cursor: string | undefined; do { const page = await health.stream(s.context).events.list({ cursor, limit: 200 }); await pushToWarehouse(page.items); cursor = page.hasMore ? page.nextCursor : undefined; } while (cursor); } streamCursor = streams.hasMore ? streams.nextCursor : undefined;} while (streamCursor);To export what a connected Agent Service computed, read its cubbies with
vault.service(agentServicePubkey).cubby(alias).query(sql, params), or the
vault’s conclusions with vault.memory.search(...).
Keep it healthy
- Treat an auth failure as consent withdrawn. The customer revoked the token or it expired. Stop, and do not retry blindly.
- Check
vault.isDisconnected()each run. The storage credential cannot refresh until the owner acts in their own app. - Make writes idempotent downstream. Dedupe on your source cursor, or give each payload a stable id agents can upsert on.