Skip to main content

Platform API

The Platform API lets you build products on top of 1Claw. Register your app, create bootstrap templates, provision end-users, and manage their secrets infrastructure — all with custody guarantees that prevent your platform from accessing end-user secrets.

Requirements

The Platform API requires a Pro or higher subscription. Upgrade your plan →

Quickstart (~10 min)

1. Register a Platform App

curl -X POST "https://api.1claw.xyz/v1/platform/apps" \
-H "Authorization: Bearer YOUR_USER_JWT" \
-H "Content-Type: application/json" \
-d '{
"name": "My DeFi Platform",
"slug": "my-defi",
"description": "DeFi automation for end users",
"billing_model": "platform_pays",
"auth_mode": "silent",
"max_connected_users": 1000,
"max_requests_per_minute": 120
}'

Save the returned api_key (prefixed plt_) — it won't be shown again. This key authenticates all subsequent Platform API calls.

Optional fields on app creation:

FieldTypeDescription
max_connected_usersintegerCap on connected users (new connections rejected when reached)
max_requests_per_minuteintegerPer-app rate limit for Platform API endpoints
Key expiration and rotation

Set api_key_expires_at (ISO 8601) when creating the app to auto-expire the key. Rotate at any time with POST /v1/platform/apps/{id}/rotate-key, optionally setting a new expiry. Expired keys return 401.

2. Create a Bootstrap Template

Templates define what gets created for each user: a vault, agents, and access policies.

curl -X POST "https://api.1claw.xyz/v1/platform/apps/APP_ID/templates" \
-H "Authorization: Bearer plt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "default-template",
"spec": {
"vault": {
"name": "user-vault",
"description": "Auto-provisioned vault"
},
"agents": [{
"name": "defi-bot",
"description": "Automated DeFi agent",
"intents": { "enabled": true },
"shroud_enabled": true,
"shroud_config": {
"pii_policy": "redact",
"enable_secret_redaction": true
}
}],
"policies": [{
"principal_ref": "agents.primary",
"vault_ref": "vault",
"paths": ["api-keys/*", "keys/*"],
"permissions": ["read", "write"],
"conditions": {}
}]
}
}'

3. Provision a User

curl -X POST "https://api.1claw.xyz/v1/platform/users/upsert" \
-H "Authorization: Bearer plt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"external_subject": "telegram:123456789"
}'

Set create_sub_org: true to auto-create a sub-organization for the connected user, giving them isolated resources under the parent org:

curl -X POST "https://api.1claw.xyz/v1/platform/users/upsert" \
-H "Authorization: Bearer plt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"external_subject": "telegram:123456789",
"create_sub_org": true
}'

4. Bootstrap the User

curl -X POST "https://api.1claw.xyz/v1/platform/connections/CONNECTION_ID/bootstrap" \
-H "Authorization: Bearer plt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"template_id": "TEMPLATE_UUID"
}'

The response includes claim_url, claim_token, and summary (with vault_id, agent_id, policy_ids, agent_api_key — one-time, and signing_keys[] when signing keys are defined in the template). See Step 7 for how to use the agent API key and signing keys.

5. Share the Claim URL

