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

# Playbooks API: run registry-owned automations

> List and start typed, registry-owned automations without allowing callers to inject workflow steps or source.

<Warning>
  **Limited release:** the Playbooks API is currently off. Until SignSealShip
  enables it for your account, both routes on this page are not mapped and
  return `404 Not Found`. Do not build an availability check that assumes a
  registry response exists before access is enabled.
</Warning>

A playbook is a named automation owned by the SignSealShip registry. You select its fixed `key` and supply values that satisfy its typed parameter schema. You can never submit workflow source, choose an underlying workflow name, or inject steps.

That boundary is a security guarantee: the registry decides which execution graph and cloud permissions can run. Caller values are validated against the exact registered schema and sent only as runtime arguments. Unknown fields are rejected before any cloud credential is acquired or outbound execution request is made, and no caller value is interpolated into workflow source or a workflow name.

## Authentication

Both routes accept a partner bearer key or a linked dashboard session. A valid bearer key is resolved first and selects the partner; otherwise the route falls back to the session.

* `401` `{"error":"Sign in to continue."}` — no bearer key resolved and there is no valid session.
* `403` `{"error":"This session is not linked to a partner. Link with a partner API key first."}` — the session is valid but unlinked.

## List the registry

`GET /api/partner/playbooks`

Returns the playbooks currently exposed by the closed registry and the parameter schema for each one.

```bash curl theme={null}
curl https://signsealship.com/api/partner/playbooks \
  -H "Authorization: Bearer sss_pk_your_key"
```

```json 200 OK theme={null}
{
  "playbooks": [
    {
      "key": "approval_gate",
      "name": "Human approval gate",
      "description": "Pauses a subject until a human approval decision is received.",
      "paramSchema": [
        { "name": "kind", "type": "string", "required": true, "maxLength": 40 },
        { "name": "subjectId", "type": "string", "required": true, "maxLength": 64 },
        { "name": "note", "type": "string", "required": false, "maxLength": 500 }
      ]
    }
  ]
}
```

<ResponseField name="playbooks" type="array">
  Complete catalog visible through this API.

  <Expandable title="playbook definition">
    <ResponseField name="key" type="string">
      Case-sensitive registry key used in the run endpoint.
    </ResponseField>

    <ResponseField name="name" type="string">
      Human-readable playbook name.
    </ResponseField>

    <ResponseField name="description" type="string">
      Short description of the registered behavior.
    </ResponseField>

    <ResponseField name="paramSchema" type="array">
      Ordered list of caller-supplied parameters accepted by this playbook.

      <Expandable title="parameter definition">
        <ResponseField name="name" type="string">
          Case-sensitive form-field name.
        </ResponseField>

        <ResponseField name="type" type="string">
          Runtime value type. The executor recognizes `string`, `int`, and
          `bool`; the current `approval_gate` parameters are all `string`.
        </ResponseField>

        <ResponseField name="required" type="boolean">
          Whether the field must be present and non-empty.
        </ResponseField>

        <ResponseField name="maxLength" type="integer">
          Maximum string length, or `null` when the registered parameter has no
          length limit.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

The response deliberately does not expose the underlying workflow name or definition.

## Run a playbook

`POST /api/partner/playbooks/{key}/run`

<ParamField path="key" type="string" required>
  Case-sensitive key returned by the registry. The only current value is
  `approval_gate`.
</ParamField>

Send parameters as form fields with `application/x-www-form-urlencoded` or `multipart/form-data`. JSON request bodies are not parsed by this route; a JSON request to a known playbook is treated as having no parameters and fails validation for its missing required fields.

```bash curl theme={null}
curl -X POST https://signsealship.com/api/partner/playbooks/approval_gate/run \
  -H "Authorization: Bearer sss_pk_your_key" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "kind=agreement" \
  --data-urlencode "subjectId=agreement-123" \
  --data-urlencode "note=Review before release"
```

```json 200 OK theme={null}
{
  "executionName": "projects/{project}/locations/{location}/workflows/{workflow}/executions/{execution}"
}
```

<ResponseField name="executionName" type="string">
  Durable execution resource name returned by the underlying workflow service.
  Treat it as an opaque identifier. A `200` means the provider confirmed that
  the execution was created; it does **not** mean the automation finished or a
  human approved the subject.
</ResponseField>

Each successful call starts a new execution. The API does not accept an execution name or idempotency key, and the executor does not automatically retry an ambiguous create request because doing so could create two approval gates. This version of the API also has no endpoint to read, cancel, or retrieve the result of an execution.

Every authenticated run attempt is audit-logged, including unknown keys, validation failures, and remote start failures. The audit record contains the partner, requested playbook key, and confirmed execution name when one exists; it does not copy the submitted parameter values.

### Responses and errors

| Status                  | Body and meaning                                                                                                                                                           |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200 OK`                | `{"executionName":"..."}` — an execution was confirmed.                                                                                                                    |
| `400 Bad Request`       | `{"error":"..."}` — a required argument is missing or empty, an argument is unknown or too long, a typed value is invalid, or the remote execution could not be confirmed. |
| `404 Not Found`         | Before limited-release access is enabled, the routes are absent. Once enabled, an unknown `key` returns `{"error":"Unknown playbook."}`.                                   |
| `401 Unauthorized`      | No bearer key resolved and there is no valid session.                                                                                                                      |
| `403 Forbidden`         | The valid session is not linked to a partner.                                                                                                                              |
| `429 Too Many Requests` | The route's fixed one-minute limit was exceeded.                                                                                                                           |

Validation errors name the rejected field, for example:

```json 400 Bad Request theme={null}
{ "error": "Unknown argument 'workflowSource'." }
```

```json 400 Bad Request theme={null}
{ "error": "Missing required argument 'subjectId'." }
```

A provider credential failure, timeout, non-success response, or response without an execution name is deliberately collapsed to:

```json 400 Bad Request theme={null}
{ "error": "The playbook could not be started." }
```

That error confirms only that this API did not receive a durable execution name. A transport failure can be ambiguous, so do not blindly retry it as though non-creation were proven.

The registry read uses the `partner-portal` limit of 120 requests per minute, bucketed by dashboard session when present and otherwise by client IP. Runs use the `partner-write` limit of 60 requests per minute, bucketed by the presented bearer key when present and otherwise by client IP.

## `approval_gate`

The current registry contains one playbook: a durable wait for a human decision.

| Parameter   | Type     | Required | Maximum        | Meaning                                                                                                                                        |
| ----------- | -------- | -------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind`      | `string` | Yes      | 40 characters  | Caller-defined subject category, such as `agreement`. Returned with the workflow outcome.                                                      |
| `subjectId` | `string` | Yes      | 64 characters  | Caller-defined identifier for the subject under review. Returned with the workflow outcome.                                                    |
| `note`      | `string` | No       | 500 characters | Additional runtime context. The current workflow accepts it but does not read, log, or return it, so it does not change the decision behavior. |

The execution creates an internal HTTP callback endpoint and waits for a human decision for up to **30 days** (`2,592,000` seconds). The workflow itself never approves anything.

* When the callback request body contains `decision`, the workflow completes with that value as `outcome`, alongside `kind` and `subjectId`.
* When the callback body omits `decision`, the outcome is `unspecified`.
* When the 30-day wait expires — or the wait raises an error — the workflow completes with `outcome: "expired"`, alongside `kind` and `subjectId`.

The public run response exposes only `executionName`; it does not expose the callback URL or the eventual decision. Callback delivery and result consumption remain inside the limited-release integration.
