Overview
A formula is a reusable message template that drives a campaign. It defines the channel (push, SMS, email, etc.), message slots, and the labeled alternates Aampe uses to personalize each send.
Formulas start in Draft status and must go through a review/approval workflow before going live.
Core Concepts
Slots vs. Alternates
The structure defines slots — one position per section (header, body). It does not list all alternates inline. Alternates for a slot are linked by a shared component_id.
Formula structure
└── section (header)
│ └── ONE variant node ← slot; node id = component_id for this slot
└── section (body)
└── ONE variant node ← slot; node id = component_id for this slot
Variants (separate)
├── component_id = header_slot_id label = Calm ← alternate 1
├── component_id = header_slot_id label = Breathe ← alternate 2
├── component_id = header_slot_id label = Stillness ← alternate 3
├── component_id = body_slot_id label = Science ← alternate 1
└── component_id = body_slot_id label = Sleep ← alternate 2
Component IDs — two distinct concepts
There are two different IDs called “component ID” and confusing them is the most common source of errors:
| Name | Type | Source | Used for |
|---|
| Component type ID | UUID | GET /component-types → id | componentId in structure nodes; identifies what kind of slot this is |
| Component instance ID | UUID (self-generated) | You create it with uuid.uuid4() | Variant node id in the structure; component_id when creating variants — links all alternates to the same slot |
Seed variant
When you put text in a variant node’s children[0].text, that text is automatically created as the first alternate for that slot. Do not re-submit it via the bulk endpoint — it will create a duplicate.
componentId — component type UUIDs
The componentId in a structure variant node is the component type UUID from GET /component-types:
| Component type | data_key | UUID |
|---|
| Offering | Offering | 8e3f0698-7564-4522-9c07-49686667c0b4 |
| Value Proposition | ValueProposition | 72150aa9-6b20-4d95-9659-0c05701ef7de |
| Call to Action | CallToAction | 8c0b9b73-b305-4f93-b41f-bde62cebac98 |
| Greeting | Greeting | 43cb8b03-4503-48ed-a09a-9110fd260e3d |
| Incentive | Incentive | 27eb65fc-75ec-4a38-8576-03672ee6c562 |
| Tone | Tone | 0fb13b80-d999-48c3-9ca9-3975c0af0bf7 |
Fetch the live list at any time:
response = requests.get(
"https://composer.api.aampe.com/api/component-types",
headers={"X-API-KEY": "YOUR_API_KEY"},
)
CT = {ct["data_key"]: ct["id"] for ct in response.json()}
Labels and variant_type
When associating a label with a variant, the label’s variant_type must exactly match the data_key of the component type.
A label created with variant_type: "Offering" cannot be used on a ValueProposition variant, and vice versa. Create separate labels for each component type you need.
Creation Flow (4 Steps)
Create the formula with structure: []. The message structure is set separately on the message-content record in Step 3.
import uuid, requests
BASE_URL = "https://composer.api.aampe.com/api"
headers = {"X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json"}
CT_OFFERING = "8e3f0698-7564-4522-9c07-49686667c0b4"
CT_VALUE_PROPOSITION = "72150aa9-6b20-4d95-9659-0c05701ef7de"
response = requests.post(f"{BASE_URL}/formulas", headers=headers, json={
"name": "My Formula",
"message_type": "push",
"structure": [],
"labels": [
{"label_id": 101, "variant_type": "Offering", "component_type_id": CT_OFFERING},
{"label_id": 102, "variant_type": "Offering", "component_type_id": CT_OFFERING},
{"label_id": 103, "variant_type": "ValueProposition", "component_type_id": CT_VALUE_PROPOSITION},
],
})
response.raise_for_status()
formula_id = response.json()["id"]
Step 2 — Fetch the message-content ID
When a formula is created, the API automatically creates a message-content record for it. Fetch its ID:
mc_resp = requests.get(
f"{BASE_URL}/formulas/{formula_id}/message-contents",
headers=headers,
)
mc_resp.raise_for_status()
message_content_id = mc_resp.json()[0]["id"]
Step 3 — Set the structure
Pre-generate a UUID for each slot. That UUID is the component instance ID — it links all of that slot’s alternates together and must be referenced when creating variants in Step 4.
The text in children[0].text is the seed variant — it is automatically created as the first alternate for that slot.
# Pre-generate component instance IDs (one per slot)
header_component_id = str(uuid.uuid4())
body_component_id = str(uuid.uuid4())
put_resp = requests.put(
f"{BASE_URL}/message-contents/{message_content_id}",
headers=headers,
json={
"id": message_content_id,
"structure": [
{
"kind": "section",
"sectionType": "header",
"children": [
{
"id": header_component_id, # ← you generated this
"kind": "variant",
"variantType": "Offering",
"componentId": CT_OFFERING, # ← component type UUID
"labelId": 101, # ← label for the seed variant
"children": [{"text": "Your first header alternate"}],
}
],
},
{
"kind": "section",
"sectionType": "body",
"children": [
{
"id": body_component_id,
"kind": "variant",
"variantType": "ValueProposition",
"componentId": CT_VALUE_PROPOSITION,
"labelId": 103,
"children": [{"text": "Your first body alternate"}],
}
],
},
],
"deeplink": None,
"deeplink_dataset_field_id": None,
},
)
put_resp.raise_for_status()
Each slot must have exactly one variant node in the structure. Do not add multiple sibling variant nodes — they render as separate inline components, not as alternatives for the same slot.
Step 4 — Add the remaining alternates
Add all remaining alternates using either POST /variants/create/bulk (multiple at once) or POST /variants (one at a time). Use the component instance IDs you generated in Step 3. Do not include the seed variants — they were already created in Step 3.
bulk_resp = requests.post(
f"{BASE_URL}/variants/create/bulk",
headers=headers,
json=[
# Additional header alternates (seed "Your first header alternate" was already created)
{"component_id": header_component_id, "content": "Your second header alternate", "label_id": 102},
{"component_id": header_component_id, "content": "Your third header alternate", "label_id": 101},
# Additional body alternates
{"component_id": body_component_id, "content": "Your second body alternate", "label_id": 103},
],
)
bulk_resp.raise_for_status()
After this call the formula has 3 header alternates × 2 body alternates = 6 unique message combinations.
Endpoint Reference
POST /formulas
Creates a new formula.
| Field | Type | Required | Description |
|---|
message_type | string | Yes | push, sms, whatsapp, web_push, in_app_banner, in_app_fullpage, email, content_cards, surface |
name | string | No | Display name (must be unique within message_type) |
structure | array | Yes | Always pass [] — structure is set on the message-content in a separate call |
labels | array | No | Formula-level label associations |
topic_id | uuid | No | Topic this formula belongs to |
audience_id | uuid | No | Restrict delivery to a segment |
send_start | datetime | No | ISO 8601 start of delivery window |
is_controlled_message | boolean | No | Controlled A/B test (default: false) |
labels item:
{"label_id": 101, "variant_type": "Offering", "component_type_id": CT_OFFERING}
PUT /message-contents/{id}
Sets the message structure and seed variants on the message-content record.
Variant node fields:
| Field | Type | Required | Description |
|---|
id | uuid | Yes | Component instance ID — a UUID you generate; used as component_id when creating variants |
kind | string | Yes | Always "variant" |
variantType | string | Yes | Component type data_key (e.g. "Offering") |
componentId | uuid | Yes | Component type UUID from GET /component-types |
labelId | integer | No | Label for the seed variant |
children | array | Yes | [{"text": "seed text"}] — becomes the first alternate for this slot |
POST /variants/create/bulk
Creates multiple variants in a single request. Use this when you have several alternates to add at once.
| Field | Type | Required | Description |
|---|
component_id | uuid | Yes | The component instance ID from the structure (the id you generated) |
content | string | Yes | The alternate’s message copy |
label_id | integer | No | Label for this alternate |
POST /variants
Creates a single variant. Use this for one-off additions. Accepts the same fields as a bulk item, plus formula_id and message_content_id for context.
| Field | Type | Required | Description |
|---|
component_id | uuid | Yes | The component instance ID from the structure |
content | string | Yes | The alternate’s message copy |
label_id | integer | No | Label for this alternate |
formula_id | integer | No | The formula this variant belongs to |
message_content_id | uuid | No | The message-content this variant belongs to |
Draft → Review → Approved → Live / Scheduled / Expired
↓
Archived
Use the state-transition endpoints (/review, /approve, /archive) to progress a formula.