Send the claim_url to your end user (e.g. via your app's UI, email, or bot message). When they visit it, they'll see what was provisioned and can claim the resources with one click.

The claim URL format is https://1claw.xyz/connect/{slug}/claim/{token}. It expires after 10 minutes.

Reissue an expired claim URL:

If the token expires before your user claims, mint a fresh one without re-provisioning:

curl -X POST "https://api.1claw.xyz/v1/platform/connections/CONNECTION_ID/reissue-claim" \
-H "Authorization: Bearer plt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{}'
# → { "claim_url": "...", "claim_token": "ct_...", "expires_in": 600, "connection_id": "..." }

Programmatic claim (for headless flows):

# Preview what was provisioned
curl "https://api.1claw.xyz/v1/platform/claim/ct_TOKEN"

# Redeem the claim
curl -X POST "https://api.1claw.xyz/v1/platform/claim/ct_TOKEN"

6. Agent Access is Automatic

After bootstrap, the agent already has access to the vault paths defined in your template's policies array. No additional delegation step is needed — the bootstrap template creates both the agent and its access policies in one atomic operation.

If the user needs to grant the agent access to additional paths later, they can:

  1. Visit the vault's Policies tab in the dashboard
  2. Create a new access policy for the agent
  3. Or use the API: POST /v1/vaults/{vault_id}/policies

7. Operate the Bootstrapped Agent

The bootstrap response includes summary.agent_api_key (one-time, like regular agent creation) and summary.signing_keys (chain, address, public key). Store the API key securely — it won't be shown again.

Get an agent JWT:

curl -X POST "https://api.1claw.xyz/v1/auth/agent-token" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "AGENT_UUID",
"api_key": "ocv_AGENT_API_KEY"
}'
# → { "access_token": "eyJ...", "vault_ids": ["..."] }

Get the agent's wallet address:

The wallet addresses are returned in the bootstrap response under summary.signing_keys. You can also retrieve them later:

curl "https://api.1claw.xyz/v1/agents/AGENT_UUID/signing-keys" \
-H "Authorization: Bearer YOUR_USER_OR_PLATFORM_JWT"
# → { "keys": [{ "chain": "ethereum", "address": "0x...", "public_key": "...", "is_active": true }] }

Submit a transaction (Intents API):

AGENT_JWT="eyJ..."  # from token exchange above

curl -X POST "https://api.1claw.xyz/v1/agents/AGENT_UUID/transactions" \
-H "Authorization: Bearer $AGENT_JWT" \
-H "Content-Type: application/json" \
-d '{
"chain": "ethereum",
"chain_id": 1,
"to": "0xRecipientAddress",
"value": "0.01",
"data": "0x"
}'
# → { "tx_hash": "0x...", "signed_tx": "0x...", "status": "broadcast" }

Sign without broadcasting (sign-only mode):

curl -X POST "https://api.1claw.xyz/v1/agents/AGENT_UUID/transactions/sign" \
-H "Authorization: Bearer $AGENT_JWT" \
-H "Content-Type: application/json" \
-d '{
"chain": "ethereum",
"chain_id": 1,
"to": "0xRecipientAddress",
"value": "0.01",
"data": "0x"
}'
# → { "signed_tx": "0x...", "tx_hash": "0x...", "from": "0x...", "status": "sign_only" }
Platform Flow Summary
  1. Bootstrap → save agent_api_key and signing_keys[].address from the response
  2. Token exchangePOST /v1/auth/agent-token with the agent's ocv_ key → get a JWT
  3. Operate → use the JWT to submit transactions, sign messages, or read secrets
  4. The platform never needs a "delegation token" — the agent authenticates directly with its own key

Template Spec Reference

The spec field is a JSON object with five top-level keys: vault, agents, policies, runtimes, and automations. All are optional — include only what you need.

vault

Creates a single vault for the user.

FieldTypeDefaultDescription
namestring"main"Vault name
descriptionstring""Vault description
{
"vault": {
"name": "prod-secrets",
"description": "Production API keys and credentials"
}
}

agents

Array of agent definitions. Each entry creates one agent with an auto-generated ocv_ API key.

FieldTypeDefaultDescription
namestring"primary"Agent name
descriptionstring""Agent description
intents.enabledbooleanfalseEnable the Intents API (transaction signing)
shroud_enabledbooleanfalseRoute LLM traffic through Shroud TEE
shroud_configobjectnullPer-agent Shroud policy (PII, injection thresholds, etc.)
{
"agents": [
{
"name": "trading-bot",
"description": "Executes DeFi trades",
"intents": { "enabled": true },
"shroud_enabled": true,
"shroud_config": {
"pii_policy": "redact",
"injection_threshold": 0.7,
"allowed_providers": ["openai", "anthropic"],
"enable_secret_redaction": true
}
}
]
}
intents vs intents_api_enabled

