---
title: Calling the API
---

# Calling the API

Use the public API directly when you want to script requests, debug payloads, or integrate bitdrift
from your own tooling. For a higher-level terminal workflow with managed authentication, start with
the [`bd` CLI quickstart](../cli/quickstart.md).

## Prefer the `bd` CLI for terminal workflows

If you are primarily working from the command line, start with the [`bd` CLI quickstart](../cli/quickstart.md).
The CLI is the protocol-independent path for authenticated public-API access: it handles user login
with `bd auth`, and it can mint API keys with `bd key create api`.

## Authentication

The public API supports both API keys and user session JWTs, including when you call it directly
outside the CLI.

| Authentication mode | Request header | Use it for |
| --- | --- | --- |
| **API key** | `x-bitdrift-api-key: <key>` | Automation and integrations with explicitly assigned permissions. |
| **User session JWT** | `Authorization: Bearer <session-jwt>` | Requests on behalf of a signed-in user, subject to that user's permissions. |

Use `bd key create api` to mint an API key and select its permissions, or refer to
[AdminService](../api/bitdrift_public_unary_admin_v1_AdminService.md#CreateKey) for the underlying
API surface. API keys are different from the SDK keys used to initialize Capture.

For interactive user authentication, the [`bd` CLI authentication flow](../cli/quickstart.md#authentication)
obtains and refreshes session JWTs for CLI requests. Direct callers can use a valid bitdrift session
JWT in the `Authorization` header; a refresh token is not a session JWT. Direct integrations must
manage token expiry and renewal themselves.

The examples below use API keys. To make the same request with a session JWT, replace the
`x-bitdrift-api-key` header with `Authorization: Bearer <session-jwt>`. Authentication does not
grant every API permission: the key or user must also be authorized for the method being called.

## Supported transports

Unary API methods are available through multiple transport layers.

| Transport | Notes |
| --- | --- |
| **HTTP/JSON (gRPC-over-JSON transcoding)** | Send an HTTP `POST` with `Content-Type: application/json` and a protobuf JSON request body to the RPC URL. No gRPC framing or generated client is required. |
| **Connect** | Use a Connect client with protobuf encoding. Supports unary and server-streaming methods. |
| **Native gRPC** | Use a gRPC client over HTTP/2. Supports unary and server-streaming methods. |

RPC URLs use the fully qualified public service name and method:
`https://api-public.bitdrift.io/<package>.<Service>/<Method>`. Use the exact URL shown on the
generated service page; the `unary` and `stream` segments in package names remain part of the URL.

## Server-streaming APIs

Server-streaming methods, including
[`BDTail/TailLogs`](../api/bitdrift_public_stream_bdtail_v1_BDTail.md#TailLogs) and
[`DebugService/DebugDevice`](../api/bitdrift_public_stream_device_debug_v1_DebugService.md#DebugDevice),
support native gRPC and Connect streaming only. They do not support gRPC-over-JSON or direct HTTP
JSON requests.

Use a generated gRPC or Connect client, or a streaming-capable tool such as `grpcurl` or `buf curl`.
Send one request, then continue reading response messages until the stream completes. A stream
can start successfully and later end with an error, so check its final RPC status as well as its
response messages. The generated API reference identifies each server-streaming method and links
to this transport guidance.

The **Device Debug** reference section also contains a separate unary `DebugService` for
device commands. Its methods do support gRPC-over-JSON. See
[Device debug](index.md#device-debug) for how the command and live-streaming services differ.

## Unary gRPC-over-JSON errors

When you call the JSON endpoint directly, application errors follow the same canonical gRPC status
model used by the native gRPC transport, but the JSON bridge maps that automatically into a normal
HTTP error response.

For RPC application errors on this surface, clients should look at:

- The HTTP status code.
- The JSON response body, which uses the Connect-style error shape with `code` and an optional
  descriptive `message`.

Authentication failures and other errors returned before the RPC handler may have an empty or
non-JSON body. Always check the HTTP status and response content type before assuming a JSON
error body is available.

Common cases look like this:

| HTTP status | Connect-style `code` | Typical meaning |
| --- | --- | --- |
| `400` | `invalid_argument` | The request could not be decoded or failed field validation. |
| `401` | `unauthenticated` | Authentication was missing or invalid; the response may omit the JSON error body. |
| `403` | `permission_denied` | The authenticated key or user lacks a required permission. |
| `404` | `not_found` | The requested resource or state record was not found. |
| `429` | `resource_exhausted` | A rate limit was reached. |

### Example error response

If you request hydration status when no hydration record exists, expect a not-found error.
This can also happen for a known session that has not been hydrated; it does not by itself prove
the session is unknown. See [Session Hydration](concepts/session-hydration.md).

```bash
curl -i -X POST https://api-public.bitdrift.io/bitdrift.public.unary.timeline.v1.TimelineService/GetHydrationStatus \
  -H "x-bitdrift-api-key: <key>" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "00000000-0000-0000-0000-000000000000"
  }'
```

Example response shape (the message text is illustrative):

```http
HTTP/1.1 404 Not Found
content-type: application/json
```

```json
{
  "code": "not_found",
  "message": "<descriptive error message>"
}
```

For JSON RPC errors, treat the HTTP status and the JSON `code` as the stable error contract,
not the human-readable `message`. Native gRPC clients should inspect the RPC status rather than
relying on the HTTP status alone.

## Call unary APIs from the command line

For quick exploration, you can call the same `ListWorkflows` method a few different ways:

=== "`curl`"

    ```bash
    curl -X POST https://api-public.bitdrift.io/bitdrift.public.unary.workflows.v1.WorkflowService/ListWorkflows \
      -H "x-bitdrift-api-key: <key>" \
      -H "Content-Type: application/json" \
      -d '{}'
    ```

    This is the gRPC-over-JSON surface used by the generated service-page examples.

=== "`grpcurl`"

    ```bash
    grpcurl \
      -H "x-bitdrift-api-key: <key>" \
      api-public.bitdrift.io:443 \
      bitdrift.public.unary.workflows.v1.WorkflowService.ListWorkflows
    ```

    Use `grpcurl` when you want to hit the native gRPC endpoint directly.

=== "`buf curl` (Connect)"

    ```bash
    buf curl \
      --reflect-header "*" \
      -H "x-bitdrift-api-key: <key>" \
      https://api-public.bitdrift.io/bitdrift.public.unary.workflows.v1.WorkflowService/ListWorkflows
    ```

    `buf curl` uses the Connect protocol by default. The tool accepts JSON input and encodes the
    request as protobuf on the wire.

=== "`buf curl` (gRPC)"

    ```bash
    buf curl \
      --reflect-header "*" \
      --protocol grpc \
      -H "x-bitdrift-api-key: <key>" \
      https://api-public.bitdrift.io/bitdrift.public.unary.workflows.v1.WorkflowService/ListWorkflows
    ```

    Use this when you want `buf curl` to target the native gRPC transport instead of Connect.

The `grpcurl` and `buf curl` examples use server reflection to discover the public schemas.
Reflection requests also require authentication. `grpcurl -H` sends the header to both reflection
and RPC requests; `buf curl --reflect-header "*"` forwards its `-H` headers to reflection.

## Generated clients

If you are integrating from application code, use the public schema definitions in
[`bitdriftlabs/api`](https://github.com/bitdriftlabs/api). The queryable API definitions documented
here live under `src/bitdrift/public`, and you can use that schema to generate the Connect or gRPC
clients that fit your environment.

## Find the right method first

The generated reference remains the source of truth for request and response shapes:

- [Services](../api/services.md) for callable methods and example payloads.
- [Types](../api/reference.md) for message and enum definitions.

If you mostly want a supported terminal workflow rather than hand-authoring requests, use the
[`bd` CLI docs](../cli/quickstart.md).
