Skip to content
1Claw Academy
Curriculum/Agents & Access Control4 minIntermediate · Lesson 7 of 15

Execution Intents and bindings

Let an agent call an external API through a binding, so the credential is injected server-side and never enters the model's context.

Policies control which secrets an agent can read. Execution Intents go further: the agent never reads the credential at all. You pre-configure a binding that holds the credential server-side, and the agent asks 1Claw to perform the call on its behalf.

  • The agent sends an intent (method, path, body), not a key.
  • 1Claw injects the credential, applies host and path allowlists, and makes the request.
  • The response comes back to the agent. The secret was never in its context window.
  • Revoking a binding cuts access instantly, with no secret to rotate.
Concept

This is the pattern 1claw.co leads with on its own homepage: credentials stay server-side and agents execute through bindings.

  1. 1

    Create an HTTP binding holding a Stripe key. The credential is stored server-side against the binding.

    bash
    curl -s -X POST https://api.1claw.co/v1/agents/$AGENT_ID/bindings \
      -H "Authorization: Bearer $ONECLAW_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "stripe-api",
        "binding_type": "http",
        "base_url": "https://api.stripe.com",
        "allowed_hosts": ["api.stripe.com"],
        "allowed_paths": ["/v1/balance", "/v1/charges"],
        "credential": { "type": "bearer", "value": "sk_live_..." }
      }'
  2. 2

    Prefer a live pointer over a copied credential. With credential_source vault_ref the value is resolved from the vault at execution time, so rotating the secret needs no binding update.

    bash
    curl -s -X POST https://api.1claw.co/v1/agents/$AGENT_ID/bindings \
      -H "Authorization: Bearer $ONECLAW_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "stripe-api-live",
        "binding_type": "http",
        "base_url": "https://api.stripe.com",
        "allowed_hosts": ["api.stripe.com"],
        "credential_source": { "type": "vault_ref", "vault_id": "'$VAULT_ID'", "path": "providers/stripe/key" }
      }'
  3. 3

    Test the binding before an agent depends on it. The binding subcommands take the agent id and the binding id as positional arguments, so list them first to get the id.

    bash
    1claw agent binding list $AGENT_ID
    1claw agent binding test $AGENT_ID $BINDING_ID
  4. 4

    Execute through it. This is what the agent calls: note there is no credential anywhere in the request.

    bash
    curl -s -X POST https://api.1claw.co/v1/agents/$AGENT_ID/execute \
      -H "Authorization: Bearer $AGENT_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "binding": "stripe-api", "intent_type": "http", "params": { "method": "GET", "path": "/v1/balance" } }'
  5. 5

    From the SDK, the same call reads the way the homepage example does.

    ts
    const result = await client.bindings.execute(agentId, {
      binding: "stripe-api",
      intent_type: "http",
      params: { method: "GET", path: "/v1/balance" },
    });
Tip

Binding types beyond HTTP: GraphQL (Pro), plus Postgres, MySQL, Redis, gRPC, SMTP, Cloud SDK, S3 and Custom on Team and above. Business+ can run execution inside the TEE with execution_surface set to tee.

Watch out

Set execution_require_tee on the agent to force every execution through Shroud AND block the agent's direct secret reads outright. It is the strongest form of this pattern, but it requires execution_intents_enabled first.

The Stripe binding above was hand-configured: base URL, allowed hosts, allowed paths, all chosen by you. Connectors are the same binding for common providers with those three decisions already made. Installing Gmail, Slack, GitHub, Notion or Discord is one call instead of four, but the interesting part is what that call refuses to let you skip.

  • The binding a connector install creates is scoped to that connector: gmail reaches gmail.googleapis.com/gmail/v1/ and nothing else, the same allowed_hosts and allowed_paths discipline you just configured by hand for Stripe.
  • That is the actual difference from a bare OAuth connection. An HTTP binding with no allowed_hosts has no host restriction, so a binding holding a user's Google token could be pointed anywhere; a connector's cannot.
  • Requested scopes may narrow a preset's list and may not extend it. A scope outside the preset is a 400, and so is dropping one the preset marks required.
  • Installed is not connected. A 201 means the binding exists; it holds no credential until the human finishes the OAuth round trip, and GET /v1/agents/{id}/connectors reports connected separately from installed.
Watch out

Installing a connector is human-only, the same rule as everything else in this lesson with a high blast radius. An agent calling the install endpoint for itself gets a 403: giving an agent reach into a third-party account starts a flow that runs in a person's browser, not an agent's context.

Decide

A binding holds a partner API key inline. The partner rotates the credential on a schedule, and every rotation breaks your agent until someone updates the binding by hand. A colleague proposes storing the key in the vault and giving the agent read access to that path, so it can fetch the current value before each call.

What do you do?

Check your understanding

4 questions
1

What is the main security advantage of executing through a binding rather than reading the secret?

2

Why choose credential_source vault_ref over an inline credential?

3

What does execution_require_tee do beyond forcing execution through Shroud?

4

You install the Gmail connector and request only the readonly scope, narrower than the preset's default list. What happens if you then try to widen it to a scope outside the preset entirely?