In the template spec, use "intents": { "enabled": true } (nested object). This is different from the direct agent creation API which uses "intents_api_enabled": true (flat boolean). The bootstrap engine translates between the two formats.

Multi-agent templates

Templates with multiple agents in the agents array now correctly provision all agents. Earlier versions only created the first agent — this has been fixed.

policies

Array of access policies linking agents to vault paths.

FieldTypeDefaultDescription
principal_refstringfirst agentReference to the agent. Use "agents.primary" for the first agent.
vault_refstringcreated vaultReference to the vault. Use "vault" for the template-created vault.
pathsstring[]["**"]Glob patterns for secret paths the agent can access
permissionsstring[]["read", "write"]Permission set: read, write, rotate
conditionsobject{}Optional conditions (IP allowlist, time windows)
{
"policies": [
{
"principal_ref": "agents.primary",
"vault_ref": "vault",
"paths": ["api-keys/*", "keys/*"],
"permissions": ["read", "write"]
},
{
"principal_ref": "agents.primary",
"vault_ref": "vault",
"paths": ["config/**"],
"permissions": ["read"],
"conditions": {
"ip_allowlist": ["10.0.0.0/8"]
}
}
]
}

runtimes (v0.44+)

Array of runtime definitions. Each entry creates a managed container for the agent.

FieldTypeDefaultDescription
namestringrequiredRuntime name
presetstring"small"Compute preset: small, medium, large, small-cc, medium-cc, large-cc
imagestring""Container image
expose_httpbooleanfalseEnable public URL
{
"runtimes": [
{
"name": "trading-runtime",
"preset": "medium",
"image": "ghcr.io/myapp/agent:latest",
"expose_http": true
}
]
}

automations (v0.44+)

Array of automation definitions. Each entry creates a scheduled, webhook-triggered, or event-driven workflow.

FieldTypeDefaultDescription
namestringrequiredAutomation name
trigger_typestring"manual"cron, webhook, event, or manual
cron_exprstringRequired for cron triggers
workflow_specobjectrequiredWorkflow step definitions
{
"automations": [
{
"name": "nightly-rotate",
"trigger_type": "cron",
"cron_expr": "0 0 * * *",
"workflow_spec": {
"steps": [
{ "type": "rotate_generate", "params": { "length": 32 } }
]
}
}
]
}

Bootstrapped runtime and automation IDs are tracked on the platform_user_connections record (runtime_ids, automation_ids).


Full Template Example

A complete template for a DeFi trading platform with Shroud inspection, Intents API, and multi-chain signing keys:

{
"name": "defi-trading-template",
"spec": {
"vault": {
"name": "trading-vault",
"description": "Keys and credentials for automated trading"
},
"agents": [
{
"name": "trade-executor",
"description": "Executes on-chain trades via Intents API",
"intents": { "enabled": true },
"shroud_enabled": true,
"shroud_config": {
"pii_policy": "redact",
"injection_threshold": 0.7,
"enable_secret_redaction": true,
"allowed_providers": ["openai", "anthropic"],
"max_requests_per_minute": 60,
"daily_budget_usd": 50
}
}
],
"signing_keys": [
{ "chain": "ethereum" },
{ "chain": "solana" }
],
"policies": [
{
"principal_ref": "agents.primary",
"vault_ref": "vault",
"paths": ["keys/*", "api-keys/*"],
"permissions": ["read"]
},
{
"principal_ref": "agents.primary",
"vault_ref": "vault",
"paths": ["config/**"],
"permissions": ["read", "write"]
}
]
}
}

Redirect URIs & "Sign in with 1Claw"

If your platform app uses the OAuth consent flow ("Sign in with 1Claw"), you need to register allowed redirect URIs. These are the URLs that 1Claw will redirect users back to after login/consent.

Adding Redirect URIs

