<!--
  Translated by the docs agent from content-src/es/api/copilot-sdk/05-escribir-el-manifiesto-v1.md
  (source_hash bc88037f8c77). If that Spanish source_hash changes, re-check this translation.
-->

The manifest is a JSON document shaped like this:

```
{
  "version": 1,
  "app": "your-site",
  "sdkMinVersion": "1.1.0",
  "screens": [
    {
      "id": "leads.new",
      "path": "/leads/new",
      "label": { "es": "Nuevo lead", "en": "New lead", "it": "Nuovo lead" },
      "anchors": [
        { "id": "name", "kind": "field", "fieldType": "text", "required": true,
          "label": { "es": "Nombre", "en": "Name", "it": "Nome" } },
        { "id": "phone", "kind": "field", "fieldType": "phone",
          "label": { "es": "Teléfono (sin espacios, sin +, solo dígitos)",
                     "en": "Phone (digits only, no spaces, no +)",
                     "it": "Telefono (solo cifre)" } },
        { "id": "save", "kind": "action", "effect": "write",
          "label": { "es": "Guardar", "en": "Save", "it": "Salva" } }
      ]
    }
  ]
}
```

| Field | What it is |
| --- | --- |
| `version` | Always `1` (the contract number this manual describes). |
| `app` | A short id for your application, informational only. |
| `sdkMinVersion` | The minimum copilot SDK version this manifest needs — at least `1.1.0` if your site uses the neutral attribute `data-copilot-anchor` (section 4); `1.0.0` is enough if you only use the previous attribute. |
| `screens` | Up to 200 screens. Each one: `id`, `path` (a relative path on your own site, never an absolute or external URL), `label` and up to 100 `anchors`. |

## 5.1 Anchor types

| `kind` | What the copilot does with it |
| --- | --- |
| `field` | It can fill it in ("let's do it together" mode). Carries `fieldType`. |
| `action` | It can tap it. Carries `effect`. |
| `region` | It can only highlight it (guide) — never fills it in or taps it. Useful for selects whose options depend on your own data (they can't be declared "closed"). |

| `fieldType` | Note |
| --- | --- |
| `text` · `textarea` · `number` · `email` · `phone` · `date` · `datetime` · `select` · `checkbox` |   |

> **Important — There is no `fieldType: "password"`**
>
> A manifest that tries to declare a password field is rejected on the server with the exact reason. And even if you tried to disguise it as another type, the SDK never writes to a real `<input type="password">` on your page — it checks against the DOM element itself, not against what you claim in the JSON.

| Action `effect` | Consequence |
| --- | --- |
| `navigate` | Opens another screen or reveals something on the same one. No confirmation. |
| `read` | Only reads. No confirmation. |
| `write` | Saves a change. **Always confirmed** — the server requires it even if the plan doesn't ask for it. |
| `send` | Sends something to a third party (e.g. replying to a customer). Always confirmed. |
| `destructive` | Deletes something. Always confirmed, with destructive wording. |

## 5.2 Shape rules (the server enforces them, no exceptions)

- Screen and anchor ids: `^[a-z0-9][a-z0-9._-]{0,63}$` — lowercase letters, digits, dot, hyphen and underscore. No uppercase and no spaces (the "T" in `appointmentType` is rejected; `appointment-type` is fine).
- `path` is always a relative path on your own domain (`/leads/new`) — never an absolute URL, never `javascript:`, never an external domain.
- Labels: plain text, max 80 characters, no `<>{}[]` or control characters. If your manifest is for the Platform or its internal console you need all three languages; for your own site one is enough.
- A `field` of type `select` must declare its `options` — a closed list. If the value depends on data that changes (a stage, an owner, a catalog specific to each customer), it isn't a `select`: use `region` and let the copilot only point it out.
- Overall limits: ≤ 200 screens, ≤ 100 anchors per screen, ≤ 256 KB for the whole JSON document, ≤ 30 steps per plan (this last one is decided by the server when validating each plan, not by your manifest).

> **Note — Phone fields: put the format in the label itself**
>
> In real tests, an assistant filled in a phone number with a leading "+" because nothing in the label said otherwise, and the form itself rejected it (the system correctly failed closed). The way to avoid that unnecessary back-and-forth is for the anchor's label to say so explicitly: "Phone (digits only, no spaces or prefix)" instead of just "Phone". The assistant only has that text to know what format you expect.

## 5.3 Manifest: what to declare and what not to

Rules learned while building the Platform's own manifests (the one for its main panel and the ones for its two internal consoles) that are worth keeping in mind when writing yours:

- **If your site can show several instances of the same kind at once** (several WhatsApp channels open at once, several cards of the same integration…), **don't declare per-instance controls**. The same `data-copilot-anchor` would exist repeated on the page, and the SDK would always resolve against the first one it finds — not necessarily the one the person has in front of them. Declare only what's safe to anchor without ambiguity (a single-instance creation screen, for example) and leave the rest undeclared, or as a `region` that guides over the whole list.
- **A field whose options depend on data that changes** (a catalog specific to each customer, an AI model that varies by plan, a funnel stage the person themselves customizes) **isn't a `select`** — declaring it as a closed list would be inaccurate, since it isn't really one. Declare it as a `region`: the copilot points it out, and the person chooses.
- **A trailing `?` on `path` for screens with no route of their own**: if two of your screens share the same base URL (for example, a form that lives at the same route as its listing, with no identifier of its own in the URL), add an empty `?` to the end of the `path` of the one without its own deep link — this keeps the SDK from confusing which of the two to resolve when navigating by the current URL.
- **Reactive deep links**: if you use a URL parameter to open a modal or a specific view (for example `?copilotOpen=new`), read it **reactively** — reacting to URL changes within the same browsing session, not only when the component mounts — because otherwise, a second copilot plan that reuses an already-open screen won't trigger the modal to open again.