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

# Update Template

> Edits the components of an existing template.

## Full-replace semantics

On Meta the edit is **full-replace**: the component set you send becomes the new state, and any component you do not send is removed. Send every component you want to keep, at minimum `body`. Sending only `body` on a template that has a header, footer, and buttons drops them and is rejected with `409` (`META_096`).

The same applies to `limitedTimeOffer` — an existing offer is removed if you do not send it again.

After a successful edit the status returns to `PENDING` for re-review, and the local record is synced.

## Constraints

- `name` and `language` cannot be changed and cannot be sent.
- Editing is allowed only when the template is `APPROVED`, `REJECTED`, or `PAUSED`. Any other status returns `409` (`META_096`).
- An active template can be edited only once in 24 hours; exceeding that returns `429` (`META_097`).
- `category` changes are effective only for `REJECTED` / `PAUSED` templates; Meta rejects a category change on an `APPROVED` one. Changes are subject to review.
- `carousel` is not accepted on edit at all. An existing carousel template's cards cannot be edited through this API — create a new template instead.

## Validation differences from create

Button, header, and body rules are identical to Create Template (`WA_TPL_001`–`005`, `015`–`020`, `006`–`010`, `021`, `011`–`014`).

The **category rules** (`WA_TPL_022`–`033`) and the **name rule** (`WA_TPL_034`) are not evaluated here, since `name` cannot be sent and the category-vs-component check only runs on create. Meta still enforces the equivalent rules, so a category-incompatible edit comes back as a `META_0xx` error rather than a `WA_TPL_0xx` one.

Limited-time offer rules apply as on create, except that the `MARKETING` check (`WA_TPL_048`) runs only when you also send a `category`; if you omit it, Meta validates the offer against the template's existing category.



## OpenAPI

````yaml /public-openapi.json post /wa/{app_id}/templates/{source_id}
openapi: 3.0.3
info:
  title: Connexease Public API
  version: 1.0.0
  description: >-
    Panel-independent API for managing WhatsApp Business operations from your
    own servers.


    Every endpoint is scoped to the organization resolved from your secret key.
    Read endpoints are uncached and proxy straight to the source.


    **On-premises installations** are served from your own domain with an extra
    `/public` segment: `https://{your_domain}/public/api/v1/...`.
    Authentication, bodies, responses, and error codes are identical.
servers:
  - url: https://public-api.gateway.connexease.com/api/v1
    description: Production
security:
  - secretKey: []
tags:
  - name: Analytics
    description: Organization-wide message volume.
  - name: Application
    description: The WhatsApp business profile linked to an application.
  - name: Template
    description: >-
      The full template lifecycle: create, list, inspect, update, delete, and
      measure.
  - name: Media
    description: >-
      Uploads that produce the single-use handle for template headers and the
      profile picture.
  - name: Developers
    description: Webhook configuration and API keys.