Dashboard: Go to Platform → your app → Settings tab → Redirect URIs section. Add each callback URL (e.g. https://myapp.com/callback).

API:

curl -X PATCH "https://api.1claw.xyz/v1/platform/apps/APP_ID" \
-H "Authorization: Bearer YOUR_USER_JWT" \
-H "Content-Type: application/json" \
-d '{
"redirect_uris": [
"https://myapp.com/callback",
"http://localhost:3000/callback"
]
}'

SDK:

await client.platform.updateApp(appId, {
redirect_uris: [
"https://myapp.com/callback",
"http://localhost:3000/callback",
],
});
localhost is allowed

Per RFC 8252 §7.3, http://localhost (any port) is allowed for development. No HTTPS required for loopback addresses.

OAuth Flow (Sign in with 1Claw)

For sign-in (OIDC tokens), use scopes like openid profile email:

  1. Your app redirects users to:
    https://1claw.xyz/oauth/authorize?client_id=YOUR_SLUG&redirect_uri=https://myapp.com/callback&response_type=code&scope=openid%20email&state=RANDOM&code_challenge=...&code_challenge_method=S256
  2. The user sees the 1Claw consent page and approves.
  3. 1Claw redirects back to your redirect_uri with an authorization code.
  4. Your backend exchanges the code for tokens via POST /v1/oauth/token (send application/x-www-form-urlencoded or JSON) with the matching code_verifier.
PKCE is required for sign-in

Standard OAuth code grants require S256 PKCE (code_challenge on authorize, code_verifier on token exchange).

Complete working example

See examples/sign-in-with-1claw/ for a minimal, copy-pasteable demo (plain HTML + vanilla JS, no build step) that implements this entire flow.

If you use scope=link on /oauth/authorize, 1Claw does not issue an authorization code. After consent, the redirect is:

https://myapp.com/callback?linked=true&connection_id=UUID&state=RANDOM

Retry POST /v1/platform/users/upsert — no token exchange step. Prefer the dashboard link URL from link_required.authorize_url (/connect/{slug}/link) for the same behavior without OAuth parameters.

client_id is your app slug, not the UUID

The client_id parameter must be your platform app's slug (e.g. cubeverse), not the app UUID. You set the slug when creating the app. If you pass the UUID, you'll get "Unknown client_id". Find your slug in the dashboard at Platform → your app → Details.

Cross-Org User Linking

When you call POST /v1/platform/users/upsert and the user already exists in a different organization, the API returns 409 Conflict with a link_required response:

{
"link_required": {
"status": "link_required",
"reason": "user_exists_in_other_org",
"authorize_url": "https://1claw.xyz/connect/cubeverse/link?login_hint=user@example.com&return_to=https://myapp.com/callback",
"app_slug": "cubeverse"
}
}

Do not treat this as an error. Redirect the user's browser to link_required.authorize_url. They sign in (if needed), approve the connection, and are sent back to your return_to URL with ?linked=true&connection_id=.... Then retry upsert — it will succeed.

Register redirect URIs first

The link flow sends users back to your first registered redirect_uri unless you pass return_to on upsert. Add your callback URL under Platform → your app → Settings → Redirect URIs.

Do not show a generic error for link_required

If your app surfaces cross_org_link_incomplete, you are detecting the 409 but not redirecting. Send the user to authorize_url instead.


Auth Modes

Set auth_mode when creating your platform app:

ModeDescription
silentUsers are provisioned without sign-in. Best for bot-first platforms (Telegram, Discord). The claim_url is still returned — share it so users can manage their vault in the dashboard.
user_signinUsers must sign in to 1Claw before claiming. Best for web apps where users already have accounts.
configurableLet the operator choose per-user at bootstrap time.

Billing Models

ModelDescription
platform_paysAll API usage is billed to the platform's subscription.
user_paysEach connected user is billed individually.
hybridPlatform covers base usage; overages billed to users.

signing_keys

