Skip to content
1Claw Academy
Curriculum/Compliance & Operations4 minAdvanced · Lesson 6 of 11

Platform API multitenancy

Register a platform app and bootstrap a user's vault, agent, and signing keys from a template.

The Platform API lets you build multi-tenant products on 1Claw. You register an app, define a bootstrap template, provision users, and hand each user a claim URL, all without your platform being able to read their secrets.

Concept

The Platform API requires a Pro or higher plan. Save the returned plt_ key when you create the app; it is not shown again.

  • Templates create a vault, agents, and policies in one atomic operation
  • In templates use nested intents: { enabled: true }, not the flat intents_api_enabled
  • signing_keys in the template are provisioned server-side; the plt_ key cannot read them across the org boundary
  • Bootstrap returns claim_url plus a one-time agent_api_key in summary
  1. 1

    Exchange your API key for a user JWT. Replace 1ck_your_key_here with your real key.

    bash
    export ONECLAW_API_KEY="1ck_your_key_here"
    export USER_JWT=$(curl -s -X POST https://api.1claw.co/v1/auth/api-key-token \
      -H "Content-Type: application/json" \
      -d "{\"api_key\":\"$ONECLAW_API_KEY\"}" | jq -r .access_token)
  2. 2

    Register a platform app with your user JWT. Silent auth skips the interactive consent screen; use 'interactive' if your platform requires explicit user approval.

    bash
    curl -X POST "https://api.1claw.co/v1/platform/apps" \
      -H "Authorization: Bearer $USER_JWT" -H "Content-Type: application/json" \
      -d '{ "name": "My DeFi Platform", "slug": "my-defi", "auth_mode": "silent" }'
  3. 3

    Save the plt_ key now: it is shown only once. Use $PLT_KEY as Bearer in later steps.

    bash
    # Save from the response:
    export APP_ID="paste-app-id-from-response"
    export PLT_KEY="paste-plt-key-from-response"
  4. 4

    Create a bootstrap template. Note intents is a nested object here.

    bash
    curl -X POST "https://api.1claw.co/v1/platform/apps/$APP_ID/templates" \
      -H "Authorization: Bearer $PLT_KEY" -H "Content-Type: application/json" \
      -d '{
        "name": "default-template",
        "spec": {
          "vault": { "name": "user-vault" },
          "agents": [{ "name": "defi-bot", "intents": { "enabled": true } }],
          "signing_keys": [{ "chain": "ethereum" }],
          "policies": [{ "principal_ref": "agents.primary", "vault_ref": "vault", "paths": ["keys/*"] }]
        }
      }'
  5. 5

    Save the template ID from the response.

    bash
    export TEMPLATE_ID="paste-template-id-from-response"
  6. 6

    Upsert a user.

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

    Save the connection_id from the upsert response.

    bash
    export CONNECTION_ID="paste-connection-id-from-response"
  8. 8

    Bootstrap the user from the template. The response has claim_url and a one-time summary.agent_api_key.

    bash
    BOOTSTRAP_RESPONSE=$(curl -s -X POST "https://api.1claw.co/v1/platform/connections/$CONNECTION_ID/bootstrap" \
      -H "Authorization: Bearer $PLT_KEY" -H "Content-Type: application/json" \
      -d '{ "template_id": "'$TEMPLATE_ID'" }')
  9. 9

    Share the claim_url with the user. It expires in 10 minutes; reissue with the reissue-claim endpoint if it lapses.

    bash
    echo "Claim URL: $(echo $BOOTSTRAP_RESPONSE | jq -r .claim_url)"
    # Format: https://1claw.co/connect/{slug}/claim/{token}
Watch out

For Sign in with 1Claw, the OAuth client_id must be your app slug (for example my-defi), not the app UUID. Passing the UUID returns Unknown client_id.

You now register a platform app and bootstrap a user with a vault, an intents-enabled agent, and server-side signing keys your platform cannot read, plus a claim URL to hand off.

Where this goes wrong in practice. Multi-tenancy failures are rarely subtle and almost always consequential, because the blast radius is every customer at once.

  • platform_locked is omitted at provisioning. It is far easier to set at creation than to retrofit once support workflows have grown to depend on operator reads.
  • The plt_ key reaches client code. It is a platform-wide credential; anywhere it appears in a browser bundle it is published to every visitor.
  • Bootstrap runs without an idempotency key. Retries then double-provision, and the second set of resources is attributed to the same connection.
  • Delegation scopes are set broadly during development. secrets:read added for debugging tends to stay, and it applies across every connected user rather than one.
  • Claim tokens are treated as durable links. They are one-time and expiring, so a token pasted into a ticket for later is a support case waiting to happen.
Watch out

Design support tooling around metadata, audit trails and error detail rather than values. Almost all real debugging needs to know that a secret exists and when it last changed, not what it is.

Decide

You are building a product on the Platform API. Your support team needs to help users debug their integrations, which means seeing the secrets those users store in the vaults you provision for them.

How should you design this?

Check your understanding

3 questions
1

In a bootstrap template, how do you enable the Intents API on an agent?

2

What must client_id be for Sign in with 1Claw?

3

Why can't the platform read a bootstrapped user's signing keys?