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

# PATCH /v1/templates/{id}

> Edit an existing template: components, category, language, or status.

Updates an existing template. Internally syncs the changes with Meta when applicable (for example when editing `components` or `category`). Only send the fields you want to change; the rest stays as-is.

<Warning>
  Meta only allows editing templates in certain states (`APPROVED`, `REJECTED`, or `PAUSED`, depending on the field). Editing a template in `PENDING` or `IN_REVIEW` may return `409 CONFLICT` from upstream. If you need to change something critical before Meta approves it, it's usually faster to create a new template with `POST /v1/templates`.
</Warning>

## Endpoint

```
PATCH https://api.keebai.com/v1/templates/{id}
```

## Required scope

`templates:update`

## Headers

| Header          | Required | Value                    |
| --------------- | -------- | ------------------------ |
| `Authorization` | Yes      | `Bearer kbai_pk_<token>` |
| `Content-Type`  | Yes      | `application/json`       |

## Path params

| Param | Type     | Description                                                                                                     |
| ----- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `id`  | `string` | Internal `ObjectId` of the template. This is the `id` returned by `GET /v1/templates` and `POST /v1/templates`. |

## Body

| Field              | Type     | Required | Description                                                                                   |
| ------------------ | -------- | -------- | --------------------------------------------------------------------------------------------- |
| `channel_id`       | `string` | Yes      | `ObjectId` of the channel that owns the template. Required by upstream to validate ownership. |
| `name`             | `string` | No       | New name. Lowercase letters, numbers, and underscores only. Max 512.                          |
| `language`         | `string` | No       | Language code. Max 10.                                                                        |
| `category`         | `string` | No       | `MARKETING`, `UTILITY`, or `AUTHENTICATION`.                                                  |
| `parameter_format` | `string` | No       | `NAMED` or `POSITIONAL`.                                                                      |
| `components`       | `array`  | No       | Replaces the components list. Same structure as in `POST /v1/templates`.                      |
| `status`           | `string` | No       | Status to force locally (`PAUSED`, `DISABLED`, etc.). Use with care.                          |
| `metadata`         | `object` | No       | Free-form metadata.                                                                           |

## Example request