Array of blockchain signing keys to auto-provision for the first agent at bootstrap time. Each entry generates a keypair, stores the private key in the __agent-keys vault, and records the public key on the agent. Requires at least one agent with intents.enabled: true.

FieldTypeDescription
chainstringBlockchain name: ethereum, bitcoin, solana, xrp, cardano, tron
{
"signing_keys": [
{ "chain": "ethereum" },
{ "chain": "solana" }
]
}
tip

Signing keys are provisioned server-side during bootstrap — the platform operator never sees the private keys, and no user interaction is required. The plt_ key cannot read signing keys across the org boundary, maintaining custody separation.


Resource Grants (User-Side)

After a user claims their bootstrapped resources, they can grant your platform app access to additional vaults and agents beyond what the template provisioned. This is useful when your users have pre-existing 1Claw resources they want to connect.

How It Works

  1. Your app redirects the user to the 1Claw grant page:
    https://1claw.xyz/connect/{your-slug}/grant?connection={connection_id}
  2. The user selects which vaults and agents to share.
  3. Your backend can query the grants to discover what access it has.

API

Grant resources (user-authenticated, 1ck_ key):

curl -X POST "https://api.1claw.xyz/v1/platform/connections/CONNECTION_ID/grant" \
-H "Authorization: Bearer 1ck_USER_KEY" \
-H "Content-Type: application/json" \
-d '{
"vault_ids": ["vault-uuid-1", "vault-uuid-2"],
"agent_ids": ["agent-uuid-1"]
}'

List active grants:

curl "https://api.1claw.xyz/v1/platform/connections/CONNECTION_ID/grants" \
-H "Authorization: Bearer 1ck_USER_KEY"

Revoke a grant:

curl -X DELETE "https://api.1claw.xyz/v1/platform/connections/CONNECTION_ID/grants/GRANT_ID" \
-H "Authorization: Bearer 1ck_USER_KEY"

SDK

// User-authenticated client (1ck_ key)
const userClient = new OneclawClient({ apiKey: "1ck_user_key" });

// Grant access
const { data } = await userClient.platform.grantAccess(connectionId, {
vault_ids: ["vault-uuid"],
agent_ids: ["agent-uuid"],
});

// List grants
const { data: grants } = await userClient.platform.listGrants(connectionId);

// Revoke
await userClient.platform.revokeGrant(connectionId, grantId);

Dashboard

Users can manage grants from Settings → Connected Apps — each app shows shared resource counts with expandable grant panels and per-grant revoke buttons.

tip

Resource grants are always user-initiated. Platform operators cannot grant themselves access — only the connected user can share their resources. Grants are instantly revocable.


Platform Audit

Track all platform-related events for your app:

curl "https://api.1claw.xyz/v1/platform/apps/APP_ID/audit" \
-H "Authorization: Bearer plt_YOUR_KEY"

Returns platform.* audit events (app creation, user provisioning, bootstrap, template changes).


Key Rotation

Rotate your platform API key at any time. The old key is immediately invalidated.

curl -X POST "https://api.1claw.xyz/v1/platform/apps/APP_ID/rotate-key" \
-H "Authorization: Bearer YOUR_USER_JWT" \
-H "Content-Type: application/json" \
-d '{ "api_key_expires_at": "2027-01-01T00:00:00Z" }'

Response:

{
"api_key": "plt_NEW_KEY_HERE",
"api_key_prefix": "plt_aBcDeFgH",
"api_key_expires_at": "2027-01-01T00:00:00+00:00"
}

The api_key_expires_at field is optional. Omit it for a key that never expires.


Marketplace

List approved platform apps in the public marketplace:

curl "https://api.1claw.xyz/v1/platform/marketplace"

Returns apps with category, tags, screenshots, and pricing_summary. No authentication required. The dashboard exposes this at /marketplace.


App Stats

Get connected user counts and bootstrap metrics for your app:

curl "https://api.1claw.xyz/v1/platform/apps/APP_ID/stats" \
-H "Authorization: Bearer plt_YOUR_KEY"

