createAppointmentReminder
A reminder TEXTS REAL CONTACTS automatically.
A reminder TEXTS REAL CONTACTS automatically. Before calling, show the user every step's exact message and timing and wait for them to confirm that copy; never invent or reword it and send it unseen. Messages may use {{merge fields}} (the APPOINTMENT keys under MERGE FIELDS in updateWorkflowGraph's description); a step with a field an appointment cannot fill is NOT sent for it, so keep to fields every appointment has. Create a reminder: one SMS to the appointment's linked contacts per step, each sent amountBefore + unitBefore (MINUTES | HOURS | DAYS; amountBefore > 0) before its start, e.g. steps: [{message, amountBefore: 30, unitBefore: MINUTES}] for "30 minutes before". isGlobal: true makes it the org default, replacing any prior one. The org DEFAULT reminder ("Run for every appointment") is attached automatically to booking-page and AI-employee bookings and preselected in the app's appointment form; NOT to appointments created through this API (pass its id as appointmentReminderWorkflowGroupId on createAppointment) nor to existing ones (updateAppointment attaches it). With none flagged and exactly ONE reminder, that one is the default anyway. So the org's FIRST reminder texts new bookings even without isGlobal; any other without it is a preset that sends for nothing until attached. listAppointmentReminders first: when one with the same timing exists, offer to make it the default (setDefaultAppointmentReminder) or edit it rather than add a duplicate.
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": "createAppointmentReminder",
"variables": {
"steps": [
{
"message": "<String>",
"amountBefore": 10,
"unitBefore": "MINUTES"
}
]
}
}'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 = {
steps: [
{
message: '<String>',
amountBefore: 10,
unitBefore: 'MINUTES'
}
]
} as Parameters<typeof goliath.appointments.createAppointmentReminder>[0]
const result = await goliath.appointments.createAppointmentReminder(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: 'createAppointmentReminder',
variables: {
steps: [
{
message: '<String>',
amountBefore: 10,
unitBefore: 'MINUTES'
}
]
}
}),
})
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.
One step of a reminder: a single SMS and how far ahead of the appointment's `startDate` to send it.
Unit for `AppointmentReminder.amountBefore`. Capped at DAYS: a reminder more than a day before the appointment isn't a meaningful product use case.
Response
What comes back under data: a list shows as one entry, and a value as its type.
{
"data": {
"appointmentReminderMutation": {
"createAppointmentReminder": {
"id": "<ID>",
"steps": [
{
"message": "…",
"amountBefore": 0,
"unitBefore": "MINUTES"
}
],
"isGlobal": true,
"createdAt": "2026-01-01T00:00:00.000Z"
}
}
}
}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 →