> ## Documentation Index
> Fetch the complete documentation index at: https://docs.beta.b3os.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Call actions

Find a marketplace action by describing what it should do, read its schemas, and call it with a stored connection, from code, a routine or an agent.

## Search

`GET /v1/actions/search` takes words and returns references ranked for agents:

```bash
curl -s -G https://beta-api.b3os.org/v1/actions/search \
  -H "Authorization: Bearer $B3OS_API_KEY" \
  --data-urlencode "q=create a github issue" -d limit=5
```

```json
{
  "object": "list",
  "data": [
    {
      "ref": "io.b3os/github/create-issue@1.2.0",
      "pack": "io.b3os/github",
      "title": "Create an issue",
      "description": "Open an issue in a repository. Returns the issue number and URL.",
      "effects": "write",
      "connections": ["api"],
      "mcp_name": "github__create-issue",
      "score": 0.92
    }
  ],
  "next_cursor": null
}
```

The values are illustrative. Narrow the search with `pack` (`io.b3os/github`) or `effects` (`read`, for example).

## Describe

`GET /v1/actions/describe?ref=io.b3os/github/create-issue@1` returns everything needed to call the action: `input_schema` and `output_schema` (JSON Schema 2020-12), the effect class, MCP annotations, the action as an MCP tool definition, and the connections it needs, each with its key and type (`bearer`, `api_key`, `basic` or `oauth2`). A major version such as `@1` resolves to the newest matching release, and the response names the exact one.

## Call

**curl**

```bash
    curl -s https://beta-api.b3os.org/v1/actions/call \
      -H "Authorization: Bearer $B3OS_API_KEY" \
      -H "Idempotency-Key: 7c1e2f6a-1d43-4a8e-b2a5-3f0c9d5e8b11" \
      -H "Content-Type: application/json" \
      -d '{"ref": "io.b3os/github/create-issue@1",
           "input": {"owner": "acme", "repo": "web", "title": "Checkout button misaligned on mobile"},
           "connections": {"api": "conn_01m5t6cbgdjs1ketgcqwa2sne8"}}'
    ```
  **TypeScript**

```ts
    import { B3OS } from "@b3os/sdk";

    const b3os = new B3OS();
    const result = await b3os.actions.call(
      {
        ref: "io.b3os/github/create-issue@1",
        input: { owner: "acme", repo: "web", title: "Checkout button misaligned on mobile" },
        connections: { api: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },
      },
      { idempotencyKey: crypto.randomUUID() },
    );
    console.log(result.ref, result.output);
    ```
  **Python**

```python
    import uuid
    from b3os import B3OS

    b3os = B3OS()
    result = b3os.actions.call(
        {
            "ref": "io.b3os/github/create-issue@1",
            "input": {"owner": "acme", "repo": "web", "title": "Checkout button misaligned on mobile"},
            "connections": {"api": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},
        },
        idempotency_key=str(uuid.uuid4()),
    )
    print(result["ref"], result["output"])
    ```
  **MCP**

```text
    Open an issue in acme/web titled "Checkout button misaligned on mobile".
    ```

    The agent calls `actions_search`, then `actions_describe` for the best match, then `actions_call` with the input. Packs you expose directly (`--packs github` for the local server, `?packs=github` on the remote URL) appear as their own tools, such as `github__create-issue`. `actions_call` takes no credential: it uses your vault connections.

```json
{
  "object": "action_result",
  "ref": "io.b3os/github/create-issue@1.2.0",
  "output": { "number": 1423, "url": "https://github.com/acme/web/issues/1423" },
  "upstream_status": 201,
  "timing": { "validate_ms": 0.03, "inject_ms": 0.02, "upstream_ms": 412.6, "map_ms": 0.04, "total_ms": 412.8 }
}
```

The values are illustrative. `ref` is the exact version that ran. `timing` separates B3OS's own work (validating the input, injecting the credential, mapping the response) from the time the upstream service took.

## Connections

`connections` maps each of the action's connection keys to a vault connection ID. Leave a key out and B3OS uses your project's only connection for that pack and key; with several, name one. Naming a connection needs `vault:run` on top of `actions:run`. [Vault](/vault) explains how to store one.

The credential is added at the egress proxy, only for the hosts the pack declares for that connection. The call fails with [`connection_required`](/errors#connection_required) when the action needs a connection and none is available to the caller, and with [`connection_mismatch`](/errors#connection_mismatch) when the connection's type, pack or hosts do not fit the action.

## Retries and idempotency

B3OS retries `pure` and `read` actions on its own. It never retries `write` or `irreversible` actions for you: send an `Idempotency-Key` with them, as above, so that your own retry after a timeout returns the stored result instead of acting twice. Packs that support it pass the key on to the service.

## When it fails

| Code | Status | Meaning |
|---|---|---|
| [`action_input_invalid`](/errors#action_input_invalid) | 422 | The input does not match the input schema; `errors[]` points at the field, such as `/input/title` |
| [`upstream_error`](/errors#upstream_error) | 502 | The service answered with an error. `upstream_status` has its status and `pack_error` the pack's reading of it |
| [`action_output_invalid`](/errors#action_output_invalid) | 502 | The service answered, but the result does not match the output schema; report it to the pack publisher |
| [`action_not_found`](/errors#action_not_found), [`pack_not_found`](/errors#pack_not_found) | 404 | No such action or pack on the node |
| [`connection_required`](/errors#connection_required) | 422 | The action needs a credential you have not supplied or may not use |

## From routines and sandboxes

- In a routine, an action is a step: `uses: io.b3os/github/create-issue@1` with the input as `with`, and the connections bound with `connections`. A major version is pinned when the routine version is published. See [definitions](/routines/definitions).
- In a sandbox, grant `actions:run` and `vault:run`, and list the connections in `api.connections`; code inside then calls `http://127.0.0.1:7700/v1/actions/call` with no key, and the credential never enters the sandbox. See [agents in sandboxes](/sandboxes/agents).