Returns connected_user_count, bootstrap_count, and active_connections.


Platform Webhook Events

Platform apps can subscribe to lifecycle events via webhooks. The following platform-specific events are available:

EventDescription
platform.user.connectedA new user was connected to your app
platform.user.disconnectedA user disconnected from your app
platform.bootstrap.completedBootstrap finished for a connected user
platform.grant.createdA user granted your app access to resources
platform.grant.revokedA user revoked a resource grant
platform.user.claimedUser claimed bootstrapped resources

Webhook signing secrets

Org webhook HMAC secrets are returned once at create time. Rotate by recreating the webhook, or use POST /v1/platform/apps/{app_id}/rotate-webhook-secret for platform app delivery secrets.

curl -X POST "https://api.1claw.xyz/v1/platform/apps/APP_ID/rotate-webhook-secret" \
-H "Authorization: Bearer YOUR_USER_JWT"

The new secret is returned once — store it securely. All subsequent deliveries use the new X-Webhook-Signature HMAC.


Platform Rate Limiting

Per-app rate limits are enforced on all Platform API endpoints. Set max_requests_per_minute when creating or updating your app:

curl -X PATCH "https://api.1claw.xyz/v1/platform/apps/APP_ID" \
-H "Authorization: Bearer YOUR_USER_JWT" \
-H "Content-Type: application/json" \
-d '{ "max_requests_per_minute": 120 }'

Requests exceeding the limit return 429 Too Many Requests.


Platform Onboarding Wizard

The dashboard includes a step-by-step onboarding wizard at /platform/wizard that walks you through creating a platform app, defining a bootstrap template, and provisioning your first user. This is the fastest way to get started if you prefer a guided UI over the API.


Spend Policies (Embedded Wallets)

If your platform offers treasury wallets to end-users, spend policies let you set guardrails on wallet sends and swaps.

Create an App-Level Default Policy

curl -X POST "https://api.1claw.xyz/v1/platform/apps/APP_ID/spend-policies" \
-H "Authorization: Bearer YOUR_USER_JWT" \
-H "Content-Type: application/json" \
-d '{
"max_value_per_tx_eth": "0.5",
"daily_limit_eth": "2.0",
"allowed_chains": ["ethereum", "base"],
"max_transactions_per_day": 50
}'

Per-User Override

Override the app default for a specific connected user:

curl -X PUT "https://api.1claw.xyz/v1/platform/connections/CONNECTION_ID/spend-policy" \
-H "Authorization: Bearer YOUR_USER_JWT" \
-H "Content-Type: application/json" \
-d '{
"max_value_per_tx_eth": "1.0",
"daily_limit_eth": "5.0"
}'

Check Effective Policy (User-Side)

End-users can see what policy applies to them:

curl "https://api.1claw.xyz/v1/treasury/wallets/spend-policy" \
-H "Authorization: Bearer USER_JWT"

Available Policy Fields

FieldTypeDescription
to_allowliststring[]Only allow sends to these addresses
to_denyliststring[]Block sends to these addresses
max_value_per_tx_ethstringMax value per transaction (ETH)
daily_limit_ethstringRolling 24h spend cap (ETH)
allowed_chainsstring[]Restrict to these chains
allowed_tokensstring[]Restrict to these token contracts
max_transactions_per_dayintegerMax sends per UTC day

Embedded Wallet Integration

Platform apps can provide passwordless wallet experiences to end-users using Email OTP, social login, or passkeys.

Email OTP (Passwordless)

# 1. Send OTP
curl -X POST "https://api.1claw.xyz/v1/auth/email-otp/send" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"platform_app_id": "YOUR_APP_UUID"
}'

# 2. Verify OTP → returns JWT + wallet address
curl -X POST "https://api.1claw.xyz/v1/auth/email-otp/verify" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"code": "123456",
"platform_app_id": "YOUR_APP_UUID",
"auto_provision_chains": ["ethereum", "solana"]
}'
# → { "token": "eyJ...", "user_id": "...", "wallet_address": "0x..." }

