# Message Templates

> Turn approved GTM context and prospect evidence into quality-gated, approval-only outreach copy.

Canonical guide: <https://api.blackpearl.com/docs/message-templates>

Raw Markdown: <https://api.blackpearl.com/docs/raw/message-templates.md>

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

Message Templates creates reusable structural email revisions and personalized prospect drafts. It is deliberately approval-only: every response reports zero messages enqueued and sent, and the capability has no provider-delivery, tracking, attachment, or CRM-write authority.

## Generate a template revision

`generate_message_template_revision_v1_message_template_revisions_generate_post` starts `POST /v1/message-template-revisions/generate`. Send an `Idempotency-Key`, a focused audience and objective, an approved offer or offer summary, and the desired next step.

```bash
curl --fail-with-body --silent --show-error \
  --request POST "$BLACKPEARL_API_BASE_URL/v1/message-template-revisions/generate" \
  --header "Authorization: Bearer $BLACKPEARL_API_KEY" \
  --header "Idempotency-Key: revops-template-20260717" \
  --header "Content-Type: application/json" \
  --data '{
    "template_key": "revops-first-touch",
    "name": "RevOps first touch",
    "language": "en-nz",
    "audience": "Revenue operations leaders at B2B software companies",
    "objective": "Start a relevant discovery conversation",
    "offer_summary": "Reduce manual forecasting work and surface account risk earlier",
    "desired_next_step": "a short comparison",
    "options": {
      "tone": "direct",
      "locale": "New Zealand English",
      "max_words": 90,
      "max_links": 0,
      "forbidden_claims": ["guaranteed outcomes"]
    },
    "reason": "Create the governed first-touch baseline for review."
  }'
```

The response is a typed asynchronous job with `type: "message_templates"`. Poll it as described in the [Jobs guide](https://api.blackpearl.com/docs/jobs). A successful result has `status: "DRAFT_READY"`, the next immutable revision, and a 100-point rubric covering relevance, evidence, clarity, recipient value, CTA, trust, and mobile readability. The server converts only its allowlisted placeholders into the closed template structure and saves nothing below the quality threshold.

You can also create an already-reviewed closed structure through `create_message_template_revision_v1_message_template_revisions_post`, list revisions with `list_message_template_revisions_v1_message_template_revisions_get`, inspect one with `get_message_template_revision_v1_message_template_revisions__revision_id__get`, and render exact escaped text/HTML through `render_message_template_revision_v1_message_template_revisions__revision_id__render_post`.

## Personalize a retained prospect

`generate_prospect_message_v1_message_template_revisions__revision_id__prospect_message_post` starts `POST /v1/message-template-revisions/{revision_id}/prospect-message`. Select one prospect from an unexpired succeeded Prospecting job and provide sender/reply/postal identity plus the exact retained evidence and policy references.

The caller cannot declare policy approval or suppression state. For real-recipient copy, the platform requires the exact policy reference and version in its operator-controlled allow-list, verifies basis and address-provenance IDs against the prospect's retained qualification/enrichment evidence, derives the address-source class from that evidence, and checks the project/environment Outcome ledger for an unsubscribe. It returns those facts in `compliance_verification`. Identity, evidence, sender, offer, next-step, company/jurisdiction, and privacy-notice gates still fail closed. The result status is:

- `DRAFT_READY` when every real-recipient gate and quality check passes.
- `NEEDS_INPUT` when required context is incomplete.
- `BLOCKED` for a platform suppression match, rejected qualification, or non-demo synthetic use.
- `DEMONSTRATION_ONLY` for any explicitly requested preview; synthetic data always requires it.

Personalization makes no OpenAI request. `NEEDS_INPUT` and `BLOCKED` return no subject or body. A passing request maps governed retained values to the selected revision's declared variables and uses the deterministic renderer, producing exactly one selected-revision subject. The server validates the complete returned subject/body for evidence and approved-value anchors, recipient relevance, numeric claims, word and link limits, one CTA, mobile structure, prohibited language, and the canonical final opt-out notice. Missing or unsupported required variables return `NEEDS_INPUT`.

## Usage, cost, and safe downstream handling

Each template-generation OpenAI Responses call is captured in a durable billing outbox. The job becomes successful only after all cost rows, customer debit, usage, result, and immutable revision are durably settled; scheduler retries do not repeat the provider call. Job usage and the Costs dashboard report input, cached-input, cache-write, output, reasoning, and total tokens plus true USD cost. Personalization has zero provider usage. See [Usage, quotas, credits, and budgets](https://api.blackpearl.com/docs/usage-quotas).

A `DRAFT_READY` message is still only a human-review artifact. Store its template and approval hashes with any downstream workflow, revalidate consent and suppression at that system's effect boundary, and never infer delivery authority from generation success. The initial POST returns `202`; asynchronous failure appears when polling as `status: "failed"` with `error: {"code": "...", "message": "..."}`. Branch on the stable code and follow the [error and retry guide](https://api.blackpearl.com/docs/errors) for idempotent retries and budget, quota, or provider failures.
