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",
"body": {
"text": "<string>",
"variables": [
"<string>"
]
},
"header": {
"type": "text",
"text": {
"content": "<string>",
"variables": [
"<string>"
]
}
},
"footer": "<string>",
"buttons": [
{
"text": "<string>",
"phone_number": "<string>",
"url": "<string>",
"variables": [
"<string>"
]
}
],
"unsubscribeButton": true,
"blockButton": true,
"addSecurityRecommendation": true,
"codeExpirationMinutes": 720,
"copyCodeButton": true
}
'import requests
url = "https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}"
payload = {
"name": "login_otp_template",
"language": "en",
"body": {
"text": "<string>",
"variables": ["<string>"]
},
"header": {
"type": "text",
"text": {
"content": "<string>",
"variables": ["<string>"]
}
},
"footer": "<string>",
"buttons": [
{
"text": "<string>",
"phone_number": "<string>",
"url": "<string>",
"variables": ["<string>"]
}
],
"unsubscribeButton": True,
"blockButton": True,
"addSecurityRecommendation": True,
"codeExpirationMinutes": 720,
"copyCodeButton": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'login_otp_template',
language: 'en',
body: JSON.stringify({text: '<string>', variables: ['<string>']}),
header: {type: 'text', text: {content: '<string>', variables: ['<string>']}},
footer: '<string>',
buttons: [
{
text: '<string>',
phone_number: '<string>',
url: '<string>',
variables: ['<string>']
}
],
unsubscribeButton: true,
blockButton: true,
addSecurityRecommendation: true,
codeExpirationMinutes: 720,
copyCodeButton: true
})
};
fetch('https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'login_otp_template',
'language' => 'en',
'body' => [
'text' => '<string>',
'variables' => [
'<string>'
]
],
'header' => [
'type' => 'text',
'text' => [
'content' => '<string>',
'variables' => [
'<string>'
]
]
],
'footer' => '<string>',
'buttons' => [
[
'text' => '<string>',
'phone_number' => '<string>',
'url' => '<string>',
'variables' => [
'<string>'
]
]
],
'unsubscribeButton' => true,
'blockButton' => true,
'addSecurityRecommendation' => true,
'codeExpirationMinutes' => 720,
'copyCodeButton' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}"
payload := strings.NewReader("{\n \"name\": \"login_otp_template\",\n \"language\": \"en\",\n \"body\": {\n \"text\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n },\n \"header\": {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n },\n \"footer\": \"<string>\",\n \"buttons\": [\n {\n \"text\": \"<string>\",\n \"phone_number\": \"<string>\",\n \"url\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n ],\n \"unsubscribeButton\": true,\n \"blockButton\": true,\n \"addSecurityRecommendation\": true,\n \"codeExpirationMinutes\": 720,\n \"copyCodeButton\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"login_otp_template\",\n \"language\": \"en\",\n \"body\": {\n \"text\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n },\n \"header\": {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n },\n \"footer\": \"<string>\",\n \"buttons\": [\n {\n \"text\": \"<string>\",\n \"phone_number\": \"<string>\",\n \"url\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n ],\n \"unsubscribeButton\": true,\n \"blockButton\": true,\n \"addSecurityRecommendation\": true,\n \"codeExpirationMinutes\": 720,\n \"copyCodeButton\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"login_otp_template\",\n \"language\": \"en\",\n \"body\": {\n \"text\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n },\n \"header\": {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n },\n \"footer\": \"<string>\",\n \"buttons\": [\n {\n \"text\": \"<string>\",\n \"phone_number\": \"<string>\",\n \"url\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n ],\n \"unsubscribeButton\": true,\n \"blockButton\": true,\n \"addSecurityRecommendation\": true,\n \"codeExpirationMinutes\": 720,\n \"copyCodeButton\": true\n}"
response = http.request(request)
puts response.read_body{
"status": 200,
"message": "Resource updated successfully.",
"data": {}
}{
"message": "Unauthenticated."
}{
"message": "Resource not found.",
"status": 404
}Templates
Update Template
Update an existing template. Supports the same payload structure as template creation.
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",
"body": {
"text": "<string>",
"variables": [
"<string>"
]
},
"header": {
"type": "text",
"text": {
"content": "<string>",
"variables": [
"<string>"
]
}
},
"footer": "<string>",
"buttons": [
{
"text": "<string>",
"phone_number": "<string>",
"url": "<string>",
"variables": [
"<string>"
]
}
],
"unsubscribeButton": true,
"blockButton": true,
"addSecurityRecommendation": true,
"codeExpirationMinutes": 720,
"copyCodeButton": true
}
'import requests
url = "https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}"
payload = {
"name": "login_otp_template",
"language": "en",
"body": {
"text": "<string>",
"variables": ["<string>"]
},
"header": {
"type": "text",
"text": {
"content": "<string>",
"variables": ["<string>"]
}
},
"footer": "<string>",
"buttons": [
{
"text": "<string>",
"phone_number": "<string>",
"url": "<string>",
"variables": ["<string>"]
}
],
"unsubscribeButton": True,
"blockButton": True,
"addSecurityRecommendation": True,
"codeExpirationMinutes": 720,
"copyCodeButton": True
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'login_otp_template',
language: 'en',
body: JSON.stringify({text: '<string>', variables: ['<string>']}),
header: {type: 'text', text: {content: '<string>', variables: ['<string>']}},
footer: '<string>',
buttons: [
{
text: '<string>',
phone_number: '<string>',
url: '<string>',
variables: ['<string>']
}
],
unsubscribeButton: true,
blockButton: true,
addSecurityRecommendation: true,
codeExpirationMinutes: 720,
copyCodeButton: true
})
};
fetch('https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'login_otp_template',
'language' => 'en',
'body' => [
'text' => '<string>',
'variables' => [
'<string>'
]
],
'header' => [
'type' => 'text',
'text' => [
'content' => '<string>',
'variables' => [
'<string>'
]
]
],
'footer' => '<string>',
'buttons' => [
[
'text' => '<string>',
'phone_number' => '<string>',
'url' => '<string>',
'variables' => [
'<string>'
]
]
],
'unsubscribeButton' => true,
'blockButton' => true,
'addSecurityRecommendation' => true,
'codeExpirationMinutes' => 720,
'copyCodeButton' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}"
payload := strings.NewReader("{\n \"name\": \"login_otp_template\",\n \"language\": \"en\",\n \"body\": {\n \"text\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n },\n \"header\": {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n },\n \"footer\": \"<string>\",\n \"buttons\": [\n {\n \"text\": \"<string>\",\n \"phone_number\": \"<string>\",\n \"url\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n ],\n \"unsubscribeButton\": true,\n \"blockButton\": true,\n \"addSecurityRecommendation\": true,\n \"codeExpirationMinutes\": 720,\n \"copyCodeButton\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"login_otp_template\",\n \"language\": \"en\",\n \"body\": {\n \"text\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n },\n \"header\": {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n },\n \"footer\": \"<string>\",\n \"buttons\": [\n {\n \"text\": \"<string>\",\n \"phone_number\": \"<string>\",\n \"url\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n ],\n \"unsubscribeButton\": true,\n \"blockButton\": true,\n \"addSecurityRecommendation\": true,\n \"codeExpirationMinutes\": 720,\n \"copyCodeButton\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://lancepilot.com/api/v3/workspaces/{workspace}/templates/{template}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"login_otp_template\",\n \"language\": \"en\",\n \"body\": {\n \"text\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n },\n \"header\": {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n },\n \"footer\": \"<string>\",\n \"buttons\": [\n {\n \"text\": \"<string>\",\n \"phone_number\": \"<string>\",\n \"url\": \"<string>\",\n \"variables\": [\n \"<string>\"\n ]\n }\n ],\n \"unsubscribeButton\": true,\n \"blockButton\": true,\n \"addSecurityRecommendation\": true,\n \"codeExpirationMinutes\": 720,\n \"copyCodeButton\": true\n}"
response = http.request(request)
puts response.read_body{
"status": 200,
"message": "Resource updated successfully.",
"data": {}
}{
"message": "Unauthenticated."
}{
"message": "Resource not found.",
"status": 404
}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
UPDATINGduring review
Request Body Schema
Same as Create Template, with these differences:| Field | Update Restrictions |
|---|---|
language | Cannot be changed (must match original) |
category | May be locked if template is approved |
header.media.file | Required 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:- PENDING → Template without
provider_id(treated as new creation) - UPDATING → Existing template resubmitted for review
- APPROVED → After Meta approval
- REJECTED → If Meta rejects the changes
Common Update Scenarios
Changing Template Text
Changing Template Text
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"]
}
}
Removing Header
Removing Header
Set
header to null or omit it:{
"name": "existing_template",
"language": "en",
"category": "UTILITY",
"header": null,
"body": {
"text": "Simple message without header",
"variables": []
}
}
Changing Button URLs
Changing Button URLs
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"
}
]
}
Adding/Removing Footer
Adding/Removing Footer
Simply include or omit the
footer field:{
"name": "existing_template",
"language": "en",
"category": "UTILITY",
"body": {
"text": "Message content here",
"variables": []
},
"footer": "New footer text"
}
Important Notes
Breaking Changes
- Changing
languagewill 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
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
ID of the workspace.
ID of the template.
Body
application/json
Example:
"login_otp_template"
Language cannot be changed after creation
Example:
"en"
Category may be restricted based on template status
Available options:
MARKETING, UTILITY, AUTHENTICATION Show child attributes
Show child attributes
Option 2: Text Header
- Option 1
- Option 2
Show child attributes
Show child attributes
Maximum string length:
60Show child attributes
Show child attributes
Required for AUTHENTICATION category
Required for AUTHENTICATION category
Required range:
1 <= x <= 1440Required for AUTHENTICATION category
⌘I