Social Login

curl -X POST "https://api.1claw.xyz/v1/auth/social-login" \
-H "Content-Type: application/json" \
-d '{
"provider": "google",
"id_token": "GOOGLE_ID_TOKEN",
"auto_provision_chains": ["ethereum"]
}'

Supported providers: google, apple, discord. Discord uses an authorization code flow (pass the code as id_token with oauth_redirect_uri).

React Widget

For the fastest integration, use the @1claw/wallet-react package:

import { OneclawEmbeddedWallet } from "@1claw/wallet-react";

function App() {
return (
<OneclawEmbeddedWallet
appId="your-slug"
theme="dark"
chains={["ethereum", "solana"]}
socialProviders={["google", "discord"]}
features={["send", "swap", "receive", "buy"]}
/>
);
}

Delegation

Platform apps can perform ongoing CRUD operations on connected user resources via delegated access. Users opt in per-connection; the platform's plt_ key then acts on behalf of the user within scoped boundaries.

Enabling delegation

Users toggle delegation for a specific platform app connection:

PATCH /v1/platform/connected-apps/{connectionId}
{ "delegation_enabled": true, "delegation_scopes": ["secrets:read", "secrets:write"] }

Using delegated access

The platform sends the X-Platform-Connection header with its plt_ key:

curl -X GET "https://api.1claw.xyz/v1/vaults" \
-H "Authorization: Bearer plt_YOUR_KEY" \
-H "X-Platform-Connection: CONNECTION_ID"

Auth middleware resolves the caller as principal_type: "platform_delegated" with scoped permissions.

Available scopes

ScopeAccess
vaults:read / vaults:writeVault CRUD
agents:read / agents:writeAgent CRUD
secrets:read / secrets:writeSecret CRUD
automations:*Automation management
runtimes:*Runtime management
memory:read / memory:writeAgent memory CRUD
chat:read / chat:writeAgent chat conversations

Scope enforcement

Delegation scopes are enforced on 4 handler groups: secrets, policies, bindings, and discovery. Disconnected connections (status disconnected) are rejected with 403.

SDK

const scoped = client.platform.withConnection(connectionId);
const vaults = await scoped.listVaults();

Delegation log

GET /v1/platform/connected-apps/{connectionId}/delegation-log

Current Limitations

  • plt_ keys can see user metadata but cannot directly access user signing keys (GET /v1/agents/{id}/signing-keys). The org boundary prevents cross-org reads.

Security

  • OIDC audience enforcement: Platform apps can set oidc_audience to restrict which JWT audiences are accepted during OIDC user provisioning. When set, JWTs with a mismatched aud claim are rejected.
  • JWKS SSRF prevention: The oidc_jwks_url field is validated against private CIDRs, cloud metadata endpoints, and localhost to prevent SSRF attacks.
  • Cross-org binding protection: upsert_user enforces that the user belongs to the same org as the platform app.

SDK Usage

import { OneclawClient } from "@1claw/sdk";

const client = new OneclawClient({
baseUrl: "https://api.1claw.xyz",
apiKey: "plt_YOUR_KEY",
});

// Create a template
const template = await client.platform.createTemplate(appId, {
name: "default-template",
spec: {
vault: { name: "user-vault" },
agents: [{ name: "bot", intents: { enabled: true } }],
policies: [{ principal_ref: "agents.primary", vault_ref: "vault", paths: ["**"] }],
},
});

// Provision + bootstrap a user
const user = await client.platform.upsertUser({
email: "user@example.com",
external_subject: "tg:12345",
});
const result = await client.platform.bootstrapUser(user.data.connection_id, {
template_id: template.data.id,
});
console.log("Claim URL:", result.data.claim_url);
console.log("Agent ID:", result.data.summary.agent_id);
console.log("Agent API Key:", result.data.summary.agent_api_key); // one-time — store securely

Python

from oneclaw import OneclawClient