paths:
  /wa/{app_id}/templates/{source_id}:
    post:
      tags:
        - Template
      summary: Update Template
      description: >-
        Edits the components of an existing template.


        ## Full-replace semantics


        On Meta the edit is **full-replace**: the component set you send becomes
        the new state, and any component you do not send is removed. Send every
        component you want to keep, at minimum `body`. Sending only `body` on a
        template that has a header, footer, and buttons drops them and is
        rejected with `409` (`META_096`).


        The same applies to `limitedTimeOffer` — an existing offer is removed if
        you do not send it again.


        After a successful edit the status returns to `PENDING` for re-review,
        and the local record is synced.


        ## Constraints


        - `name` and `language` cannot be changed and cannot be sent.

        - Editing is allowed only when the template is `APPROVED`, `REJECTED`,
        or `PAUSED`. Any other status returns `409` (`META_096`).

        - An active template can be edited only once in 24 hours; exceeding that
        returns `429` (`META_097`).

        - `category` changes are effective only for `REJECTED` / `PAUSED`
        templates; Meta rejects a category change on an `APPROVED` one. Changes
        are subject to review.

        - `carousel` is not accepted on edit at all. An existing carousel
        template's cards cannot be edited through this API — create a new
        template instead.


        ## Validation differences from create


        Button, header, and body rules are identical to Create Template
        (`WA_TPL_001`–`005`, `015`–`020`, `006`–`010`, `021`, `011`–`014`).


        The **category rules** (`WA_TPL_022`–`033`) and the **name rule**
        (`WA_TPL_034`) are not evaluated here, since `name` cannot be sent and
        the category-vs-component check only runs on create. Meta still enforces
        the equivalent rules, so a category-incompatible edit comes back as a
        `META_0xx` error rather than a `WA_TPL_0xx` one.


        Limited-time offer rules apply as on create, except that the `MARKETING`
        check (`WA_TPL_048`) runs only when you also send a `category`; if you
        omit it, Meta validates the offer against the template's existing
        category.
      operationId: updateTemplate
      parameters:
        - $ref: '#/components/parameters/AppId'
        - $ref: '#/components/parameters/SourceId'
        - $ref: '#/components/parameters/PreferHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - body
              properties:
                body:
                  $ref: '#/components/schemas/TemplateBody'
                header:
                  $ref: '#/components/schemas/TemplateHeader'
                footer:
                  $ref: '#/components/schemas/TemplateFooter'
                buttons:
                  type: array
                  maxItems: 10
                  items:
                    $ref: '#/components/schemas/TemplateButton'
                messageSendTtlSeconds:
                  type: integer
                limitedTimeOffer:
                  $ref: '#/components/schemas/LimitedTimeOffer'
                category:
                  $ref: '#/components/schemas/TemplateCategory'
            example:
              header:
                format: TEXT
                text: 'Order #{{1}} update'
                examples:
                  - '12345'
              body:
                text: Hi {{1}}, your order {{2}} is now {{3}}. Thank you.
                examples:
                  - John
                  - '#12345'
                  - shipped
              footer:
                text: Connexease
              buttons:
                - type: QUICK_REPLY
                  text: Track
      responses:
        '200':
          description: >-
            The updated template re-fetched from Meta, with `status: "PENDING"`.
            With `Prefer: return=minimal` the `data` key is omitted entirely —
            the response is exactly `{ "isSuccess": true }`, still HTTP `200`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Template'
                  isSuccess:
                    type: boolean
                    enum:
                      - true
        '400':
          description: Meta rejected a parameter (`META_095`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: >-
            Secret key missing or invalid (`ORGANIZATION_SECRET_010` / `011`),
            or Meta token invalid during the pre-fetch (`META_034`) or the
            update (`META_092`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: >-
            Meta permission denied during the pre-fetch (`META_035`) or the
            update (`META_093`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: >-
            `app_id` not in the organization (`APPLICATION_004`), or `source_id`
            not found (`META_075`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: >-
            Template is not in an editable status, or the edit broke its
            structure (`META_096`), or a name + language conflict (`META_017`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              example:
                errors:
                  code: META_096
                  group: CONFLICT
                  description: >-
                    Template update failed. Template is not in an editable
                    status (editable only when APPROVED, REJECTED or PAUSED; it
                    may be PENDING after a recent edit).
                isSuccess: false
        '422':
          description: >-
            A button, header, or body rule, an invalid `category` value, or a
            limited-time offer rule (`WA_TPL_048`–`051`, `053`–`055`). Meta-side
            validation also lands here: `META_022`–`META_029`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          description: >-
            Meta rate limit during the pre-fetch (`META_036`) or the update
            (`META_094`), or the 24-hour edit limit (`META_097`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '502':
          description: >-
            General Meta-side failure during the pre-fetch (`META_004`) or the
            update (`META_091`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
components:
  parameters:
    AppId:
      name: app_id
      in: path
      required: true
      description: >-
        Application ID. Must belong to the organization resolved from your
        secret key, otherwise the request returns `404` with `APPLICATION_004`.
      schema:
        type: string
        example: app_7poyXj8GXuv76e
    SourceId:
      name: source_id
      in: path
      required: true
      description: The template's Meta source ID.
      schema:
        type: string
        example: '1234567890123456'
    PreferHeader:
      name: Prefer
      in: header
      required: false
      description: >-
        `return=representation` (default) returns the current record;
        `return=minimal` returns no body. The response echoes the choice in
        `Preference-Applied`.
      schema:
        type: string
        enum:
          - return=representation
          - return=minimal
        default: return=representation
  schemas:
    TemplateBody:
      type: object
      properties:
        text:
          type: string
          maxLength: 1024
          description: >-
            Supports `{{1}}`, `{{2}}` variables. Cannot start or end with a
            variable (`WA_TPL_011` / `WA_TPL_012`). Required for `UTILITY` and
            `MARKETING` (`WA_TPL_027`); not allowed for `AUTHENTICATION`, where
            Meta generates it (`WA_TPL_023`).
        examples:
          type: array
          items:
            type: string
          description: >-
            Example values for the variables, in order. The count must equal the
            number of variables (`WA_TPL_014`); variables with no examples at
            all are rejected with `WA_TPL_013`.
        addSecurityRecommendation:
          type: boolean
          description: >-
            `AUTHENTICATION` only (`WA_TPL_028`). Adds Meta's security
            recommendation text.
    TemplateHeader:
      type: object
      properties:
        format:
          $ref: '#/components/schemas/HeaderFormat'
        text:
          type: string
          maxLength: 60
          description: >-
            `TEXT` headers only. Cannot start or end with a variable
            (`WA_TPL_008` / `WA_TPL_009`). Sending it on a media or `LOCATION`
            header is rejected (`WA_TPL_010` / `WA_TPL_021`).
        examples:
          type: array
          items:
            type: string
          description: >-
            Variable example for a `TEXT` header; for media headers, the media
            handle from Upload Media.
    TemplateFooter:
      type: object
      properties:
        text:
          type: string
          maxLength: 60
          description: >-
            Not allowed in `AUTHENTICATION` (`WA_TPL_024`) or in a limited-time
            offer template (`WA_TPL_051`).
        codeExpirationMinutes:
          type: integer
          minimum: 1
          maximum: 90
          description: '`AUTHENTICATION` only (`WA_TPL_029`). How long the code stays valid.'
    TemplateButton:
      type: object
      properties:
        type:
          type: string
          enum:
            - QUICK_REPLY
            - URL
            - PHONE_NUMBER
            - COPY_CODE
            - OTP
          description: >-
            `AUTHENTICATION` accepts only `OTP`; `UTILITY` accepts neither `OTP`
            nor `COPY_CODE`; `COPY_CODE` is `MARKETING`-only.
        text:
          type: string
          minLength: 2
          maxLength: 25
          description: >-
            Required for `URL`, `PHONE_NUMBER`, and `QUICK_REPLY`
            (`WA_TPL_015`).
        url:
          type: string
          description: >-
            `URL` type only, and required for it (`WA_TPL_001`). Must start with
            `https://` and may end in a `{{1}}` variable, in which case
            `example` is required. Cannot be combined with `phoneNumber`
            (`WA_TPL_002`).
        phoneNumber:
          type: string
          description: >-
            `PHONE_NUMBER` type only, and required for it (`WA_TPL_003`). E.164
            on the way in; Meta returns it in RFC3966 form (`tel:+90-555-...`).
            Cannot be combined with `url` (`WA_TPL_004`).
        example:
          type: array
          items:
            type: string
          description: >-
            Dynamic URL example, or the coupon code for `COPY_CODE` — required
            for that type (`WA_TPL_016`).
        otpType:
          type: string
          enum:
            - COPY_CODE
            - ONE_TAP
            - ZERO_TAP
          description: >-
            Required on `OTP` buttons (`WA_TPL_017`). `COPY_CODE` has the user
            copy the code manually; `ONE_TAP` reads it with one tap; `ZERO_TAP`
            delivers it with no user action.
        autofillText:
          type: string
          maxLength: 25
          description: Optional for `ONE_TAP` / `ZERO_TAP`.
        packageName:
          type: string
          description: >-
            Android package name. Required for `ONE_TAP` / `ZERO_TAP`
            (`WA_TPL_018`).
        signatureHash:
          type: string
          description: Required for `ONE_TAP` / `ZERO_TAP` (`WA_TPL_019`).
        zeroTapTermsAccepted:
          type: boolean
          description: Must be `true` on `ZERO_TAP` buttons (`WA_TPL_020`).
      required:
        - type
    LimitedTimeOffer:
      type: object
      properties:
        text:
          type: string
          minLength: 1
          maxLength: 16
          description: Offer text on the banner, e.g. `Expiring offer!`.
        hasExpiration:
          type: boolean
          default: false
          description: >-
            When `true`, WhatsApp renders a countdown. This only controls
            whether the countdown is shown — the expiration timestamp itself is
            supplied per message at send time, so one approved template can
            serve different deadlines.
      required:
        - text
    TemplateCategory:
      type: string
      description: >-
        Determines which components are allowed. `UTILITY` for transactional
        messages, `MARKETING` for promotional, `AUTHENTICATION` for one-time
        passcodes.
      enum:
        - UTILITY
        - MARKETING
        - AUTHENTICATION
    Template:
      type: object
      description: >-
        Null fields (`id`, `createdAt`, `updatedAt`, `messageSendTtlSeconds`,
        `correctCategory`, `previousCategory`) are dropped from the response.
      properties:
        sourceId:
          type: string
          description: The template's Meta ID.
        name:
          type: string
        language:
          type: string
        category:
          $ref: '#/components/schemas/TemplateCategory'
        status:
          $ref: '#/components/schemas/TemplateStatus'
        components:
          type: array
          items:
            $ref: '#/components/schemas/TemplateComponent'
          description: Returned only with `expand=components`.
        qualityScore:
          $ref: '#/components/schemas/TemplateQualityScore'
        rejectedReason:
          $ref: '#/components/schemas/TemplateRejectedReason'
        parameterFormat:
          $ref: '#/components/schemas/TemplateParameterFormat'
    ErrorEnvelope:
      type: object
      description: >-
        Every failure shares this shape. `description` already has any `params`
        interpolated into it.
      properties:
        isSuccess:
          type: boolean
          enum:
            - false
        errors:
          type: object
          properties:
            code:
              type: string
              description: >-
                `VAL_*`, `WA_*`, `META_*`, `APPLICATION_*`,
                `ORGANIZATION_SECRET_*`, `REQ_001`, or `SYS_001`.
              example: META_075
            group:
              type: string
              enum:
                - VALIDATION
                - NOT_FOUND
                - CONFLICT
                - AUTHENTICATION
                - NOT_ALLOWED
                - LIMIT_EXCEEDED
                - SERVICE_UNAVAILABLE
                - SYSTEM
            description:
              type: string
              example: Template with 0000000000 not found in Meta.
            params:
              type: object
              additionalProperties: true
              description: >-
                Raw values interpolated into `description`. Present only on
                parametric messages.
            fields:
              type: object
              description: >-
                Field-level validation errors only. Field name (dot-joined for
                nested fields, list indices omitted) to its own `code` /
                `description` / `params`.
              additionalProperties:
                type: object
                properties:
                  code:
                    type: string
                  description:
                    type: string
                  params:
                    type: object
                    additionalProperties: true
          required:
            - code
            - group
            - description
      required:
        - isSuccess
        - errors
    HeaderFormat:
      type: string
      description: >-
        `NONE` is the default when `header` is omitted. `AUTHENTICATION` allows
        no header at all; a carousel card header must be `IMAGE` or `VIDEO`; a
        limited-time offer header is limited to `NONE` / `IMAGE` / `VIDEO`.
      enum:
        - NONE
        - TEXT
        - IMAGE
        - VIDEO
        - DOCUMENT
        - LOCATION
    TemplateStatus:
      type: string
      description: >-
        Review and lifecycle status. `UNKNOWN` means Meta reported a status this
        API does not recognise.
      enum:
        - APPROVED
        - PENDING
        - REJECTED
        - PAUSED
        - DISABLED
        - IN_APPEAL
        - PENDING_DELETION
        - DELETED
        - LIMIT_EXCEEDED
        - UNKNOWN
    TemplateComponent:
      type: object
      description: >-
        One component. Every field other than `type` is dropped when Meta does
        not report it.
      properties:
        type:
          type: string
          enum:
            - HEADER
            - BODY
            - FOOTER
            - BUTTONS
            - CAROUSEL
            - LIMITED_TIME_OFFER
        format:
          type: string
          enum:
            - TEXT
            - IMAGE
            - VIDEO
            - DOCUMENT
            - LOCATION
            - CAROUSEL
          description: >-
            `HEADER` only. A response header never reports `NONE` — the
            component is simply absent — but may report `CAROUSEL` for a
            carousel template.
        text:
          type: string
          description: '`HEADER` with `format: TEXT`, `BODY`, or `FOOTER`.'
        example:
          $ref: '#/components/schemas/ComponentExample'
        buttons:
          type: array
          items:
            $ref: '#/components/schemas/ComponentButton'
          description: '`BUTTONS` only.'
        cards:
          type: array
          description: >-
            `CAROUSEL` only. One entry per card, each holding its own
            `components` array limited to `HEADER`, `BODY`, and `BUTTONS`.
          items:
            type: object
            properties:
              components:
                type: array
                items:
                  $ref: '#/components/schemas/CardComponent'
        addSecurityRecommendation:
          type: boolean
          description: '`BODY` of an `AUTHENTICATION` template.'
        codeExpirationMinutes:
          type: integer
          description: '`FOOTER` of an `AUTHENTICATION` template.'
        limitedTimeOffer:
          $ref: '#/components/schemas/LimitedTimeOffer'
      required:
        - type
    TemplateQualityScore:
      type: string
      description: Quality rating derived by Meta from user feedback.
      enum:
        - GREEN
        - YELLOW
        - RED
        - UNKNOWN
    TemplateRejectedReason:
      type: string
      enum:
        - NONE
        - ABUSIVE_CONTENT
        - INVALID_FORMAT
        - PROMOTIONAL
        - TAG_CONTENT_MISMATCH
        - SCAM
        - INCORRECT_CATEGORY
        - UNKNOWN
    TemplateParameterFormat:
      type: string
      description: How variables are referenced within the template.
      enum:
        - POSITIONAL
        - NAMED
        - UNKNOWN
    ComponentExample:
      type: object
      description: Sample values Meta stores for the component's variables or media.
      properties:
        headerText:
          type: array
          items:
            type: string
          description: Sample values for the `TEXT` header variables.
        headerHandle:
          type: array
          items:
            type: string
          description: The media handle behind an `IMAGE` / `VIDEO` / `DOCUMENT` header.
        headerUrl:
          type: array
          items:
            type: string
          description: The resolved media URL, when Meta returns one instead of a handle.
        bodyText:
          type: array
          items:
            type: array
            items:
              type: string
          description: >-
            Sample values for the body variables — a list of value sets, so
            nested one level deeper than `headerText`.
    ComponentButton:
      type: object
      description: >-
        A button as Meta reports it. Note the snake_case keys — they differ from
        the camelCase keys used when creating a template.
      properties:
        type:
          type: string
          enum:
            - URL
            - PHONE_NUMBER
            - QUICK_REPLY
            - COPY_CODE
            - OTP
        text:
          type: string
          description: The label shown on the button.
        url:
          type: string
          description: '`URL` buttons: the target link, possibly containing `{{1}}`.'
        phone_number:
          type: string
          description: >-
            `PHONE_NUMBER` buttons. Meta returns RFC3966 form
            (`tel:+90-555-123-45-67`), not the E.164 form you sent.
        example:
          type: array
          items:
            type: string
          description: The dynamic-URL sample, or the `COPY_CODE` coupon code.
        otp_type:
          type: string
          enum:
            - COPY_CODE
            - ONE_TAP
            - ZERO_TAP
        autofill_text:
          type: string
        package_name:
          type: string
        signature_hash:
          type: string
        zero_tap_terms_accepted:
          type: boolean
    CardComponent:
      type: object
      description: >-
        A component inside a carousel card. A card is a reduced template: no
        footer, no limited-time offer.
      properties:
        type:
          type: string
          enum:
            - HEADER
            - BODY
            - BUTTONS
        format:
          type: string
          enum:
            - IMAGE
            - VIDEO
          description: '`HEADER` only.'
        text:
          type: string
          description: '`BODY` only.'
        example:
          $ref: '#/components/schemas/ComponentExample'
        buttons:
          type: array
          items:
            $ref: '#/components/schemas/ComponentButton'
  securitySchemes:
    secretKey:
      type: http
      scheme: bearer
      description: >-
        Secret key in `Bearer sk_...` format. Server-to-server only — never
        embed it in a browser or mobile client. The organization is resolved
        from the key, so `organization_id` is never passed explicitly.

````