# BioFlow API documentation

The BioFlow public REST API lives at `https://app.getbioflow.com/v1`. It is available on
the Creator and Pro plans; requests authenticate with an API key created in
the developer console (https://app.getbioflow.com/dashboard/settings/developers).

## Quickstart

1. Create an API key in the developer console: https://app.getbioflow.com/dashboard/settings/developers
2. Call the API:

```bash
curl "https://app.getbioflow.com/v1/pages" \
  -H "Authorization: Bearer bf_live_YOUR_KEY"
```

3. You get a cursor page of your bio pages:

```json
{
  "data": [
    {
      "id": "pg_abc123",
      "title": "My links",
      "slug": "maya",
      "status": "PUBLISHED",
      "block_count": 6,
      "public_url": "https://getbioflow.com/maya",
      "updated_at": "2026-07-20T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## Authentication

Two equivalent header forms — send ONE of them on every request:

- `Authorization: Bearer bf_live_YOUR_KEY`
- `x-api-key: bf_live_YOUR_KEY`

Key prefixes: `bf_live_` keys are production keys; `bf_test_` keys are
restricted to read operations (writes get 403 `test_key_read_only`).

## Rate limits and quotas

| Plan | Monthly quota | Per-key burst |
| --- | --- | --- |
| Free | — (no API access) | — |
| Creator | 10,000 requests/mo | 60 requests/min |
| Pro | 100,000 requests/mo | 120 requests/min |

The quota counts successful /v1 requests per calendar month (UTC).
`GET /v1/usage` is free and reports current usage. Every response carries
IETF draft-11 `RateLimit` / `RateLimit-Policy` headers. Exceeding the burst
returns 429 `rate_limited`; exhausting the month returns 429
`quota_exhausted`; the Free plan gets 403 `feature_not_enabled`.

## Pagination

List endpoints return `{ "data": [...], "has_more": boolean, "next_cursor": string | null }`.
Pass `limit` (page size) and `after` (the previous response's
`next_cursor`) to walk the collection.

## Idempotency

Consequential POSTs accept an `Idempotency-Key` header (any unique string
per logical attempt). Retrying with the same key returns the original result
instead of repeating the action; reusing a key with a different request body
returns 422 `idempotency_key_reused`.

## Errors

Errors are RFC 9457 `application/problem+json` with a stable `code`:

- [`invalid_request`](https://getbioflow.com/docs/api/errors/invalid-request) (400): The request body or parameters failed validation
- [`invalid_api_key`](https://getbioflow.com/docs/api/errors/invalid-api-key) (401): Missing, malformed, unknown, disabled, or expired API key
- [`insufficient_scope`](https://getbioflow.com/docs/api/errors/insufficient-scope) (403): The API key was not granted the required scope
- [`feature_not_enabled`](https://getbioflow.com/docs/api/errors/feature-not-enabled) (403): The workspace plan does not include public API access
- [`test_key_read_only`](https://getbioflow.com/docs/api/errors/test-key-read-only) (403): Test-mode keys are restricted to read operations
- [`resource_not_found`](https://getbioflow.com/docs/api/errors/resource-not-found) (404): No resource with that ID exists in this workspace
- [`stale_snapshot`](https://getbioflow.com/docs/api/errors/stale-snapshot) (409): The draft changed since it was last read
- [`idempotency_in_progress`](https://getbioflow.com/docs/api/errors/idempotency-in-progress) (409): A request with this Idempotency-Key is still executing
- [`idempotency_key_reused`](https://getbioflow.com/docs/api/errors/idempotency-key-reused) (422): This Idempotency-Key was already used with a different request
- [`endpoint_verification_failed`](https://getbioflow.com/docs/api/errors/endpoint-verification-failed) (422): The webhook endpoint did not accept the signed test event
- [`endpoint_limit_reached`](https://getbioflow.com/docs/api/errors/endpoint-limit-reached) (422): The workspace webhook endpoint limit was reached
- [`rate_limited`](https://getbioflow.com/docs/api/errors/rate-limited) (429): Per-key rate limit exceeded
- [`quota_exhausted`](https://getbioflow.com/docs/api/errors/quota-exhausted) (429): Monthly API quota exhausted
- [`internal_error`](https://getbioflow.com/docs/api/errors/internal-error) (500): Something went wrong on our side

## Operations (22)

- [`GET /v1/analytics/summary`](https://getbioflow.com/docs/api/operations/get-analytics-summary): Get analytics summary
- [`GET /v1/contacts`](https://getbioflow.com/docs/api/operations/list-contacts): List contacts
- [`GET /v1/files`](https://getbioflow.com/docs/api/operations/list-files): List files
- [`GET /v1/pages`](https://getbioflow.com/docs/api/operations/list-pages): List pages
- [`POST /v1/pages`](https://getbioflow.com/docs/api/operations/create-page): Create a page
- [`DELETE /v1/pages/{page_id}`](https://getbioflow.com/docs/api/operations/delete-page): Delete a page
- [`GET /v1/pages/{page_id}`](https://getbioflow.com/docs/api/operations/get-page): Get a page
- [`PATCH /v1/pages/{page_id}`](https://getbioflow.com/docs/api/operations/update-page): Update a page draft
- [`POST /v1/pages/{page_id}/blocks`](https://getbioflow.com/docs/api/operations/add-block): Add a block
- [`DELETE /v1/pages/{page_id}/blocks/{block_id}`](https://getbioflow.com/docs/api/operations/remove-block): Remove a block
- [`POST /v1/pages/{page_id}/publish`](https://getbioflow.com/docs/api/operations/publish-page): Publish a page
- [`GET /v1/usage`](https://getbioflow.com/docs/api/operations/get-usage): Get API usage
- [`GET /v1/webhook-endpoints`](https://getbioflow.com/docs/api/operations/list-webhook-endpoints): List webhook endpoints
- [`POST /v1/webhook-endpoints`](https://getbioflow.com/docs/api/operations/create-webhook-endpoint): Create a webhook endpoint
- [`DELETE /v1/webhook-endpoints/{endpoint_id}`](https://getbioflow.com/docs/api/operations/delete-webhook-endpoint): Delete a webhook endpoint
- [`GET /v1/webhook-endpoints/{endpoint_id}`](https://getbioflow.com/docs/api/operations/get-webhook-endpoint): Get a webhook endpoint
- [`PATCH /v1/webhook-endpoints/{endpoint_id}`](https://getbioflow.com/docs/api/operations/update-webhook-endpoint): Update a webhook endpoint
- [`GET /v1/webhook-endpoints/{endpoint_id}/deliveries`](https://getbioflow.com/docs/api/operations/list-webhook-deliveries): List deliveries
- [`POST /v1/webhook-endpoints/{endpoint_id}/deliveries/{delivery_id}/resend`](https://getbioflow.com/docs/api/operations/resend-webhook-delivery): Resend a delivery
- [`POST /v1/webhook-endpoints/{endpoint_id}/replay`](https://getbioflow.com/docs/api/operations/replay-webhook-deliveries): Replay failed deliveries
- [`POST /v1/webhook-endpoints/{endpoint_id}/rotate-secret`](https://getbioflow.com/docs/api/operations/rotate-webhook-secret): Rotate the signing secret
- [`POST /v1/webhook-endpoints/{endpoint_id}/test`](https://getbioflow.com/docs/api/operations/test-webhook-endpoint): Send a test event

## More

- Interactive reference: https://getbioflow.com/docs/api/reference
- OpenAPI 3.1 spec: https://getbioflow.com/docs/api/openapi.json
- Webhooks guide: https://getbioflow.com/docs/api/webhooks
- AI/agent tooling: https://getbioflow.com/docs/ai
- Changelog: https://getbioflow.com/changelog
- TypeScript SDK (source): https://github.com/DevinoSolutions/bioflow-sdk — the npm package (@getbioflow/sdk) is landing shortly.
