package main
import (
"context"
"fmt"
"log"
"os"
"github.com/AhaSend/ahasend-go"
"github.com/AhaSend/ahasend-go/api"
"github.com/AhaSend/ahasend-go/models/requests"
"github.com/google/uuid"
)
func main() {
client := api.NewAPIClient(api.WithAPIKey(os.Getenv("AHASEND_API_KEY")))
accountID := uuid.MustParse(os.Getenv("AHASEND_ACCOUNT_ID"))
ctx := context.Background()
templateID := uuid.MustParse("11111111-1111-4111-8111-111111111111")
// Only the fields you set change, at once. Campaigns already made from
// the template keep their own copies.
response, _, err := client.CampaignTemplatesAPI.UpdateCampaignTemplate(ctx, accountID, templateID, requests.UpdateCampaignTemplateRequest{
Name: ahasend.String("Monthly newsletter, 2026"),
})
if err != nil {
log.Fatalf("Error updating campaign template: %v", err)
}
fmt.Printf("Updated campaign template %s\n", response.Name)
}import { AhaSendClient } from "@ahasend/sdk";
const client = AhaSendClient.fromEnv();
// The SDK sends a fresh Idempotency-Key automatically and reuses it on its own retries.
const templateId = "00000000-0000-4000-8000-000000000013";
// Campaigns already made from the template keep their own copies.
const template = await client.campaignTemplates.update(templateId, {
name: "Monthly newsletter (2026)",
});
console.log("Campaign template updated.", { id: template.id, name: template.name });
curl --request PUT \
--url https://api.ahasend.com/v2/accounts/{account_id}/campaign-templates/{template_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"content": {
"mjml": "<string>",
"html": "<string>",
"text": "<string>",
"text_is_custom": true
}
}
'import requests
url = "https://api.ahasend.com/v2/accounts/{account_id}/campaign-templates/{template_id}"
payload = {
"name": "<string>",
"content": {
"mjml": "<string>",
"html": "<string>",
"text": "<string>",
"text_is_custom": True
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ahasend.com/v2/accounts/{account_id}/campaign-templates/{template_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'name' => '<string>',
'content' => [
'mjml' => '<string>',
'html' => '<string>',
'text' => '<string>',
'text_is_custom' => 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;
}HttpResponse<String> response = Unirest.put("https://api.ahasend.com/v2/accounts/{account_id}/campaign-templates/{template_id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"content\": {\n \"mjml\": \"<string>\",\n \"html\": \"<string>\",\n \"text\": \"<string>\",\n \"text_is_custom\": true\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ahasend.com/v2/accounts/{account_id}/campaign-templates/{template_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"content\": {\n \"mjml\": \"<string>\",\n \"html\": \"<string>\",\n \"text\": \"<string>\",\n \"text_is_custom\": true\n }\n}"
response = http.request(request)
puts response.read_body{
"object": "campaign_template",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"name": "<string>",
"editor": "advanced",
"content": {
"text_is_custom": true,
"mjml": "<string>",
"html": "<string>",
"text": "<string>"
}
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "A request with this idempotency key is already in progress"
}{
"message": "idempotency key was already used with a different request payload"
}{
"message": "Error message"
}{
"message": "Error message"
}Update a Campaign Template via the API
Change the name or the content of an AhaSend campaign template. The change applies immediately, and campaigns made from it keep their own designs.
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/AhaSend/ahasend-go"
"github.com/AhaSend/ahasend-go/api"
"github.com/AhaSend/ahasend-go/models/requests"
"github.com/google/uuid"
)
func main() {
client := api.NewAPIClient(api.WithAPIKey(os.Getenv("AHASEND_API_KEY")))
accountID := uuid.MustParse(os.Getenv("AHASEND_ACCOUNT_ID"))
ctx := context.Background()
templateID := uuid.MustParse("11111111-1111-4111-8111-111111111111")
// Only the fields you set change, at once. Campaigns already made from
// the template keep their own copies.
response, _, err := client.CampaignTemplatesAPI.UpdateCampaignTemplate(ctx, accountID, templateID, requests.UpdateCampaignTemplateRequest{
Name: ahasend.String("Monthly newsletter, 2026"),
})
if err != nil {
log.Fatalf("Error updating campaign template: %v", err)
}
fmt.Printf("Updated campaign template %s\n", response.Name)
}import { AhaSendClient } from "@ahasend/sdk";
const client = AhaSendClient.fromEnv();
// The SDK sends a fresh Idempotency-Key automatically and reuses it on its own retries.
const templateId = "00000000-0000-4000-8000-000000000013";
// Campaigns already made from the template keep their own copies.
const template = await client.campaignTemplates.update(templateId, {
name: "Monthly newsletter (2026)",
});
console.log("Campaign template updated.", { id: template.id, name: template.name });
curl --request PUT \
--url https://api.ahasend.com/v2/accounts/{account_id}/campaign-templates/{template_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"content": {
"mjml": "<string>",
"html": "<string>",
"text": "<string>",
"text_is_custom": true
}
}
'import requests
url = "https://api.ahasend.com/v2/accounts/{account_id}/campaign-templates/{template_id}"
payload = {
"name": "<string>",
"content": {
"mjml": "<string>",
"html": "<string>",
"text": "<string>",
"text_is_custom": True
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ahasend.com/v2/accounts/{account_id}/campaign-templates/{template_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'name' => '<string>',
'content' => [
'mjml' => '<string>',
'html' => '<string>',
'text' => '<string>',
'text_is_custom' => 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;
}HttpResponse<String> response = Unirest.put("https://api.ahasend.com/v2/accounts/{account_id}/campaign-templates/{template_id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"content\": {\n \"mjml\": \"<string>\",\n \"html\": \"<string>\",\n \"text\": \"<string>\",\n \"text_is_custom\": true\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ahasend.com/v2/accounts/{account_id}/campaign-templates/{template_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"content\": {\n \"mjml\": \"<string>\",\n \"html\": \"<string>\",\n \"text\": \"<string>\",\n \"text_is_custom\": true\n }\n}"
response = http.request(request)
puts response.read_body{
"object": "campaign_template",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"name": "<string>",
"editor": "advanced",
"content": {
"text_is_custom": true,
"mjml": "<string>",
"html": "<string>",
"text": "<string>"
}
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "A request with this idempotency key is already in progress"
}{
"message": "idempotency key was already used with a different request payload"
}{
"message": "Error message"
}{
"message": "Error message"
}templates:write.
Changes:
- A campaign template has no draft. The change applies when the request succeeds.
- Campaigns already made from the campaign template keep their own copies of the design. The change applies only to campaigns that you create after it.
- A field that you leave out does not change and is not checked. A request with no field is refused with
400. - A
nameorcontentequal to the current one changes nothing, so you can repeat the request safely. - You cannot change the editor.
- You can send back the
contentof Get Campaign Template with your changes. Leave outcontentwhen you do not change it. - For a
simplecampaign template, send onlycontent.text: you can change its design only in the dashboard. - When you send a new
content.mjmlorcontent.htmland leave outcontent.text, a text written by hand stays, and a text made from the HTML is made again from the new HTML. Sendcontent.textasnullto make the text again from the new HTML, also when the text was written by hand. - A text written by hand that stays must still render. If it holds a
{{,{%or{#that cannot be rendered, the request is refused with400. - The content follows the rules of Create Campaign Template: strict MJML, full
https://URLs for images and stylesheets, and at most 200 KB each for the MJML, the HTML and a text written by hand.
Idempotency-Key header. See Idempotency.
Related Guides
- API authentication, Permission scopes, Idempotency.
- Campaigns API overview, API errors, API rate limits.
Authorizations
API key for authentication. Non-empty Security Requirement values are AhaSend API-key roles. Roles listed within one requirement object are jointly required; separate requirement objects are alternatives.
Headers
Optional idempotency key for safe request retries. Must be a unique string for each logical request.
An identical request with a completed stored outcome returns the original status and body. An in-progress
execution returns 409, a changed method/path/body returns 422, and a released 5xx execution may run again.
Keys for non-secret responses expire after 24 hours. API-key create responses include a one-time secret_key,
so successful encrypted replay responses for those operations expire after 5 minutes.
255Path Parameters
Account ID
Campaign template ID
Body
Changes to a campaign template. A field left out is not changed or checked. The editor never changes.
Campaign template name. Spaces around it are dropped.
1 - 255The content, by the rules of TemplateContentInput. A campaign template has no draft, so where those rules name the draft, they mean the campaign template itself. For a simple campaign template, send only text.
Show child attributes
Show child attributes
Response
Campaign template updated
A design a campaign copies when it is made from it. A campaign template has no draft, no versions, no subject and no sender; the campaign owns its subject and sender.
Object type identifier
campaign_template Campaign template ID
When the campaign template was created
When the campaign template last changed
Campaign template name, as it appears in the dashboard
The editor the template is designed with. It never changes after the template is created.
advanced: designed in MJML, with the dashboard's advanced editor.simple: designed with the dashboard's simple editor. Its HTML is made from the design and can be read but not changed through the API.html: an HTML template, uploaded or written by hand.
advanced, simple, html The content. Null when the campaign template has neither HTML nor text, as for one created without content. A campaign can be made from the campaign template only once it has HTML. List Campaign Templates leaves this field out; every other operation that returns a CampaignTemplate sets it.
Show child attributes
Show child attributes

