createContentTemplate
Create a reusable template. type is EMAIL | SMS | NOTE | TASK | TASK_SET.
Create a reusable template. type is EMAIL | SMS | NOTE | TASK | TASK_SET. The nested input carries name, bodyContent (merge fields and [a|b] spintax allowed) and subjectContent (email only). Its merge fields are the vocabulary of WHATEVER NODE renders it, since a template has no domain of its own: from a CONTACT node or applyTaskTemplate, the CONTACT vocabulary listed under updateWorkflowGraph ({{firstName}}, {{contactPropertyAddress}}, {{userFirstName}}, …); from an APPOINTMENT text node, that PLUS the appointment fields ({{appointmentStartTime}}, …). Quote merge fields to the person as [First name], not raw keys, unless they check spelling. Keys match EXACTLY and case-sensitively, with no dotted or snake_case form: {{merge_fields}} and {{contact.firstName}} send the literal braces. TASK (the checklist/SOP step under Automations → Templates) takes input.taskConfig; bodyContent holds the task description and the due date is RELATIVE, resolved when applied. TASK_SET is a whole SOP: bodyContent "", no taskConfig, and input.taskSetSteps of 1-25 steps (their rules are on the input shape), applied in one call with applyTaskSet.
curl https://server.goliathdata.com/api/v1/call \
-H "Authorization: Bearer $GOLIATH_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"operationId": "createContentTemplate",
"variables": {
"input": {
"name": "<String>",
"input": {
"name": "<String>",
"bodyContent": "<String>"
},
"type": "EMAIL"
}
}
}'import { GoliathClient } from '@goliath-data/sdk'
const goliath = new GoliathClient({
apiKey: process.env.GOLIATH_API_KEY!,
baseUrl: 'https://server.goliathdata.com',
})
// Enum values as strings: the SDK's enum types aren't exported yet, so assert to the method's variables type.
const variables = {
input: {
name: '<String>',
input: {
name: '<String>',
bodyContent: '<String>'
},
type: 'EMAIL'
}
} as Parameters<typeof goliath.workflows.createContentTemplate>[0]
const result = await goliath.workflows.createContentTemplate(variables, { idempotencyKey: crypto.randomUUID() })const res = await fetch('https://server.goliathdata.com/api/v1/call', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.GOLIATH_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
operationId: 'createContentTemplate',
variables: {
input: {
name: '<String>',
input: {
name: '<String>',
bodyContent: '<String>'
},
type: 'EMAIL'
}
}
}),
})
const { data, errors } = await res.json()Variables
The example sends only what the schema requires. Some operations also need one of several optional variables (one filter or another, say); the description above says which.
TASK_SET: A named collection of TASK templates, each with its own due offset and time-of-day, applied to a contact in one gesture. Carries no body and no taskConfig of its own: see `ContentTemplate.taskSetSteps`.
Every field, all the way down
[
{
"name": "input",
"type": "CreateContentTemplateInput!",
"required": true,
"description": null,
"defaultValue": null,
"fields": [
{
"name": "name",
"type": "String!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "input",
"type": "ContentTemplateMutationInput!",
"required": true,
"description": null,
"defaultValue": null,
"fields": [
{
"name": "name",
"type": "String!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "bodyContent",
"type": "String!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "bodyFormat",
"type": "String",
"required": false,
"description": null,
"defaultValue": null
},
{
"name": "subjectContent",
"type": "String",
"required": false,
"description": null,
"defaultValue": null
},
{
"name": "attachments",
"type": "[ContentTemplateAttachmentInput!]",
"required": false,
"description": null,
"defaultValue": null,
"fields": [
{
"name": "uploadedFileId",
"type": "ID!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "fileName",
"type": "String!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "contentType",
"type": "String!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "fileSize",
"type": "Int!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "page",
"type": "ContentTemplateAttachmentPageInput",
"required": false,
"description": null,
"defaultValue": null,
"fields": [
{
"name": "itemId",
"type": "ID!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "versionNumber",
"type": "Int!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "sendsLatestAs",
"type": "ID",
"required": false,
"description": null,
"defaultValue": null
}
]
}
]
},
{
"name": "taskConfig",
"type": "TaskTemplateConfigInput",
"required": false,
"description": "Structured config for a TASK content template. The template's `bodyContent` holds the task description and `name` is the template label; everything else task-shaped lives here. Assignee is intentionally absent: an applied task template is always assigned to the acting user at apply time.",
"defaultValue": null,
"fields": [
{
"name": "title",
"type": "String!",
"required": true,
"description": "Task title. Rendered with merge fields + spintax against the contact at apply time.",
"defaultValue": null
},
{
"name": "taskType",
"type": "String",
"required": false,
"description": "Free-text subtype label (maps to `Task.taskType`).",
"defaultValue": null
},
{
"name": "dueOffset",
"type": "TaskTemplateRelativeDateOffsetInput!",
"required": true,
"description": "Due date as a relative offset from apply time.",
"defaultValue": null,
"fields": [
{
"name": "amount",
"type": "Int!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "unit",
"type": "TaskTemplateRelativeTimeUnit!",
"required": true,
"description": null,
"defaultValue": null,
"enumValues": [
"DAYS",
"WEEKS",
"MONTHS"
]
}
]
},
{
"name": "preferredTimeOfDay",
"type": "TaskTemplateTimeOfDayInput!",
"required": true,
"description": "Time-of-day the resolved due date snaps to.",
"defaultValue": null,
"fields": [
{
"name": "hour",
"type": "Int!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "minute",
"type": "Int!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "timezone",
"type": "Timezone!",
"required": true,
"description": "AZ: Arizona: MST all year, no DST.",
"defaultValue": null,
"enumValues": [
"IDLW",
"SST",
"HST",
"AKST",
"PST",
"MST",
"AZ",
"CST",
"EST",
"AST",
"BRT",
"FNT",
"AZOT",
"UTC",
"CET",
"EET",
"MSK",
"GST",
"PKT",
"BST",
"ICT",
"JST",
"AEST",
"NCT",
"NZST"
]
}
]
},
{
"name": "autoCompleteOnNoteAdded",
"type": "Boolean",
"required": false,
"description": null,
"defaultValue": null
},
{
"name": "autoCompleteOnReassignment",
"type": "Boolean",
"required": false,
"description": null,
"defaultValue": null
}
]
},
{
"name": "taskSetSteps",
"type": "[TaskSetStepInput!]",
"required": false,
"description": "One step of a TASK_SET template. `taskTemplateId` must name a TASK template in the caller's organization: a TASK_SET is rejected (a set cannot contain a set). The same template may appear more than once. The timing here WINS over the referenced template's own `taskConfig` timing.",
"defaultValue": null,
"fields": [
{
"name": "taskTemplateId",
"type": "ID!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "dueOffset",
"type": "TaskTemplateRelativeDateOffsetInput!",
"required": true,
"description": null,
"defaultValue": null,
"fields": [
{
"name": "amount",
"type": "Int!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "unit",
"type": "TaskTemplateRelativeTimeUnit!",
"required": true,
"description": null,
"defaultValue": null,
"enumValues": [
"DAYS",
"WEEKS",
"MONTHS"
]
}
]
},
{
"name": "preferredTimeOfDay",
"type": "TaskTemplateTimeOfDayInput!",
"required": true,
"description": null,
"defaultValue": null,
"fields": [
{
"name": "hour",
"type": "Int!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "minute",
"type": "Int!",
"required": true,
"description": null,
"defaultValue": null
},
{
"name": "timezone",
"type": "Timezone!",
"required": true,
"description": "AZ: Arizona: MST all year, no DST.",
"defaultValue": null,
"enumValues": [
"IDLW",
"SST",
"HST",
"AKST",
"PST",
"MST",
"AZ",
"CST",
"EST",
"AST",
"BRT",
"FNT",
"AZOT",
"UTC",
"CET",
"EET",
"MSK",
"GST",
"PKT",
"BST",
"ICT",
"JST",
"AEST",
"NCT",
"NZST"
]
}
]
}
]
}
]
},
{
"name": "type",
"type": "ContentTemplateType!",
"required": true,
"description": "TASK_SET: A named collection of TASK templates, each with its own due offset and time-of-day, applied to a contact in one gesture. Carries no body and no taskConfig of its own: see `ContentTemplate.taskSetSteps`.",
"defaultValue": null,
"enumValues": [
"EMAIL",
"SMS",
"NOTE",
"TASK",
"TASK_SET"
]
},
{
"name": "folderId",
"type": "ID",
"required": false,
"description": null,
"defaultValue": null
}
]
}
]Response
What comes back under data: a list shows as one entry, and a value as its type.
{
"data": {
"workflowAutomationsMutation": {
"createContentTemplate": {
"id": "<ID>",
"name": "…",
"type": "EMAIL",
"folderId": "<ID>",
"taskSetSteps": [
{
"id": "<ID>",
"taskTemplate": {
"id": "<ID>",
"name": "…"
},
"dueOffset": {
"amount": 0,
"unit": "DAYS"
},
"preferredTimeOfDay": {
"hour": 0,
"minute": 0,
"timezone": "IDLW"
}
}
]
}
}
}
}Errors
unknown_variableA variable the operation does not declare. A typo is refused rather than silently dropped.
unauthorizedNo bearer header, or the key is invalid or revoked.
insufficient_scopeThe key lacks the scope the operation requires.
forbiddenA referenced record does not exist, was deleted, or is outside your organization. The gateway cannot tell those apart.
idempotency_conflictA request with this Idempotency-Key is still in flight.
Idempotency
Send an Idempotency-Key of 1 to 255 characters. A finished request retried with the same key replays its stored response, marked Idempotency-Replayed: true; one still in flight returns 409 idempotency_conflict. Keys expire after 24 hours. Write without fear →