# Playbooks API primer for agents

> Give a coding agent the minimum complete contract for creating, polling, and consuming a Playbooks job safely.

Canonical guide: <https://api.blackpearl.com/docs/playbooks-api-primer>

Raw Markdown: <https://api.blackpearl.com/docs/raw/playbooks-api-primer.md>

OpenAPI: <https://api.blackpearl.com/v1/openapi.json>

Use this page with the canonical [OpenAPI document](https://api.blackpearl.com/v1/openapi.json). OpenAPI is authoritative for exact schemas; this primer is authoritative for the integration sequence and operational decisions.

## Contract at a glance

- Base URL: `BLACKPEARL_API_BASE_URL`, configured for the deployment.
- Authentication: `Authorization: Bearer $BLACKPEARL_API_KEY` on every `/v1` request.
- Create: `POST /v1/playbooks` using operation `create_playbook_v1_playbooks_post` and schema `PlaybookInput`.
- Poll: `GET /v1/jobs/{job_id}` using operation `get_job_v1_jobs__job_id__get`.
- Create response: `202 Accepted` with schema `PublicPlaybookJobResponse`.
- Create retry safety: send `Idempotency-Key`; matching retries replay the original response for 24 hours.
- Result: when the job succeeds, `result` is schema `PublicPlaybookResult`.
- Retention: `expires_at` is 30 days after creation; store results needed after that time.
- Errors: schema `ErrorResponse`; branch on `error.code`, not HTTP status alone.

Create separate projects and keys for development, staging, and production. Hosted development calls still perform real work and record real usage.

## Minimal request

Set the configured API URL and a server-side project key:

```bash
export BLACKPEARL_API_BASE_URL="https://api.blackpearl.com"
export BLACKPEARL_API_KEY="replace-with-your-project-key"
export BLACKPEARL_IDEMPOTENCY_KEY="playbook-$(date +%s)-$$"

curl --fail-with-body --silent --show-error \
  --request POST "$BLACKPEARL_API_BASE_URL/v1/playbooks" \
  --header "Authorization: Bearer $BLACKPEARL_API_KEY" \
  --header "Idempotency-Key: $BLACKPEARL_IDEMPOTENCY_KEY" \
  --header "Content-Type: application/json" \
  --data '{"target_company_domain":"acme.example"}'
```

Provide at least one of `target_company` or `target_company_domain`. A domain is a hostname without a scheme or path. Optional `tone` values are `consultative`, `direct`, and `technical`; `model` is currently `default`. Omit `sections` for the complete result.

Persist the idempotency key before the first request and the returned `id` immediately after success. If the connection is lost or a server response is ambiguous, repeat the exact POST with the same key within 24 hours. A matching request returns the original response without creating or billing another job. A changed request returns `idempotency_key_reused`; `idempotency_in_progress` means retry the same request and key after a short delay.

## Poll every terminal state

Wait 2 seconds before the first GET, then use exponential backoff with jitter capped at 10 seconds. Apply a caller deadline and retain the job ID so polling can resume later. A client timeout does not cancel server work.

| Status | Terminal | Action |
|---|---:|---|
| `queued` | No | Wait and poll again. |
| `running` | No | Optionally show `progress`, then poll again. |
| `succeeded` | Yes | Validate and consume the typed `result`. |
| `failed` | Yes | Stop and surface the job `error`. |
| `canceled` | Yes | Stop; there is no public cancel operation. |

On success, useful first outputs are `result.business_summary` and the first item in `result.sales_angles`. Collections are arrays. Research-derived scalar fields can be null; follow `PublicPlaybookResult` rather than inventing fallback fields.

## Errors, retries, and usage

- `rate_limit_exceeded` is transient. Wait at least the `Retry-After` value, then retry with bounded backoff and jitter.
- `quota_exceeded` and `budget_exceeded` also use status `429`, but require a reset or configuration change. Do not automatically retry them.
- Authentication, entitlement, and validation codes require the caller to change credentials, project state, access, or input.
- A safe GET may be retried for a documented transient `5xx`. Repeat an ambiguous create POST only with its original idempotency key and exact input.
- Polling is rate-limited but does not start another billed Playbooks generation. Inspect the completed job's `usage` and reconcile project totals through `GET /v1/usage`.
- A job or candidate read after `expires_at` returns `410 job_expired`. Create a new job if no client-stored result exists.

## Do not assume

- Do not assume a key prefix creates a sandbox.
- Do not assume a fixed completion-time SLA, fixed request-per-minute limit, or cancellation endpoint.
- Do not assume an idempotency key remains replayable after 24 hours or can be reused with different input.
- Do not wait only for `succeeded`; all three terminal states stop polling.
- Do not treat every `429` or `5xx` as retryable.
- Do not expose a project key in browser or mobile code.

For complete examples, use the [Quickstart](https://api.blackpearl.com/docs/getting-started). For field interpretation and recovery details, use [Playbooks](https://api.blackpearl.com/docs/playbooks), [Jobs and resumable polling](https://api.blackpearl.com/docs/jobs), [Errors and retry decisions](https://api.blackpearl.com/docs/errors), and [Limits, budgets, and usage](https://api.blackpearl.com/docs/usage-quotas).