client = OneclawClient(
base_url="https://api.1claw.xyz",
api_key="plt_YOUR_KEY",
)

# Create a template
template = client.platform.create_template(app_id, {
"name": "default-template",
"spec": {
"vault": {"name": "user-vault"},
"agents": [{"name": "bot", "intents": {"enabled": True}}],
"signing_keys": [{"chain": "ethereum"}],
"policies": [{"principal_ref": "agents.primary", "vault_ref": "vault", "paths": ["**"]}],
},
})

# Provision + bootstrap a user
user = client.platform.upsert_user({
"email": "user@example.com",
"external_subject": "tg:12345",
})
result = client.platform.bootstrap_user(user["connection_id"], {
"template_id": template["id"],
})
print("Claim URL:", result["claim_url"])
print("Agent ID:", result["summary"]["agent_id"])
print("Agent API Key:", result["summary"]["agent_api_key"]) # one-time — store securely
print("Signing keys:", result["summary"]["signing_keys"])

# Rotate platform key
rotated = client.platform.rotate_key(app_id, {
"api_key_expires_at": "2027-01-01T00:00:00Z",
})
print("New key:", rotated["api_key"])

# Create a spend policy
policy = client.platform.create_spend_policy(app_id, {
"max_value_per_tx_eth": "0.5",
"daily_limit_eth": "2.0",
"allowed_chains": ["ethereum", "base"],
})

Complete Endpoint Reference

MethodPathAuthDescription
POST/v1/platform/appsUser JWTRegister a platform app (returns plt_ key one-time)
GET/v1/platform/appsUser JWTList platform apps for org
GET/v1/platform/apps/{id}User JWTGet platform app details
PATCH/v1/platform/apps/{id}User JWTUpdate platform app
DELETE/v1/platform/apps/{id}User JWTDelete platform app
POST/v1/platform/apps/{id}/rotate-keyUser JWTRotate plt_ API key
POST/v1/platform/apps/{id}/templatesUser JWTCreate bootstrap template
GET/v1/platform/apps/{id}/templatesUser JWTList templates
PATCH/v1/platform/apps/{id}/templates/{tid}User JWTUpdate template
DELETE/v1/platform/apps/{id}/templates/{tid}User JWTDelete template
POST/v1/platform/users/upsertplt_ keyProvision or find user
POST/v1/platform/connections/{id}/bootstrapplt_ keyBootstrap resources from template
POST/v1/platform/connections/{id}/reissue-claimplt_ keyReissue expired claim URL
GET/v1/platform/claim/{token}None (public)Preview claim token
POST/v1/platform/claim/{token}None (public)Redeem claim token
GET/v1/platform/apps/{id}/usersplt_ keyList connected users
GET/v1/platform/apps/{id}/auditUser JWT or plt_Platform audit events
GET/v1/platform/connected-appsUser JWTList apps connected to calling user
DELETE/v1/platform/connected-apps/{id}User JWTDisconnect from a platform app
POST/v1/platform/connections/{id}/grantUser JWTGrant vault/agent access to app
GET/v1/platform/connections/{id}/grantsUser JWTList active grants
DELETE/v1/platform/connections/{id}/grants/{gid}User JWTRevoke a grant
POST/v1/platform/apps/{id}/spend-policiesUser JWTCreate app-level spend policy
GET/v1/platform/apps/{id}/spend-policiesUser JWTList spend policies
DELETE/v1/platform/apps/{id}/spend-policies/{pid}User JWTDeactivate spend policy
PUT/v1/platform/connections/{id}/spend-policyUser JWTSet per-user spend policy override
GET/v1/treasury/wallets/spend-policyUser JWTView effective policy for calling user
GET/v1/platform/marketplaceNone (public)List approved apps in the marketplace
GET/v1/platform/apps/{id}/statsplt_ key or User JWTApp stats (connected users, bootstraps)
POST/v1/platform/apps/{id}/rotate-webhook-secretUser JWTRotate platform app webhook HMAC secret