Skip to main content

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:
NameTypeSourceUsed for
Component type IDUUIDGET /component-typesidcomponentId in structure nodes; identifies what kind of slot this is
Component instance IDUUID (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 typedata_keyUUID
OfferingOffering8e3f0698-7564-4522-9c07-49686667c0b4
Value PropositionValueProposition72150aa9-6b20-4d95-9659-0c05701ef7de
Call to ActionCallToAction8c0b9b73-b305-4f93-b41f-bde62cebac98
GreetingGreeting43cb8b03-4503-48ed-a09a-9110fd260e3d
IncentiveIncentive27eb65fc-75ec-4a38-8576-03672ee6c562
ToneTone0fb13b80-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)

Step 1 — Create the formula

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.
FieldTypeRequiredDescription
message_typestringYespush, sms, whatsapp, web_push, in_app_banner, in_app_fullpage, email, content_cards, surface
namestringNoDisplay name (must be unique within message_type)
structurearrayYesAlways pass [] — structure is set on the message-content in a separate call
labelsarrayNoFormula-level label associations
topic_iduuidNoTopic this formula belongs to
audience_iduuidNoRestrict delivery to a segment
send_startdatetimeNoISO 8601 start of delivery window
is_controlled_messagebooleanNoControlled 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:
FieldTypeRequiredDescription
iduuidYesComponent instance ID — a UUID you generate; used as component_id when creating variants
kindstringYesAlways "variant"
variantTypestringYesComponent type data_key (e.g. "Offering")
componentIduuidYesComponent type UUID from GET /component-types
labelIdintegerNoLabel for the seed variant
childrenarrayYes[{"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.
FieldTypeRequiredDescription
component_iduuidYesThe component instance ID from the structure (the id you generated)
contentstringYesThe alternate’s message copy
label_idintegerNoLabel 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.
FieldTypeRequiredDescription
component_iduuidYesThe component instance ID from the structure
contentstringYesThe alternate’s message copy
label_idintegerNoLabel for this alternate
formula_idintegerNoThe formula this variant belongs to
message_content_iduuidNoThe message-content this variant belongs to

Formula Lifecycle

Draft → Review → Approved → Live / Scheduled / Expired

                 Archived
Use the state-transition endpoints (/review, /approve, /archive) to progress a formula.