Skip to main content
POST
/
workspaces
/
{workspace}
/
templates
/
{template}
Update a template
curl --request POST \
  --url https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template} \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "name": "login_otp_template",
  "language": "en",
  "category": "MARKETING",
  "body": {
    "text": "<string>",
    "variables": [
      "<string>"
    ]
  },
  "header": "<unknown>",
  "footer": "<string>",
  "buttons": [
    {
      "type": "URL",
      "text": "<string>",
      "phone_number": "<string>",
      "url": "<string>",
      "variables": [
        "<string>"
      ]
    }
  ],
  "unsubscribeButton": true,
  "blockButton": true,
  "addSecurityRecommendation": true,
  "codeExpirationMinutes": 720,
  "copyCodeButton": true
}
'
{
  "status": 200,
  "message": "Resource updated successfully.",
  "data": {}
}

Overview

Update an existing WhatsApp message template. The template will be resubmitted to Meta for review.
  • Language cannot be changed after template creation
  • Category may be restricted for approved templates
  • Media file only required when changing media type
  • Template status changes to UPDATING during review

Request Body Schema

Same as Create Template, with these differences:
FieldUpdate Restrictions
languageCannot be changed (must match original)
categoryMay be locked if template is approved
header.media.fileRequired only when changing header.media.type

Example Payloads

Update Text Content Only

{
  "name": "welcome_template",
  "language": "en",
  "category": "MARKETING",
  "body": {
    "text": "Welcome {{1}}! Your account is now active. Enjoy {{2}}% off your first order!",
    "variables": ["Customer", "20"]
  },
  "footer": "Valid for 30 days"
}

Update Header Media Type

When changing media type (e.g., from image to video), the file is required:
{
  "name": "product_promo",
  "language": "en",
  "category": "MARKETING",
  "header": {
    "type": "media",
    "media": {
      "type": "video",
      "file": "<binary_file_data>"
    }
  },
  "body": {
    "text": "Check out our latest product showcase!",
    "variables": []
  }
}

Keep Same Media Type

When keeping the same media type, file is optional:
{
  "name": "product_promo",
  "language": "en",
  "category": "MARKETING",
  "header": {
    "type": "media",
    "media": {
      "type": "image"
      // file not required if keeping same type
    }
  },
  "body": {
    "text": "Updated message text here!",
    "variables": []
  }
}

Add Buttons to Existing Template

{
  "name": "order_confirmation",
  "language": "en",
  "category": "UTILITY",
  "body": {
    "text": "Your order #{{1}} is confirmed!",
    "variables": ["ORD-12345"]
  },
  "buttons": [
    {
      "type": "URL",
      "text": "Track Order",
      "url": "https://example.com/track"
    },
    {
      "type": "PHONE_NUMBER",
      "text": "Contact Support",
      "phone_number": "+1234567890"
    }
  ]
}

Response

Success (200 OK)

{
  "success": true,
  "message": "Template updated successfully and submitted to Meta for review."
}

Validation Error (422)

{
  "success": false,
  "message": "Validation failed",
  "errors": {
    "language": ["Language cannot be changed after template creation."],
    "header.media.file": ["The file field is required when changing media type."]
  }
}

Template Status Flow

After updating:
  1. PENDING → Template without provider_id (treated as new creation)
  2. UPDATING → Existing template resubmitted for review
  3. APPROVED → After Meta approval
  4. REJECTED → If Meta rejects the changes
Use the Sync endpoint to refresh template statuses.

Common Update Scenarios

Update body.text and adjust body.variables array to match new placeholders:
{
  "name": "existing_template",
  "language": "en",
  "category": "MARKETING",
  "body": {
    "text": "New message with {{1}} and {{2}}",
    "variables": ["value1", "value2"]
  }
}
Set header to null or omit it:
{
  "name": "existing_template",
  "language": "en",
  "category": "UTILITY",
  "header": null,
  "body": {
    "text": "Simple message without header",
    "variables": []
  }
}
Update the entire buttons array:
{
  "name": "existing_template",
  "language": "en",
  "category": "MARKETING",
  "body": {
    "text": "Click below for updated link",
    "variables": []
  },
  "buttons": [
    {
      "type": "URL",
      "text": "New Link",
      "url": "https://newdomain.com/page"
    }
  ]
}

Important Notes

Breaking Changes
  • Changing language will fail validation
  • Approved templates may have category restrictions
  • Template name follows same rules as creation (lowercase + underscores only)
Best Practices
  • Test updates on duplicate templates first
  • Keep variable placeholders consistent
  • Use Sync endpoint to monitor approval status
  • Backup original template configuration before major updates

For complete payload examples, see TEMPLATE_PAYLOADS.md.

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Path Parameters

workspace
string<uuid>
required

ID of the workspace.

template
integer
required

ID of the template.

Body

application/json
name
string
required
Example:

"login_otp_template"

language
string
required

Language cannot be changed after creation

Example:

"en"

category
enum<string>
required

Category may be restricted based on template status

Available options:
MARKETING,
UTILITY,
AUTHENTICATION
body
object
required
header
object

Option 2: Text Header

Maximum string length: 60
buttons
object[]
unsubscribeButton
boolean
blockButton
boolean
addSecurityRecommendation
boolean

Required for AUTHENTICATION category

codeExpirationMinutes
integer

Required for AUTHENTICATION category

Required range: 1 <= x <= 1440
copyCodeButton
boolean

Required for AUTHENTICATION category

Response

status
integer
required
Example:

200

message
string
required
Example:

"Resource updated successfully."

data
object
required

Updated resource data.