<CodeGroup>
  ```bash curl theme={null}
  curl -X PATCH https://api.keebai.com/v1/templates/65f3a1b2c3d4e5f6a7b8c9d2 \
    -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "channel_id": "65f3a1b2c3d4e5f6a7b8c9d0",
      "components": [
        {
          "type": "BODY",
          "text": "Hola {{nombre}}, tu pedido por {{monto}} fue actualizado.",
          "example": {
            "body_text_named_params": [
              { "param_name": "nombre", "example": "Juan" },
              { "param_name": "monto", "example": "$15.000" }
            ]
          }
        }
      ]
    }'
  ```

  ```js JavaScript theme={null}
  const resp = await fetch(
    `https://api.keebai.com/v1/templates/${templateId}`,
    {
      method: "PATCH",
      headers: {
        Authorization: `Bearer ${process.env.KEEBAI_API_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        channel_id: "65f3a1b2c3d4e5f6a7b8c9d0",
        components: [
          {
            type: "BODY",
            text: "Hola {{nombre}}, tu pedido por {{monto}} fue actualizado.",
            example: {
              body_text_named_params: [
                { param_name: "nombre", example: "Juan" },
                { param_name: "monto", example: "$15.000" },
              ],
            },
          },
        ],
      }),
    },
  );
  const template = await resp.json();
  ```

  ```python Python theme={null}
  import os, requests

  resp = requests.patch(
      f"https://api.keebai.com/v1/templates/{template_id}",
      headers={
          "Authorization": f"Bearer {os.environ['KEEBAI_API_TOKEN']}",
          "Content-Type": "application/json",
      },
      json={
          "channel_id": "65f3a1b2c3d4e5f6a7b8c9d0",
          "components": [
              {
                  "type": "BODY",
                  "text": "Hola {{nombre}}, tu pedido por {{monto}} fue actualizado.",
                  "example": {
                      "body_text_named_params": [
                          {"param_name": "nombre", "example": "Juan"},
                          {"param_name": "monto", "example": "$15.000"},
                      ]
                  },
              }
          ],
      },
      timeout=20,
  )
  resp.raise_for_status()
  template = resp.json()
  ```
</CodeGroup>

## Response

### 200 OK

```json theme={null}
{
  "id": "65f3a1b2c3d4e5f6a7b8c9d2",
  "name": "welcome_v2",
  "language": "es_CL",
  "status": "PENDING",
  "category": "UTILITY",
  "parameter_format": "NAMED",
  "variables": ["nombre", "monto"]
}
```

Same shape as `POST /v1/templates`. If the edit triggered a fresh Meta review, `status` may sit in `IN_REVIEW` or `PENDING` until Meta approves it again.

### 400 / 401 / 403 / 404 / 409 / 429

Common cases:

* `400 BAD_REQUEST`: body validation.
* `403 FORBIDDEN` with `code: INSUFFICIENT_SCOPE`: the PAT does not have `templates:update`.
* `404 NOT_FOUND`: the template does not exist or does not belong to your tenant.
* `409 CONFLICT`: Meta rejected the edit because the template is not in an editable state.
* `502 UPSTREAM_ERROR`: generic error from Meta. `details` describes the cause.

<Tip>
  After editing components, poll [`GET /v1/templates`](/dev/endpoints/templates-list) to confirm Meta has re-approved the template before continuing to send it in production.
</Tip>


## OpenAPI

````yaml PATCH /v1/templates/{id}
openapi: 3.0.0
info:
  title: Keebai Public API
  description: Superficie pública REST autenticada con Personal Access Tokens (PAT).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.keebai.com
    description: Production
security: []
tags: []
paths:
  /v1/templates/{id}:
    patch:
      tags:
        - templates
      summary: Editar un template existente
      operationId: PublicTemplatesController_update
      parameters:
        - name: id
          required: true
          in: path
          description: ObjectId del template
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTemplateDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicTemplateDto'
      security:
        - PAT: []
components:
  schemas:
    UpdateTemplateDto:
      type: object
      properties:
        channel_id:
          type: string
          description: >-
            ObjectId del canal WhatsApp dueño del template (requerido por
            upstream).
        name:
          type: string
          maxLength: 512
        language:
          type: string
          maxLength: 10
        category:
          type: string
          enum:
            - MARKETING
            - UTILITY
            - AUTHENTICATION
        parameter_format:
          type: string
          enum:
            - NAMED
            - POSITIONAL
        components:
          type: array
          items:
            $ref: '#/components/schemas/TemplateComponentDto'
        status:
          type: string
          enum:
            - PENDING
            - IN_REVIEW
            - REJECTED
            - APPROVED
            - PAUSED
            - DISABLED
            - IN_APPEAL
        metadata:
          type: object
          additionalProperties: true
      required:
        - channel_id
    PublicTemplateDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        language:
          type: string
        status:
          type: string
        category:
          type: string
        parameter_format:
          type: string
        variables:
          type: array
          items:
            type: string
      required:
        - id
        - name
        - language
        - status
        - category
        - variables
    TemplateComponentDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - HEADER
            - BODY
            - FOOTER
            - BUTTONS
        format:
          type: string
          enum:
            - TEXT
            - IMAGE
            - VIDEO
            - DOCUMENT
        text:
          type: string
          maxLength: 1024
        buttons:
          type: array
          items:
            $ref: '#/components/schemas/TemplateButtonDto'
        example:
          $ref: '#/components/schemas/TemplateExampleDto'
        add_security_recommendation:
          type: boolean
        code_expiration_minutes:
          type: number
      required:
        - type
    TemplateButtonDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - QUICK_REPLY
            - URL
            - PHONE_NUMBER
            - FLOW
            - VOICE_CALL
            - COPY_CODE
            - OTP
        text:
          type: string
          maxLength: 25
        url:
          type: string
        phone_number:
          type: string
        example:
          type: array
          items:
            type: string
        flow_id:
          type: string
        flow_action:
          type: string
          enum:
            - navigate
            - data_exchange
        coupon_code:
          type: string
        otp_type:
          type: string
          enum:
            - COPY_CODE
            - ONE_TAP
            - ZERO_TAP
        autofill_text:
          type: string
        package_name:
          type: string
        signature_hash:
          type: string
      required:
        - type
        - text
    TemplateExampleDto:
      type: object
      properties:
        header_text:
          type: array
          items:
            type: string
        header_text_named_params:
          type: array
          items:
            $ref: '#/components/schemas/NamedParamDto'
        header_handle:
          type: array
          items:
            type: string
        body_text:
          type: array
          items:
            type: string
        body_text_named_params:
          type: array
          items:
            $ref: '#/components/schemas/NamedParamDto'
    NamedParamDto:
      type: object
      properties:
        param_name:
          type: string
          example: nombre
        example:
          type: string
          example: Juan
      required:
        - param_name
        - example
  securitySchemes:
    PAT:
      scheme: bearer
      bearerFormat: kbai_pk_<hex>
      type: http
      description: >-
        Personal Access Token con prefijo `kbai_pk_`. Generar desde el portal
        con permiso `developer.manage_tokens`.

````