# Aampe API Guide Source: https://kb.aampe.com/api-guide/api-guide ## Overview Aampe provides two main APIs that work together to power personalized experiences: | API | Purpose | Base URL | | - | - | - | | **Content API** | Retrieve personalized content for Surfaces | `https://content.{region}.api.aampe.com` | | **Ingestion API** | Send user events and properties to Aampe | `https://ingestion.{region}.api.aampe.com` | Both APIs require authentication. [See Authentication](/api-guide/authentication) for how to authenticate with each. *** ## Content API (Surfaces) The Content API provides programmatic access to retrieve personalized content for your users across different surfaces. This API powers real-time content decisioning based on your configured agents and optimization strategies. **Base URL:** `https://content.{region}.api.aampe.com` **Regions:** `us-east1` | `europe-west3` | `asia-southeast1` **Authentication:** `X-API-Key` header (Surfaces API key) ### Use Cases * Fetch personalized content for in-app Surfaces * Get content assignments for a user at a specific surface * Verify API connection via health endpoint *** ## Ingestion API (Data) The Ingestion API allows you to send user events and property updates to Aampe in real time. Use it to stream behavioral data and user attributes that power personalization. **Base URL:** `https://ingestion.{region}.api.aampe.com` **Regions:** `us-east1` | `europe-west3` | `asia-southeast1` **Authentication:** `Authorization: Bearer ` header ### Use Cases * Stream user events (clicks, purchases, page views) as they happen * Update user properties (attributes, traits, preferences) * Power real-time personalization and audience targeting *** ## Core Concepts ### Surfaces Surfaces represent locations in your application where Aampe can deliver personalized content. Each surface has a unique UUID and associated configuration. ### Assignments When you request content for a surface, Aampe returns an assignment — the specific content variant selected for that user at that moment, along with any configured alternates. ### Contact IDs Your user identifiers that Aampe uses to personalize content delivery and track performance. Use the same contact IDs when sending events (Ingestion API) and requesting content (Content API). *** ## Response Formats All API responses return JSON. Successful requests return appropriate HTTP status codes along with the requested data. *** ## Error Handling Both APIs use standard HTTP status codes: | Code | Description | | - | - | | 200 | Success (Content API) | | 202 | Accepted (Ingestion API — request queued for processing) | | 400 | Bad Request | | 401 | Unauthorized (invalid or missing API key) | | 422 | Validation Error | | 429 | Rate Limited | | 500 | Server Error | *** ## Getting Started 1. **Get your API keys** — Navigate to Integrations > API keys in the Aampe Dashboard 2. **Choose your API** — Use the Surfaces key for Content API, Ingestion key for Ingestion API 3. **Test your connection** — Use the health endpoint for Content API 4. **Start building** — Send events, update properties, and fetch personalized content *** ## Need Help? Feeling stuck? We're here to help! Reach out to your customer service rep and we will help you build something amazing with Aampe. # API Keys Source: https://kb.aampe.com/api-guide/api-keys Where can I find my API keys? ## Aampe API Keys To find your API keys, navigate to **Integrations > API keys** Composer Api Key Page Pn You'll find two API keys * **Ingestion API key:** this is for sending data to Aampe via the [Aampe Streaming API](/developer-guide/data-ingest/data-sources/streaming-aampe-api). * **Surfaces API key:** this is for pulling personalized content from Aampe for [Surfaces](/developer-guide/surfaces/surfaces-embed). Please keep your API keys secure. # Authentication Source: https://kb.aampe.com/api-guide/authentication Aampe has two APIs with **different authentication methods**. Use the correct method for the API you're calling. *** ## Quick Reference | API | Header | Example | Base URL | | - | - | - | - | | **Content API** (Surfaces) | `X-API-Key` | `X-API-Key: your_surfaces_api_key` | `https://content.api.aampe.com` | | **Ingestion API** (Data) | `Authorization: Bearer` | `Authorization: Bearer your_ingestion_api_key` | `https://ingestion.{region}.api.aampe.com` | *** ## Content API Authentication Use the **Surfaces API key** when calling the Content API (e.g., Get Surface Content, Health). Your Surfaces API key for the Content API **Base URL:** `https://content.api.aampe.com` ```bash cURL theme={null} curl -X GET \ 'https://content.api.aampe.com/health' \ -H 'X-API-Key: YOUR_SURFACES_API_KEY' ``` ```python Python theme={null} import requests response = requests.get( "https://content.api.aampe.com/health", headers={"X-API-Key": "YOUR_SURFACES_API_KEY"} ) print(response.json()) ``` ```javascript JavaScript theme={null} const response = await fetch('https://content.api.aampe.com/health', { headers: { 'X-API-Key': 'YOUR_SURFACES_API_KEY' } }); const data = await response.json(); console.log(data); ``` *** ## Ingestion API Authentication Use the **Ingestion API key** when sending events or property updates to Aampe. Bearer token with your Ingestion API key. Format: `Authorization: Bearer YOUR_INGESTION_API_KEY` **Base URL:** `https://ingestion.{region}.api.aampe.com` (e.g., `us-east1`, `europe-west3`, `asia-southeast1`) ```bash cURL theme={null} curl -X POST \ 'https://ingestion.us-east1.api.aampe.com/v1/events' \ -H 'Authorization: Bearer YOUR_INGESTION_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "contact_id": "user123", "event_name": "button_click", "timestamp": 1638360000 }' ``` ```python Python theme={null} import requests response = requests.post( "https://ingestion.us-east1.api.aampe.com/v1/events", headers={ "Authorization": "Bearer YOUR_INGESTION_API_KEY", "Content-Type": "application/json" }, json={ "contact_id": "user123", "event_name": "button_click", "timestamp": 1638360000 } ) print(response.status_code) # 202 = Accepted ``` ```javascript JavaScript theme={null} const response = await fetch('https://ingestion.us-east1.api.aampe.com/v1/events', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_INGESTION_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ contact_id: 'user123', event_name: 'button_click', timestamp: 1638360000 }) }); console.log(response.status); // 202 = Accepted ``` *** ## Obtaining an API Key To find your API keys, navigate to **Integrations > API keys** Composer Api Key Page Pn You'll find two API keys: * **Surfaces API key:** For pulling personalized content from Aampe for [Surfaces](/developer-guide/surfaces/surfaces-embed) * **Ingestion API key:** For sending data to Aampe via the [Aampe Streaming API](/developer-guide/data-ingest/data-sources/streaming-aampe-api) ### Key Generation Steps 1. Log into your Aampe Dashboard 2. Navigate to System settings → Integrations → Aampe Endpoint 3. Copy the API Key (hidden) *** ## Configure API Playground To test endpoints interactively: 1. Click the "Configure" button on any API endpoint page 2. Enter your API key in the appropriate field: * **Content API endpoints:** Use `X-API-Key` field * **Ingestion API endpoints:** Use Bearer token field 3. Your key will be saved locally in your browser for this session 4. Try making requests directly from the documentation Your API key is stored locally in your browser and never sent to our documentation servers. It's only used for direct API calls to the Aampe API. *** ## Security Best Practices Never expose your API key in client-side code or public repositories. Use environment variables or secure secret management for your keys. Rotate keys if you suspect they've been compromised. *** ## Next Steps Verify your Content API connection is working Retrieve personalized content for users Stream events to Aampe Update user properties # Audit Log Source: https://kb.aampe.com/api-guide/composer-api/audit-log Read the history of configuration changes in your Aampe account: who made each change, when, and what changed. The audit log records configuration changes in your Aampe account (who made each one, when, and for most kinds of item the values before and after), and you can read it with three endpoints on the public API. ## Authentication Send your Aampe API key in the `X-API-KEY` header on every request (see [Authentication](/api-guide/composer-api/authentication)). All endpoints are read-only `GET` requests under `https://composer.api.aampe.com/api`, and the key's user needs read access to the account. ```bash theme={null} curl https://composer.api.aampe.com/api/activity-logs/account \ -H "X-API-KEY: " ``` Each request only ever returns your own account's history. Times are UTC, in ISO 8601 format. *** ## What's Recorded Each change is one entry: who made it, when, what changed and, for most kinds of item, the values before and after. Entries are never edited or removed after they're written. | What changed | `object_type` | Filed under (`target_type`) | | - | - | - | | Labels and label weights | `Label` | `Label` itself, for a label's creation | | A label's component type | `LabelVariantType` | `Label` | | Audiences and their filters | `Audience` | — | | Triggers and their rules | `CompoundTrigger` | — | | Frequency caps | `FrequencyLimit` | `CompoundTrigger`, when the cap belongs to a trigger | | Default frequency caps, for the account or for one trigger | `DefaultFrequencyLimits` | `CompoundTrigger`, when the default belongs to a trigger | | Reward functions, their target events and weight overrides | `RewardFunction` | — | | A message created, or its settings or status changed | `Formula` | `Formula` itself | | A label added to or removed from a message | `FormulaLabel` | `Formula` | | A message's audience set | `FormulaAudience` | `Formula` | | An alternate added to or removed from a message | `Variant` | `Formula` | Some changes are recorded without values for now: their `data` is `null`, so an entry says who changed which item and when, but not what changed. This covers every `Formula`, `FormulaLabel`, `FormulaAudience`, `Variant` and `LabelVariantType` entry, and a label's creation. Tag changes and edits to an alternate's text aren't recorded yet. In every entry: | Field | Meaning | | - | - | | `id` | The entry's id | | `verb` | `CREATE`, `UPDATE`, `DELETE` or `ARCHIVE` | | `object_type`, `object_id`, `object_name` | The item that changed, and its current name if it has one | | `target_type`, `target_id`, `target_name` | The item it's filed under, if any | | `user_id`, `user_name` | Who made the change | | `created_at` | When (UTC) | | `data` | `before`, `after` and `changed_fields`: the item's values before and after, and which fields differ | | `transaction_id` | The save the change was part of. Changes made in one save share it. | | `save_count`, `first_activity_id`, `first_created_at` | See [Combined saves](#combined-saves) | `before` is `null` for a creation. Use `verb` to tell a deletion or archive apart: most items are kept when deleted or archived, so `after` holds the item with `deleted_at` or `archived_at` set, and `after` is `null` only when the item is removed outright. Ids of events and of audience-filter attributes and operators come with their names alongside, for example `event_id` with `event_name` and `event_code`. Snapshots in the examples on this page are excerpts. Older entries, from before the audit log launched, can have a simpler `data` shape without `before` and `after`. *** ## Account Log ``` GET /activity-logs/account ``` Returns every change in your account, newest first, one page at a time. All parameters are optional and combine with AND. | Parameter | Type | Meaning | | - | - | - | | `start` | ISO 8601 datetime | Only changes at or after this time. Without a timezone, UTC is assumed. | | `end` | ISO 8601 datetime | Only changes before this time. Must not be earlier than `start`. | | `scope` | type, e.g. `Formula` | One kind of item and everything filed under it. `scope=Formula` covers messages with the labels, audiences and alternates filed under them; `scope=CompoundTrigger` covers triggers with their frequency caps. | | `object_type` | type | Only this kind of item, matched exactly. | | `user_id` | integer | Only changes by this person. [Who made changes](#who-made-changes) lists the ids. | | `target_model` + `target_id` | type + id | One item's history: changes to it and to everything filed under it. Give both or neither. | | `surface_id` | UUID | Every message on one surface. | | `ungrouped` | boolean, default `false` | One entry per recorded change instead of [combined saves](#combined-saves). | | `page` | integer, default `1` | Page number, from 1. | | `per_page` | integer, default `10` | Entries per page. `0` returns only the count. | For example, the changes Ana made in September: ```bash theme={null} curl "https://composer.api.aampe.com/api/activity-logs/account?user_id=42&start=2026-09-01T00:00:00Z&end=2026-10-01T00:00:00Z&per_page=50" \ -H "X-API-KEY: " ``` The response wraps the entries with paging details: ```json theme={null} { "items": [ { "id": 9182, "verb": "UPDATE", "object_type": "FrequencyLimit", "object_id": "6f1c2a0e-8d3b-4c55-9a51-2b7e0c4d9f10", "object_name": null, "target_type": null, "target_id": null, "target_name": null, "user_id": 42, "user_name": "Ana Lopez", "created_at": "2026-09-14T10:02:11", "transaction_id": 55410, "save_count": 1, "first_activity_id": 9182, "first_created_at": "2026-09-14T10:02:11", "data": { "before": {"max_messages": 3, "time_frame": "daily_limit", "aggregation_type": "channel", "aggregation_value": "email"}, "after": {"max_messages": 5, "time_frame": "daily_limit", "aggregation_type": "channel", "aggregation_value": "email"}, "changed_fields": ["max_messages"] } } ], "total_count": 128, "page": 1, "per_page": 50, "total_pages": 3, "has_prev": false, "has_next": true, "prev_page": null, "next_page": 2 } ``` *** ## Combined Saves Settings are often saved several times in quick succession, so the log combines a person's back-to-back saves of the same item into one entry. Saves are combined when they are by the same person, on the same item, and each within 10 minutes of the one before. Another person's save in between starts a new entry. A combined entry shows the net change: * `data.before` is the item before the first save, `data.after` is the item after the last. * `changed_fields` lists only fields that end up different. A field edited and then put back isn't listed, so an entry can show `[]`: no net change. * `save_count` is how many saves it combines; `first_activity_id` and `first_created_at` are the first save's. `id` and `created_at` are the last save's. * A single save has `save_count` 1, and its `first_*` fields equal its own. Only changes to existing items combine. A creation, deletion or archive is always its own entry, and message changes aren't combined yet. Combining never crosses the edges of a `start`/`end` range, and never changes what's stored: add `ungrouped=true` to see every save on its own. *** ## One Item's History ``` GET /activity-logs ``` Returns every change to one item and to everything filed under it, oldest first, as a plain list with no paging. It uses the same entries and [combined saves](#combined-saves) as the account log. | Parameter | Required | Meaning | | - | - | - | | `target_model` | Yes | The item's type, e.g. `Formula`. An unknown type returns an empty list. | | `target_id` | Yes | The item's id | | `ungrouped` | No | `true` for one entry per save | ```bash theme={null} curl "https://composer.api.aampe.com/api/activity-logs?target_model=Formula&target_id=1204" \ -H "X-API-KEY: " ``` For large histories, prefer the account log with `target_model` and `target_id`, which pages and can be filtered by date and person. *** ## Who Made Changes ``` GET /activity-logs/account/users ``` Lists everyone who has changed your account, for filtering the log by `user_id`. It includes people who have since left or been deactivated, and Aampe staff who changed your configuration on your behalf. | Parameter | Meaning | | - | - | | `start`, `end` | Only people with changes in this range, as on the account log | | `scope` | Only people who changed this kind of item, as on the account log | ```json theme={null} [ {"user_id": 42, "user_name": "Ana Lopez", "email": "ana@example.com"}, {"user_id": 57, "user_name": "Sam Chen", "email": null} ] ``` `user_name` is the name on the person's latest change, or their current name if that change has none. `email` is `null` when their user account no longer exists. The list is sorted by name. *** ## Errors | Status | When | | - | - | | `401` | The API key is missing or invalid | | `403` | The key's user can't read this account | | `422` | A parameter is invalid: an unknown type, `start` after `end`, or only one of `target_model`/`target_id`. On [one item's history](#one-items-history), an unknown `target_model` returns an empty list instead. | Errors return a JSON body with a `detail` message. # Authentication Source: https://kb.aampe.com/api-guide/composer-api/authentication How to obtain and use an API key to authenticate with the Aampe Composer API. ## Overview The Composer API uses API key authentication. Every request must include your API key in the `X-API-KEY` header. ```python theme={null} import requests response = requests.get( "https://composer.api.aampe.com/api/formulas", headers={"X-API-KEY": "YOUR_API_KEY"}, ) ``` *** ## Getting Your API Key 1. In the Aampe dashboard, navigate to **System setup → Integrations → API Keys** 2. Click **+ Create new** 3. Copy the generated key immediately — it will not be shown again Store your API key somewhere safe as soon as you create it. The dashboard will not show the full key again after you leave the page. *** ## Using the Key Pass the key in the `X-API-KEY` request header on every call: ```python theme={null} headers = { "X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json", # required for POST/PUT requests with a body } ``` Requests without a valid key, or with a key that has been revoked, receive a `401 Unauthorized` response. *** ## Best Practices * Store keys in environment variables or a secrets manager — never hard-code them in source files * Use a separate key for each environment (production, staging, local development) * Rotate keys immediately if you suspect compromise * Contact [support@aampe.com](mailto:support@aampe.com) to revoke or regenerate a key # Create a Formula Source: https://kb.aampe.com/api-guide/composer-api/formulas/create-formula Complete guide to creating a formula with labeled alternates using the Composer API. ## 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: ```python theme={null} 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. ```python theme={null} 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: ```python theme={null} 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. ```python theme={null} # 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. ```python theme={null} 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:** ```python theme={null} {"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 | *** ## Formula Lifecycle ``` Draft → Review → Approved → Live / Scheduled / Expired ↓ Archived ``` Use the state-transition endpoints (`/review`, `/approve`, `/archive`) to progress a formula. # Concepts and System Objects Source: https://kb.aampe.com/api-guide/composer-api/formulas/overview What formulas are and how they map to the messages you see in Composer. ## What Is a Formula? A **formula** is the API's internal representation of a message in Composer. Every message you see listed in the Composer UI corresponds to exactly one formula under the hood. When you open a message in Composer, browse its alternates, or trigger a send — you are interacting with a formula and the variants attached to it. *** ## What a Formula Contains A formula captures everything needed to describe a message: | Property | Description | | - | - | | `name` | The display name shown in the Composer UI | | `message_type` | The channel — push, SMS, email, in-app, etc. | | `status` | The current lifecycle state — Draft, Review, Approved, Live, etc. | | `labels` | Tags that describe the formula's overall intent and which component types they apply to | | `topic_id` | The topic this message belongs to | | `audience_id` | An optional segment filter controlling who can receive it | | `send_start` | Optional delivery window start | The message structure and all actual copy live in the **message-content** record, which is a separate object automatically created alongside the formula. *** ## Message-Content Every formula has exactly one **message-content** record. This is where the structure (the slot layout) and all variants (the actual copy) live — not on the formula itself. The formula is the container: name, channel, status, targeting. The message-content is the payload: what the message looks like and what it says. When you create a formula, the API automatically creates a message-content record alongside it. You then set the structure and variants on that record directly. ``` Formula → created via POST /formulas └── message-content → auto-created, fetched via GET /formulas/{id}/message-contents ├── structure → set via PUT /message-contents/{id} └── variants → created via POST /variants or POST /variants/create/bulk ``` You'll need the message-content ID for most content operations: | Endpoint | What it does | | - | - | | `GET /formulas/{id}/message-contents` | Returns the message-content ID | | `PUT /message-contents/{id}` | Sets or replaces the structure and seed variants | | `GET /message-contents/{id}` | Reads the current structure | | `GET /message-contents/{id}/variants` | Lists all variants | *** ## Structure, Variants, Components, and Alternates These four concepts work together to describe a message and all the ways it can be personalized. ### Structure and Slots The message-content's `structure` defines the skeleton of the message — what slots exist (header, body, etc.) and what type of content each slot holds. It does not contain the message text itself. Each slot is one variant node in the structure. The node's `id` is the **component instance ID** — a UUID you generate that identifies the slot and ties all its alternates together. ### Variants A **variant** is a single piece of message copy for a slot. It has a label, a text value, and a `component_id` pointing to the slot it belongs to. Every slot has at least one variant — the **seed variant**, which is set inline in the structure when you first define it. ### Components A **component** is a slot, identified by its component instance ID. In the Composer UI, a component appears as a single content block within a message that shows how many alternates it has. All variants sharing the same `component_id` belong to the same component. ### Alternates **Alternates** are the multiple variants grouped under a single component. If a header slot has three variants (Calm, Breathe, Stillness), those three are the alternates for that component. Aampe picks one alternate per component per send. ``` Formula └── message-content ├── structure │ ├── slot: header (component instance id = X) ← seed text auto-creates first alternate │ └── slot: body (component instance id = Y) └── variants ├── component_id = X, label = Calm ← alternate 1 for header (seed) ├── component_id = X, label = Breathe ← alternate 2 for header ├── component_id = X, label = Stillness ← alternate 3 for header ├── component_id = Y, label = Science ← alternate 1 for body (seed) └── component_id = Y, label = Sleep ← alternate 2 for body ``` This formula produces `3 × 2 = 6` unique message combinations. *** ## The Two Component IDs There are two distinct IDs named "component ID" in the system. Confusing them is the most common source of errors: | Name | Type | Source | Used in | | - | - | - | - | | **Component type ID** | UUID | `GET /component-types` → `id` | `componentId` in structure nodes; identifies what *kind* of content a slot holds | | **Component instance ID** | UUID | You generate with `uuid.uuid4()` | Variant node `id` in the structure; `component_id` when creating variants | The component type ID says *what kind* of slot this is (e.g. Offering, CallToAction). The component instance ID says *which specific slot* in this message a variant belongs to. *** ## Formula Lifecycle ``` Draft → Review → Approved → Live / Scheduled / Expired ↓ Archived ``` Only formulas in **Approved** or **Live** status are eligible for delivery. Formulas stay in **Draft** until explicitly submitted for review. # Update a Formula Source: https://kb.aampe.com/api-guide/composer-api/formulas/update-formula Learn how to update the settings and content of an existing formula. ## Overview You can update a formula's name, targeting settings, schedule, and more using the update endpoint. To replace the message structure or add new alternates, use the message-content endpoints. *** ## Updating Formula Metadata ``` POST /formulas/{formula_id}/update ``` All fields are optional — only include the fields you want to change. | Field | Type | Description | | - | - | - | | `name` | string | The display name of the formula | | `deeplink` | string | Deep link URL for the message CTA | | `send_start` | datetime | ISO 8601 datetime when delivery becomes eligible | | `send_stop` | datetime | ISO 8601 datetime when delivery stops | | `send_start_date` | date | Date-only version of send start | | `send_stop_date` | date | Date-only version of send stop | | `is_controlled_message` | boolean | Whether this is a controlled A/B test message | | `timezone` | string | IANA timezone string (e.g. `"America/New_York"`) | | `audience_id` | uuid | Target audience segment | | `topic_id` | uuid | Topic association | | `tags` | array of uuids | Replace all tag associations | | `labels` | array | Replace formula-level label associations | | `compound_trigger_id` | uuid | Compound trigger rule for delivery | | `dataset_id` | integer | Dataset used for user-level personalization | | `override_provider_frequency_limits` | boolean | Bypass provider-level send frequency caps | | `additional_metadata` | object | Free-form metadata | ### Example: Rename a formula and set a schedule ```python theme={null} import requests BASE_URL = "https://composer.api.aampe.com/api" headers = {"X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json"} FORMULA_ID = 215 response = requests.post( f"{BASE_URL}/formulas/{FORMULA_ID}/update", headers=headers, json={ "name": "Welcome Back Campaign — Spring", "send_start": "2026-04-10T09:00:00Z", "send_stop": "2026-04-30T23:59:59Z", "timezone": "America/New_York", }, ) response.raise_for_status() print(f"Updated: {response.json()['name']}") ``` *** ## Replacing the Message Structure To replace what a formula's message looks like — its slots and seed variants — update the message-content directly: ``` PUT /message-contents/{message_content_id} ``` This is a **full replacement**. Include every section and slot you want to keep — anything omitted will be removed. ### Step 1 — Get the message-content ID ```python theme={null} 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 2 — Replace the structure Pre-generate a component instance ID for each slot. The text in `children[0].text` becomes the seed variant (first alternate) for that slot. ```python theme={null} import uuid CT_OFFERING = "8e3f0698-7564-4522-9c07-49686667c0b4" CT_CTA = "8c0b9b73-b305-4f93-b41f-bde62cebac98" header_component_id = str(uuid.uuid4()) body_component_id = str(uuid.uuid4()) response = 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, "kind": "variant", "variantType": "Offering", "componentId": CT_OFFERING, "children": [{"text": "Spring is here, {{first_name}}!"}], } ], }, { "kind": "section", "sectionType": "body", "children": [ { "id": body_component_id, "kind": "variant", "variantType": "CallToAction", "componentId": CT_CTA, "children": [{"text": "Check out what's new."}], } ], }, ], "deeplink": None, "deeplink_dataset_field_id": None, }, ) response.raise_for_status() ``` After replacing the structure, add more alternates via `POST /variants/create/bulk` using the component instance IDs. *** ## Adding Alternates to an Existing Formula To add alternates to a formula without touching the structure, first fetch the existing component instance IDs from the message-content, then bulk-create: ```python theme={null} # Get the existing structure to find component instance IDs mc_detail = requests.get( f"{BASE_URL}/message-contents/{message_content_id}", headers={"X-API-KEY": "YOUR_API_KEY"}, ) mc_detail.raise_for_status() structure = mc_detail.json()["structure"] # Build a map: variantType → component instance ID slot_ids = {} for section in structure: for child in section.get("children", []): if child.get("kind") == "variant": slot_ids[child["variantType"]] = child["id"] # Add new alternates requests.post( f"{BASE_URL}/variants/create/bulk", headers={"X-API-KEY": "YOUR_API_KEY", "Content-Type": "application/json"}, json=[ {"component_id": slot_ids["Offering"], "content": "New spring deals await.", "label_id": 104}, {"component_id": slot_ids["Offering"], "content": "Your seasonal picks are here.", "label_id": 105}, ], ).raise_for_status() ``` *** ## Finding a Formula ID Formulas are listed by lifecycle category and message type: ```python theme={null} import requests BASE_URL = "https://composer.api.aampe.com/api" headers = {"X-API-KEY": "YOUR_API_KEY"} for category in ("Draft", "Live"): resp = requests.get( f"{BASE_URL}/formulas/category/{category}", headers=headers, params={"message_type": "push"}, ) resp.raise_for_status() for f in resp.json(): print(f"[{category}] ID: {f['id']} Name: {f['name']}") ``` # Manage Labels Source: https://kb.aampe.com/api-guide/composer-api/labels/manage-labels How to list, create, and use labels with the Composer API. ## List Existing Labels Before creating new labels, fetch the ones that already exist. Labels rarely change — you can cache this list for the duration of a session. ```python theme={null} import requests BASE_URL = "https://composer.api.aampe.com/api" headers = {"X-API-KEY": "YOUR_API_KEY"} response = requests.get(f"{BASE_URL}/labels", headers=headers) response.raise_for_status() labels = response.json() for label in labels: if label["archived_at"] is None: # skip archived labels print(f"[{label['variant_type']}] {label['id']}: {label['label_name']}") ``` **Response:** ```json theme={null} [ { "id": 101, "label_name": "Curiosity", "label_description": "Piques interest and invites exploration", "variant_type": "Offering", "archived_at": null }, { "id": 102, "label_name": "Urgency", "label_description": "Creates time pressure", "variant_type": "Offering", "archived_at": null } ] ``` To look up labels by component type: ```python theme={null} from collections import defaultdict by_type = defaultdict(list) for label in labels: if label["archived_at"] is None: by_type[label["variant_type"]].append(label) # e.g. by_type["Offering"] → list of all Offering labels ``` *** ## Create a Label ``` POST /labels ``` ```python theme={null} response = requests.post( f"{BASE_URL}/labels", headers={**headers, "Content-Type": "application/json"}, json={ "label_name": "Calm", "label_description": "Gentle, reassuring tone", "variant_type": "Offering", }, ) response.raise_for_status() label_id = response.json()["id"] print(f"Created label: {label_id}") ``` | Field | Type | Required | Description | | - | - | - | - | | `label_name` | string | Yes | Human-readable name | | `label_description` | string | No | What this label means | | `variant_type` | string | Yes | The component type `data_key` this label applies to — must match exactly (e.g. `"Offering"`, `"ValueProposition"`) | Create one label per semantic concept per component type. If you need a `"Calm"` label for both Offering and ValueProposition variants, create two separate labels — one with each `variant_type`. *** ## Using Label IDs Once you have label IDs, they appear in three places: ### 1. Formula creation — declare which labels the formula uses ```python theme={null} 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}, ], }) ``` ### 2. Structure definition — label the seed variant ```python theme={null} { "id": component_instance_id, "kind": "variant", "variantType": "Offering", "componentId": CT_OFFERING, "labelId": 101, # ← label for this seed variant "children": [{"text": "Your copy here"}], } ``` ### 3. Variant creation — label each alternate ```python theme={null} requests.post(f"{BASE_URL}/variants/create/bulk", headers=headers, json=[ {"component_id": component_instance_id, "content": "Your copy", "label_id": 101}, {"component_id": component_instance_id, "content": "More urgent copy", "label_id": 102}, ]) ``` # Labels Source: https://kb.aampe.com/api-guide/composer-api/labels/overview What labels are and how they classify variants in Composer. ## What Is a Label? A **label** is a semantic tag that classifies a variant — it captures the intent, tone, or strategy behind a piece of message copy. Examples: `Curiosity`, `Urgency`, `Calm`, `Social Proof`. Labels are how Aampe learns which type of message resonates with which user. Over time, Aampe associates each user with the labels they respond to and selects variants accordingly. *** ## Labels and Component Types Every label is scoped to a specific **component type** via its `variant_type` field. A label with `variant_type: "Offering"` can only be used on Offering variants — it cannot be applied to a `ValueProposition` or `CallToAction` variant. This means you need separate labels for each component type you use in a formula. If you try to attach a label to a variant whose component type doesn't match the label's `variant_type`, the API will return an error. *** ## How Labels Flow Through the System ``` Label (id, variant_type, label_name) │ ├─► Formula-level label association │ {"label_id": 101, "variant_type": "Offering", "component_type_id": "..."} │ Declares which labels this formula uses per component type │ └─► Variant-level label assignment {"component_id": "...", "content": "...", "label_id": 101} Tags each individual alternate with its semantic meaning ``` Labels appear in two places: * **On the formula** (`labels` array at creation time) — declares which labels are associated with each component type in this formula * **On each variant** (`label_id` at variant creation time) — tags the specific alternate with its meaning *** ## Label Fields | Field | Type | Description | | - | - | - | | `id` | integer | The label's unique ID — used as `label_id` everywhere | | `label_name` | string | Human-readable name (e.g. `"Curiosity"`) | | `label_description` | string | What this label means | | `variant_type` | string | The component type `data_key` this label applies to | | `archived_at` | datetime or null | If set, the label is archived and should not be used | See [Manage Labels](./manage-labels) for how to fetch and create labels. # Multilanguage Formulas Source: https://kb.aampe.com/api-guide/composer-api/languages/multilanguage How to create formulas with content in multiple languages. ## How Multilanguage Works Each formula can have one version of its content per language. These language versions are separate **message-content** records — each with its own structure and variants. ``` Formula ├── message-content (English — default) │ ├── structure │ └── variants: "Find your calm", "Breathe and begin", ... └── message-content (German) ├── structure └── variants: "Finde deine Ruhe", "Atme und beginne", ... ``` When Aampe sends a message, it selects the message-content that matches the user's language. Each language version is personalized independently — Aampe learns label preferences per language. *** ## Step 1 — List Available Languages ```python theme={null} import requests BASE_URL = "https://composer.api.aampe.com/api" headers = {"X-API-KEY": "YOUR_API_KEY"} response = requests.get(f"{BASE_URL}/languages", headers=headers) response.raise_for_status() for lang in response.json(): default = " (default)" if lang["is_default"] else "" print(f"{lang['id']} {lang['name']}{default}") ``` **Response:** ```json theme={null} [ { "id": "4abd0bdd-9d63-44de-b9d1-affcd3f61bfc", "name": "Default Language", "is_default": true, "status": "active" } ] ``` *** ## Step 2 — Create a Language (if it doesn't exist) ```python theme={null} response = requests.post( f"{BASE_URL}/languages", headers={**headers, "Content-Type": "application/json"}, json={ "name": "German", "description": "German language", }, ) response.raise_for_status() german_language_id = response.json()["id"] ``` | Field | Type | Required | Description | | - | - | - | - | | `name` | string | Yes | Display name for the language | | `description` | string | No | Optional description | *** ## Step 3 — Create the Formula Create the formula as normal. The API automatically creates a message-content for the **default language**. You will add the German message-content separately. ```python theme={null} import uuid CT_OFFERING = "8e3f0698-7564-4522-9c07-49686667c0b4" CT_VALUE_PROPOSITION = "72150aa9-6b20-4d95-9659-0c05701ef7de" headers_json = {**headers, "Content-Type": "application/json"} formula_resp = requests.post(f"{BASE_URL}/formulas", headers=headers_json, json={ "name": "My Multilanguage Formula", "message_type": "push", "structure": [], }) formula_resp.raise_for_status() formula_id = formula_resp.json()["id"] # Default language message-content is auto-created mc_resp = requests.get(f"{BASE_URL}/formulas/{formula_id}/message-contents", headers=headers) mc_resp.raise_for_status() default_mc_id = mc_resp.json()[0]["id"] ``` *** ## Step 4 — Set the Default Language Content Pre-generate component instance IDs. These identify the slots for the default language — **each language uses its own component instance IDs**. ```python theme={null} en_header_cid = str(uuid.uuid4()) en_body_cid = str(uuid.uuid4()) requests.put( f"{BASE_URL}/message-contents/{default_mc_id}", headers=headers_json, json={ "id": default_mc_id, "structure": [ { "kind": "section", "sectionType": "header", "children": [{ "id": en_header_cid, "kind": "variant", "variantType": "Offering", "componentId": CT_OFFERING, "labelId": 101, "children": [{"text": "Find your calm"}], }], }, { "kind": "section", "sectionType": "body", "children": [{ "id": en_body_cid, "kind": "variant", "variantType": "ValueProposition", "componentId": CT_VALUE_PROPOSITION, "labelId": 103, "children": [{"text": "5 minutes a day reduces stress hormones 30%."}], }], }, ], "deeplink": None, "deeplink_dataset_field_id": None, }, ).raise_for_status() # Add remaining English alternates requests.post(f"{BASE_URL}/variants/create/bulk", headers=headers_json, json=[ {"component_id": en_header_cid, "content": "Breathe and begin", "label_id": 102}, {"component_id": en_header_cid, "content": "A moment of stillness awaits", "label_id": 101}, {"component_id": en_body_cid, "content": "Fall asleep faster and wake up restored.", "label_id": 103}, ]).raise_for_status() ``` *** ## Step 5 — Add a New Language Version Create a new message-content for German by posting with `formula_id` and `language_id`: ```python theme={null} de_mc_resp = requests.post( f"{BASE_URL}/message-contents", headers=headers_json, json={ "formula_id": formula_id, "language_id": german_language_id, }, ) de_mc_resp.raise_for_status() german_mc_id = de_mc_resp.json()["id"] ``` Then set its structure and variants the same way as the default language — using new component instance IDs: ```python theme={null} de_header_cid = str(uuid.uuid4()) de_body_cid = str(uuid.uuid4()) requests.put( f"{BASE_URL}/message-contents/{german_mc_id}", headers=headers_json, json={ "id": german_mc_id, "structure": [ { "kind": "section", "sectionType": "header", "children": [{ "id": de_header_cid, "kind": "variant", "variantType": "Offering", "componentId": CT_OFFERING, "labelId": 101, "children": [{"text": "Finde deine Ruhe"}], }], }, { "kind": "section", "sectionType": "body", "children": [{ "id": de_body_cid, "kind": "variant", "variantType": "ValueProposition", "componentId": CT_VALUE_PROPOSITION, "labelId": 103, "children": [{"text": "5 Minuten täglich senken Stresshormone um 30%."}], }], }, ], "deeplink": None, "deeplink_dataset_field_id": None, }, ).raise_for_status() requests.post(f"{BASE_URL}/variants/create/bulk", headers=headers_json, json=[ {"component_id": de_header_cid, "content": "Atme und beginne", "label_id": 102}, {"component_id": de_header_cid, "content": "Ein Moment der Stille erwartet dich", "label_id": 101}, {"component_id": de_body_cid, "content": "Schlaf schneller ein und wache erholt auf.", "label_id": 103}, ]).raise_for_status() ``` *** ## Reading Back All Language Versions ```python theme={null} all_mc = requests.get( f"{BASE_URL}/formulas/{formula_id}/message-contents", headers=headers, ).json() for mc in all_mc: lang_id = mc["language_id"] mc_id = mc["id"] variants = requests.get(f"{BASE_URL}/message-contents/{mc_id}/variants", headers=headers).json() print(f"Language: {lang_id} — {len(variants)} variants") ``` *** ## Key Points * The default language's message-content is auto-created with the formula — do not create it manually * Each language version uses its own component instance IDs (the UUIDs you generate per slot) * Labels and component types are shared across languages — you do not need to recreate them * Each language version is personalized independently by Aampe # Aampe Events Ingestion Source: https://kb.aampe.com/api-reference/aampe-events-ingestion /api-guide/ingestion-openapi.json post /v1/events Handle an event coming from an Aampe Webhook. # Aampe Property Update Source: https://kb.aampe.com/api-reference/aampe-property-update /api-guide/ingestion-openapi.json post /v1/properties Handle property updates coming from Aampe identify calls. Expects data in the exact format needed for publishing. # Get surface content Source: https://kb.aampe.com/api-reference/surface/get-surface-content /api-guide/content-openapi.json get /v1/surface/content Returns one result per surface that could be resolved, in the same order as requested. Surfaces that cannot be resolved are omitted from the list. # Archiving Message Groups Source: https://kb.aampe.com/archiving-message-groups ## Overview Need a campaign to take a breather? Maybe you're running an experiment, setting up a holdout, or rethinking the copy. Aampe doesn't have a pause button, and it doesn't need one: **archiving a message works as a clean, reversible pause.** The message steps out of rotation, and your agents keep everything they've learned about it. When you're ready, un-archive it and they pick up right where they left off. ## How to pause a campaign 1. Find the message group(s) you want to pause under your Live messages 2. Click the orange Archive icon on the right of each Message Group to archive it Image 3. When asked for confirmation, click "Archive" again Image 4. Make sure your newly-archived message now shows up under "Archived" in the Message Group list That's it. Archived messages drop out of rotation and stop being served, so your agents won't pick them for anyone until you bring them back. > **Tip:** Jot down which messages you archived and why (your experiment plan is a good spot). Future you will thank you when it's time to resume. ## Why archiving makes a good pause * **It stops the sends.** The message leaves active rotation, so the campaign goes quiet. * **It's fully reversible.** Nothing is deleted. Un-archive whenever you're ready. * **Your agents don't forget.** Everything they've learned about the message stays intact while it's paused. ## How to resume 1. Find the archived messages in the message list. 2. Un-archive each one. Your campaign is back in rotation, and **there's no cold start**. Agents don't have to re-explore the message from scratch. They carry on from what they already know. # BigQuery data share Source: https://kb.aampe.com/developer-guide/aampe-data/data-share-bigquery Query your Aampe data share in Google BigQuery with a service account Aampe delivers your data share into Google BigQuery as read-only tables. Use a service account. That account is the identity Aampe grants, and the identity your jobs use to query. You pay for the queries you run. There is no storage charge for the shared tables. Tables refresh daily. Column definitions are on the [table descriptions](/developer-guide/aampe-data/data_share_tables) page. ## Send this to Aampe * Service account email, in the form `aampe-data-share@YOUR_PROJECT_ID.iam.gserviceaccount.com` * The BigQuery region you query from, such as `US` or `us-central1` Aampe grants that service account and sends you a Google Cloud link to the listing. ## 1. Prepare your project In the Google Cloud project where you want the tables: 1. Turn on [billing](https://console.cloud.google.com/billing). 2. Enable the [BigQuery API](https://console.cloud.google.com/apis/library/bigquery.googleapis.com). 3. Enable the [Analytics Hub API](https://console.cloud.google.com/apis/library/analyticshub.googleapis.com). ## 2. Create a service account 1. Open [Service accounts](https://console.cloud.google.com/iam-admin/serviceaccounts) for that project. 2. Click **Create service account**. 3. Name it `aampe-data-share`. 4. Copy the email address. ## 3. Grant roles on your project On **your** project, grant that service account: * **BigQuery User** (`roles/bigquery.user`), so it can create the linked dataset and run queries * **BigQuery Data Viewer** (`roles/bigquery.dataViewer`), so it can read the shared tables 1. Open [IAM](https://console.cloud.google.com/iam-admin/iam). 2. Click **Grant access**. 3. Paste the service account email. 4. Add both roles and save. ## 4. Subscribe once Wait until Aampe confirms the share is ready, then open the link Aampe sends. Subscribing creates a read-only linked dataset in your project. The link path contains four values: `projects/PUBLISHER_PROJECT/locations/LOCATION/dataExchanges/DATA_EXCHANGE_ID/listings/LISTING_ID` A service account cannot click **Subscribe** in the browser. Run the command below once as that service account. The person who runs it needs **Service Account Token Creator** (`roles/iam.serviceAccountTokenCreator`) on the service account. ```bash theme={null} SA=aampe-data-share@YOUR_PROJECT_ID.iam.gserviceaccount.com curl -sS -X POST \ -H "Authorization: Bearer $(gcloud auth print-access-token --impersonate-service-account="$SA")" \ -H "Content-Type: application/json" \ "https://analyticshub.googleapis.com/v1/projects/PUBLISHER_PROJECT/locations/LOCATION/dataExchanges/DATA_EXCHANGE_ID/listings/LISTING_ID:subscribe" \ -d '{ "destinationDataset": { "datasetReference": { "projectId": "YOUR_PROJECT_ID", "datasetId": "aampe" }, "location": "LOCATION" } }' ``` `PUBLISHER_PROJECT`, `LOCATION`, `DATA_EXCHANGE_ID`, and `LISTING_ID` come from the link. `YOUR_PROJECT_ID` is your project. `datasetId` is the name of the dataset in your project. Set `location` to the region you sent Aampe. ## 5. Query ```sql theme={null} SELECT * FROM `YOUR_PROJECT_ID.aampe.TABLE_NAME` LIMIT 10; ``` If this project sits inside a VPC Service Controls perimeter, add ingress and egress rules for BigQuery and Analytics Hub before you subscribe. Otherwise the subscribe call and later queries are blocked. # Aampe Data Share Source: https://kb.aampe.com/developer-guide/aampe-data/data-share-overview Make Aampe data part of your data science toolkit ## What is a Data Share? A data share is a mechanism for accessing external datasets directly within your data warehouse environment. Instead of receiving data through file transfers, APIs, or ETL pipelines, data shares appear as read-only tables in your warehouse that you can query alongside your own data. The tables remain stored in Aampe's infrastructure but are queryable from your environment. **Key Characteristics:** * **No ETL Latency** - Data refreshes in real-time as production systems update * **Easy Access** - Tables appear directly in your warehouse environment as read-only objects * **No Storage Costs** - You're responsible only for compute resources used when querying. There are no ingestion or storage costs. ## What Data Does Aampe Share? See our page [with detailed table and column descriptions](/developer-guide/aampe-data/data_share_tables). ## How Do I Activate Data Sharing? Contact your Aampe representative to turn on a data share. For Google BigQuery, create a service account in your project and send us the email. The steps are on the [BigQuery](/developer-guide/aampe-data/data-share-bigquery) page. Once the share is active, the tables appear in your project as read-only. You can join them with your own data using standard SQL. ## Supported Destinations * Amazon S3 * Azure Blob Storage * Google Cloud Storage * Amazon Redshift * [Google BigQuery](/developer-guide/aampe-data/data-share-bigquery) * Databricks * Snowflake ## Change Management Backwards compatible changes may happen at any time (e.g. new columns). \ Breaking changes (removing columns or tables, renaming columns) will be communicated in advance. ## Data Share FAQs No. Data sharing is currently part of our standard offering.  The tables update daily. For each user, we simulate hundreds of future potential messages given the current Bayesian priors for each label. We repeat this process for value propositions, offerings, tone, etc. and then report the percent of messages with each label.  # Table Descriptions Source: https://kb.aampe.com/developer-guide/aampe-data/data_share_tables See details on the Aampe datasets ## Overview Aampe is ready to share granular data about users, messages, and other events straight to your data warehouse. While there are many potential applications, customers generally find these datasets useful for * Reporting and monitoring * Causal inference and offline message evaluation * Inputs into various AI models (llm prompts, ML features, etc.) Expand the tables below to see column descriptions, example queries, and more. ## Active Tables Aampe generates a profile for each user that has interacted with an agent. \ \ The **propensity** for each label within a given label set reflects the agent's estimate for how often that label will outperform the other label options. (If I were to simulate 100 messages from Aampe for each user, how often would the agent choose each label?) | Column | Type | Description | | :- | :-: | :- | | `contact_id` | STR | Unique person identifier | | `propensities_offering` | ARRAY | User propensities for each **offering** | | `propensities_valueproposition` | ARRAY | User propensities for each **value proposition** | | `propensities_tone` | ARRAY | User propensities for each **tone** label | | `propensities_timing` | ARRAY | User propensities for each **timing** window | | `propensities_channel` | ARRAY | User propensities for each **channel** | The propensity arrays contain scores that sum to one and look like the following: ```text theme={null} [ {"label_id":5678, "propensity":0.5432, "label_name":"Food"}, {"label_id":6789, "propensity":0.4568, "label_name":"Travel"} ] ``` ### User Profiles - Example Queries ```rust BigQuery theme={null} // for a given user, what's their propensity for each offering label SELECT contact_id, label_name, propensity FROM aampe_contact_profiles, UNNEST(propensities_offering) WHERE contact_id = '1a2b3c4d' ORDER BY propensity DESC ``` Agents save the details of each decision they make, including the parameters of the statistical models, sampling results, and the final decision result.\ \ With a row for every message event, this table helps with day-to-day reporting and monitoring. It also plays a key role in offline analysis and causal inference. | Column | Type | Description | | :- | :-: | :- | | `aampe_message_id (pk)` | STR | Aampe's unique identifier for a message event | | `provider_message_id` | STR | The event identifier from the message provider (e.g. Braze). Not available for all message providers. | | `contact_id` | STR | Unique person identifier | | `message_timestamp` | TIMESTAMP | When the message was sent | | `received_timestamp` | TIMESTAMP | When the message provider confirmed a successful delivery. | | `message_timezone` | STR | Timezone for `message_timestamp` | | `message_formula_id` | INT | Numerical Identifier for a message formula (called "messages" in the Aampe composer.) | | `message_formula_name` | STR | Name for a message formula (called "messages" in the Aampe composer) | | `message_content` | STRUCT | `title`: title of message
`body`: main message content | | `message_diversity_score` | FLOAT | Computed by Aampe. When several message alternates are available within a chosen set of labels, we send the alternate with the best message diversity score. This ensures messages vary their language even if the offering or value proposition is the same. | | `reward_baseline` | FLOAT | Computed by Aampe. This is the reward Aampe expects to observe if the agent does nothing. | | `channel_assignment_id` | STR | Unique decision ID for channel selection | | `channel` | STR | Channel | | `timing_assignment_id` | STR | Decision ID for timing selection (potentially a joint decision with channel) | | `timing_label_assigned` | STR | `label_set`: the decision being made (typically "day\_time" for timing decisions)
`label_id`: numeric ID for the timing label
`label_name`: human-friendly name for the timing label
`score`: Model score for timing-decision model
`assignment_type`: description of the model output (is it exploratory or not) | | `trigger_id` | STR | Identifier for trigger associated with message. There is always a trigger id, even if the message was not triggered by an event. | | `messaging_type` | STR | Either "controlled" (scheduled message blasts). Or "automated" which means an agent decided the message to send and when to send it. | | `copy_assignment_id` | STR | Decision ID for message copy elements | | `tags` | ARRAY | Array of tags associated with the message event | | `message_to` | STR | Delivery to address | | `message_from` | STR | Delivery from address | | `message_date` | DATE | Date of `message_timestamp` - for easier partitioning when needed | | `click_action` | STR | Type: what happens when the message is clicked (e.g. deeplink)
Value: deeplink URL or other api call resulting from click | | `image_url` | STR | Image URL (when not included in `message_content`) | | `reward_delta` | FLOAT | The extent to which the message event outperformed expectations (given by `reward_baseline`). | | `trigger_name` | STR | Name of trigger (or else "no trigger") | | `copy_assignment` | STR | `action_set_family`: the agents decision set (typically "copy" for copy decisions)
`action_set`: Label groups such as value proposition, offering, ...
`action_name`: the specific label
`probability`: mean-concentration parameter of the Beta distribution for the chosen action
`aggregated_signal`: another mean-concentration parameter of the Beta distribution for the chosen action | ### Aampe Messages - Example Queries ```rust BigQuery theme={null} // for each message, what were the labels, label parameters, etc.? SELECT aampe_message_id, message_date, action_set, action_name as label_name, probability, aggregated_signal FROM aampe_message_events, UNNEST(copy_assignment) WHERE aampe_message_id = '1234-asdf' ```
Not all messages are delivered successfully. Some messages fail between Aampe and the message provider. Other messages fail between the message provider and the customer. The `aampe_message_attempts` table provides message-level details on which messages were delivered successfully. | Column | Type | Description | | :- | :-: | :- | | `aampe_message_id (pk)` | STR | Aampe's unique identifier for a message event | | `provider_message_id` | STR | The event identifier from the message provider (e.g. Braze). Not available for all message providers. | | `contact_id` | STR | Unique person identifier | | `message_timestamp` | TIMESTAMP | When the message was sent | | `message_date` | DATE | Date of `message_timestamp` - for easier partitioning when needed | | `message_timezone` | STR | Timezone for `message_timestamp` | | `channel` | STR | Channel | | `message_formula_id` | INT | Numerical Identifier for a message formula (called "messages" in the Aampe composer.) | | `message_formula_name` | STR | Name for a message formula (called "messages" in the Aampe composer) | | `trigger_id` | STR | Identifier for trigger associated with message. There is always a trigger id, even if the message was not triggered by an event. | | `copy_assignment_id` | STR | Decision ID for message copy elements | | `message_content` | STRUCT | `title`: title of message
`body`: main message content | | `tags` | ARRAY | Array of tags associated with the message event | | `delivery_status` | STR | Whether or not the message was successfully delivered |
This table contains events delivered to Aampe from the message provider. Depending on your message provider, channels, and Aampe settings Aampe, these events may include: * `aampe_clicked` * `aampe_email_clicked` * `aampe_email_opened` * `aampe_email_unsubscribed` * `aampe_email_bounced` This is a subset of all Aampe events visible in the composer, hence the `_partial` suffix.  | Column | Type | Description | | :- | :-: | :- | | `event_instance_id (pk)` | STR | Aampe's unique identifier for an event occurance event | | `event_name` | STR | The event name (as seen in the Aampe composer). | | `contact_id` | STR | Unique person identifier | | `event_timestamp` | TIMESTAMP | When the message was sent | | `event_date` | DATE | Date of `event_timestamp` | | `metadata` | STRUCT | Numerical Identifier for a message formula (called "messages" in the Aampe composer.) | # Recommender System Source: https://kb.aampe.com/developer-guide/cms-integration/recommender-systems A combination of user behavior data with catalog information to deliver personalized product recommendations. ## Introduction Aampe's recommender system **combines user behavior data with catalog information** to deliver personalized product recommendations in push notifications and surfaces. The system also allows a cool-down period to be set, so if a recommendation is made today, N days can elapse before the recommendation can be used again in a communication. The customer can control how long or short this cool-down period is. The process starts with catalog data ingestion through CSV files, data aggregators, or cloud buckets, capturing essential product details like IDs, names, prices, and deep-links. This data can then be leveraged using CMS filters to write messages specifically for sub-categories in your catalog. **Aampe offers different recommenders for all your use-case ideas, e.g., abandoned cart, browse abandonment, new products, and more**. The recommendation engine tracks user consumption patterns and creates an item-to-item transition matrix based on factors like sequence frequency and timing. It generates personalized recommendations by matching user history against this matrix, selecting high-scoring items while avoiding previous recommendations. Implementation is straightforward - recommendations are inserted into messages through a simple interface during content creation. The system continuously learns from user interactions, improving recommendation accuracy over time while effectively handling both popular and niche products across large catalogs. ## Integration Steps Our dedicated developers will be with you every step of the way to ensure that the integration is smooth and easy. Here’s a step-by-step overview of the process: ### Prepare your CMS data: Ensure your product catalog includes essential fields such as: * Item ID/SKU * Item name * Item price (Optional for in-depth analytics) * Other relevant catalog properties you wish to include in your messaging (e.g., brand, category, description) Depending on the recommender you want to use, we require more information. Example: For abandoned cart messages, we would need details of items in both the add\_to\_cart event and the purchase event to process them using CMS. Our developers will go through the use case with you to define what information is needed per use case. ### Choose your integration method: * Cloud bucket integration ### Verify the integration: Work with our support team to ensure the data is being correctly received and processed. ### Additional Information **Content Types:** Aampe's recommender works with various content types, including products, blog posts, articles, lessons, videos, and more. **Support**: Integration support is available for any questions or assistance. # Data Security and Privacy Source: https://kb.aampe.com/developer-guide/data-ingest/data-ingest-help/data-security-and-privacy ## Data Security FAQs Each customer has a dedicated pipeline that pulls data into Aampe and applies the necessary transformations to fit our data model. Aampe uses a multi-tenant architecture to ensure customer datasets remain separate. We are able to co-locate BigQuery instances into any [Google Cloud location](https://cloud.google.com/about/locations) to satisfy any needs on data storage location. No. Aampe follows a strict multi-tenant data and model architecture. No data is ever shared between customers.  Aampe doesn’t operate around a central model that undergoes training or fine-tuning cycles. There’s no global predictive function, shared parameter space, or batch-training pipeline. \ \ Because of that, there is nothing in the system that resembles a model checkpoint, a retraining schedule, or a fine-tuning workflow. Each agent learns continuously from its own interactions with a single user. Learning is online, individual, and incremental, driven entirely by the feedback generated in that user’s own history. Many customers use Aampe without sending any PII. Since Aampe is not the final sender of any message, we do not need user names or emails. The Aampe message uses placeholders that are populated by the final-mile sender. At times certain user attributes (e.g. state of residence) are helpful to create audiences. Many customers choose to send limited PII on an as-needed basis. Data is retained by Aampe throughout the contract period. This enables embedded charts and analytics, auditing, and offline policy evaluation.\ \ For more information, please visit our [trust center](http://trust.aampe.com/). All data is encrypted at rest and in transit. Access to data is restricted to authorized personnel only. Aampe conducts regular security audits. Security certificates and reports are available upon request. Yes. Our AI security program is aligned with the OWASP AI Security & Privacy Top 10 for vulnerability identification and mitigation, and we incorporate the NIST AI Risk Management Framework (AI RMF) into our governance and risk-assessment processes. Aampe agents don’t share a global model, and there’s no promptable inference surface that a user can manipulate. Each agent learns only from observed behavioral signals through its own causal-learning loop. This architecture reduces Aampe's exposure to these kinds of attacks. Aampe maintains continuous monitoring across all production systems to detect anomalous or high-risk behavior, including unexpected API calls, traffic spikes, or abnormal system activity. * API Behavior Analytics: We track request patterns, rate anomalies, unauthorized endpoints, and abnormal payloads. Threshold- and behavior-based alerts trigger on deviation from established baselines. * Traffic & Volume Monitoring: Real-time metrics detect sudden spikes, unusual burst patterns, or other deviations in system load. * Centralized Logging & Telemetry: Aampe uses GCP Cloud Logging for centralized log collection and an OpenTelemetry-compatible platform for telemetry and alerting. Logs are aggregated, immutable, access-controlled, and continuously monitored in alignment with our SOC 2–compliant security program. * System Health & Drift Checks: Automated checks validate system integrity, operational stability, and drift from expected behavior. Aampe employs the following procedures in response to high-severity anomalies: * Automated Safeguards: High-severity anomalies trigger automatic containment (rate limiting, endpoint blocking, or disabling an action/service). * On-Call Engineering Escalation: Alerts route to the 24/7 on-call SRE/security team, who investigate and mitigate under defined SLAs. * Security Incident Response: Events meeting incident criteria are escalated to our Security Incident Response Team following SOC 2–aligned incident response procedures. * Customer Notification: If an incident affects customer data, notifications follow contractual and regulatory obligations. # How Often Should We Send Data to Aampe? Source: https://kb.aampe.com/developer-guide/data-ingest/data-ingest-help/how-often-data-sync Is once a day enough? Do things need to update in real time? ## The Choice is Yours You have full control over how often Aampe syncs data from your integrated data sources. The right frequency depends on your specific use case and how you want to balance responsiveness with potential cost and complexity. ### **Lower Frequency Syncs** Syncing once or twice a day is low complexity and still provides Aampe agents with the data they need to learn. Audiences based on user events or attributes may not reflect real-time changes, but many messages can still be effective. This approach works well when: * Customer data changes slowly * You're running campaigns based on stable attributes (demographics, historical patterns) * Cost efficiency is a priority Aampe's learning still works effectively and adapts to data that is backfilled or otherwise delayed. That said, **Aampe recommends syncing no less than once a day.** ### **Higher Frequency Syncs** Syncing more frequently (e.g. hourly batch or real-time via API) creates more accurate audiences, enabling real-time or near-real-time personalization. This enables time-sensitive use cases like: * Abandoned carts or browse recovery * Location-based triggers * Flash sales or limited-time offers This comes at the potential cost of higher compute and may require additional engineering resources. ### **Syncing Fast and Slow** Many customers start with a daily sync and then sync more often as needed. Sometimes this comes through more frequent batch syncs. Other times this comes from sending specific events via API in addition to a daily sync. \ \ The sync frequency doesn't affect Aampe's ability to optimize and personalize. But fresh data improves the integrity of audiences and enables event-based messages. # What Events Should We Send To Aampe? Source: https://kb.aampe.com/developer-guide/data-ingest/data-ingest-help/what-events-should-i-send-to-aampe Timestamps! We love timestamps! # What Is a "Good" Event Feed? Agentic learning systems depend on high-quality behavioral data to evaluate the impact of their actions. This guide explains which data matters most and provides a framework for prioritizing event instrumentation. ## Categories of Data in Agentic Systems Agentic systems are designed to learn by observing the effects of their actions on users. To facilitate that learning, they rely on three fundamental categories of data: user attributes, content attributes, and timestamped events. Each of these serves a distinct role in the agent’s learning environment. * **User attributes** (location, language, subscription tier, behavioral rollups) are static and lack timestamp precision. They can't measure treatment impact directly but help filter eligibility and match similar users for learning acceleration. * **Content attributes** (genre, artist, brand, feature category) enable content personalization but describe treatments rather than outcomes. They show what was recommended, not how users responded. * **Timestamped user events** are most valuable for learning. These actions occur at specific moments (starting a stream, completing a purchase, reading an article), enabling causal inferences about treatment effects. ## Why Timestamped Events Matter Most Agentic learning requires isolating individual treatment effects. Agents must track when treatments are delivered, to whom, and what happened afterward. Timestamped events make this linkage robust. Without them, or with only coarse aggregates, agents lose the ability to learn precise cause-effect relationships. Not all events are equally valuable. The best signals represent volitional, meaningful, spontaneous engagement. Subscription renewals are business-critical but predictable and scheduled, making them poor reward events. Account linking actions are finite and non-repeatable, failing to provide ongoing feedback. Quantity matters alongside quality. Too narrow an event set starves agents of signal. Rich event variety captures subtle response patterns and prevents agents from misinterpreting missing data as non-response. Broad coverage ensures genuine influence patterns remain visible. ## Characteristics of Good Reward Events Good reward events are distinguished by a set of properties that make them reliable indicators of user intent and value alignment. These events provide the backbone for agentic learning because they allow the system to observe how users react to treatments in ways that are both timely and meaningful. Not every user action qualifies. Some are noisy, passive, or structurally unsuitable for learning. The most effective reward events share these qualities: * **Volitional**: The user initiates the action by choice, not passively or automatically. * **Spontaneous**: The event can occur at any time; it is not bound to a predefined schedule. * **Repeatable**: The user can perform the action multiple times without exhausting the opportunity. * **Value-Aligned**: The event reflects real usage of the product or service in a way that supports the business's long-term goals. These traits ensure that the system learns from actions that are not only detectable but also meaningful. For instance, a user streaming a song, purchasing a product, or completing a fitness goal are all behaviors that are voluntary, time-sensitive, and repeatable. By contrast, subscription renewals or account linkages may be strategically important, but they occur on predictable schedules or as one-time actions, making them poor choices for continuous feedback and learning. ## A Checklist for Event Prioritization This checklist ranks event types by usefulness to agentic learning. Provide as many as possible, starting from the top: * **Tier 1: Core Value Actions**\ Direct indicators of meaningful product use: content streamed/consumed (music, video, articles), products purchased, transactions completed (bill payments, fund transfers), key actions logged. * **Tier 2: Feature Engagement**\ Deliberate interaction with features: adding to playlists/wishlists/carts, toggling settings or features, modifying subscription plans. Strong signals of intentional use. * **Tier 3: Session and Message Engagement**\ User presence indicators: app opens, session starts/stops, notification clicks, message views. Establishes attribution windows and general engagement, though more ambiguous for value measurement. * **Tier 4: Utility Interactions**\ Intent signals weakly linked to consumption: site search, browsing, tag-based exploration. Often preliminary to meaningful behaviors but less discriminative alone. * **Tier 5: Passive Interaction Events**\ Generic page views or button clicks not tied to specific features/content. Useful for interface flow and debugging, but limited value for modeling satisfaction or preferences. * **Tier 6: Scheduled or One-Time Outcomes**\ Business milestones that are externally timed or terminal: subscription renewals, trial completions, account linkages. Poor causal indicators, unsuitable as reward signals. Note that events that make very poor reward signal may still be very important to the business. Agents generally cannot learn user preferences based on subscription auto-renewals, but those renewals should still be tracked over time, since better, more meaningful, and more frequent engagement should correlate with increased retention. ## Avoiding Unnecessary Data Volume For many organizations - particularly those operating in event-driven architectures such as streaming services - the volume of raw behavioral data can be immense. Ingesting and transmitting all raw playback events, particularly if sampled at regular time intervals, is both financially and technically infeasible. In these situations, it helps to identify compact and representative signals. For streaming apps, a single "stream start" event per session or item is typically sufficient. This approach preserves the essential behavioral insight without overwhelming infrastructure. The broader principle is that agentic learning depends on timing, not exhaustive detail. A sparse set of carefully selected, timestamped events offers more actionable learning signal than high-volume logs that are costly to transmit and difficult to operationalize. # Sending Data to Aampe Source: https://kb.aampe.com/developer-guide/data-ingest/data-integrations See the following guides for instructions on how to send data to Aampe ## Supported Data Sources Don't see your preferred data source? Send an email to [support@aampe.com](mailto:support@aampe.com) and we'll find a solution. * [Aampe Streaming API](/developer-guide/data-ingest/data-sources/streaming-aampe-api) * [Amazon RDS](/developer-guide/data-ingest/data-sources/database-aws-rds) * [Google Spanner](/developer-guide/data-ingest/data-sources/database-spanner) * [Amazon S3](/developer-guide/data-ingest/data-sources/cloud-storage-aws-s3) * [Google Cloud Storage](/developer-guide/data-ingest/data-sources/cloud-storage-gcs) * [SFTP](/developer-guide/data-ingest/data-sources/sftp) * [Amplitude - batch](/developer-guide/data-ingest/data-sources/amplitude-batch) * [Amplitude - cohort](/developer-guide/data-ingest/data-sources/amplitude-cohort) * [Amplitude - streaming](/developer-guide/data-ingest/data-sources/amplitude-streaming) * [Clevertap](/developer-guide/data-ingest/data-sources/clevertap) * [Firebase Cloud Messaging](/developer-guide/data-ingest/data-sources/firebase) * [Mixpanel](/developer-guide/data-ingest/data-sources/mixpanel) * [Segment](/developer-guide/data-ingest/data-sources/segment) * [Databricks](/developer-guide/data-ingest/data-sources/data-warehouse-databricks) * [Google BigQuery](/developer-guide/data-ingest/data-sources/data-warehouse-bigquery) * [Snowflake](/developer-guide/data-ingest/data-sources/data-warehouse-snowflake) * [Redshift](/developer-guide/data-ingest/data-sources/data-warehouse-redshift) ## Data Integration FAQs Aampe needs a high-fidelity event feed that includes key conversion events *and* day-to-day engagement events. [Read more about what a "good" event feed looks like.](/developer-guide/data-ingest/data-ingest-help/what-events-should-i-send-to-aampe) For most customers, a daily sync is a good starting point. More frequent syncs will improve the accuracy of audiences, but introduces some cost and complexity. For real-time use cases (e.g. abandoned cart messages), you will want to [send events to Aampe via API](developer-guide/data-ingest/data-sources/streaming-aampe-api.mdx). [Continue reading about what to consider when deciding how frequently to send data to Aampe.](/developer-guide/data-ingest/data-ingest-help/how-often-data-sync) Yes. Both event and user-attribute data should be formatted to be compatible with Aampe's system. Depending on where your data originates, this may not require any work on your part.\ \ [Continue reading about data formatting](/developer-guide/data-ingest/data-model).  Event data is central to how agents measure their success and learn what messages successfully influence user behavior. User attributes are primarily used to define which users are eligible for receiving a given message. Eligibility can also be based on events (we will send message ABC to users who have attribute X and performed Y action in the last week). [Read a more detailed explanation of why we built Aampe agents to learn from events instead of user attributes.](/user-guide/welcome-to-aampe/how-does-aampe-work/agent-help/events-vs-attributes) # Aampe Data Management Source: https://kb.aampe.com/developer-guide/data-ingest/data-management Aampe Data Management Practices ## Definitions **Enduser data**: This includes contact\_ids, behavior event data and attribute information on your end users. See our [data model documentation](/developer-guide/data-ingest/data-models) for more information. **System data**: This includes information such as server logs and error reports. **Aampe customer records**: Usernames, billing related information ## Data Storage Location Each customer has a **dedicated pipeline** that reads enduser data from their sources and transforms it into Aampe's data model. Any PII present is **removed or anonymized**, ensuring compliance and security. Once transformed, enduser data is stored in our cloud warehouse as a separate, **tenant-specific dataset**. We are able to co-locate BigQuery instances into any [Google Cloud location](https://cloud.google.com/about/locations) to satisfy any needs on data storage location. Every tenant has their own message queues. These queues handle messages across all channels. ## Agentic Learning Your agents learn **based only on your data**. While your agents may share information amongst themselves, they cannot share information with agents from other Aampe customers. Agentic learning based on users message interactions is stored into the same tenant-specific dataset. This data includes preferences over labels, timing, and channels for each user. Aampe data on users and content decisions can be exported directly to your data warehouse. See [here](/developer-guide/aampe-data/data-share-overview) for information on receiving data from Aampe. ## Data Retention Data is retained by Aampe throughout the contract period. ## Additional Information All data is encrypted at rest and in transit. Access to data is restricted to authorized personnel only. Regular security audits are conducted to ensure the security of data. # Aampe Data Model Source: https://kb.aampe.com/developer-guide/data-ingest/data-model To start with Aampe, you need to send Aampe two datasets ## Two Datasets Aampe needs two datasets to function. 1. A table of users along with important user properties. Any properties included will be available within the audience builder. 2. A table of user events. This table powers agentic learning and is also available within the audience builder. ## User Properties Following is the data model Aampe uses to store properties about users. | Column Name | Data Type | Description | | :- | :- | :- | | **contact\_id** | String | A unique user identifier | | **country** | String | Required | | **\** (e.g language) | ... | ... | | **\** (e.g subscription\_status) | | | | **push\_token** | String (Optional) | Token of the user that will be used to send out push-messages, if different from **contact\_id** | | **phone\_number** | String (Optional) | Number to be used to send out SMS and whatsapp messages, if different from **contact\_id** | | **email\_address** | String (Optional) | Email of the user that will be used to send out email messages, if different from **contact\_id** | See this page for industry-specific examples of [user attributes](/developer-guide/data-ingest/user-properties) ## Events | Column Name | Data type | Description | | - | - | - | | **contact\_id** | String | A unique user identifier | | **timestamp** | Timestamp | The timestamp of when the event happened in UTC | | **event\_name** | String | An event name corresponding to the event that was triggered | | **event\_instance\_id** | String (Optional) | An event identifier | | **timezone** | String (Optional) | Local timezone of the user/event - locale | | **metadata** | JSON (Optional) | Any useful data points about the event | See this page for industry-specific examples of [events and event properties](/developer-guide/data-ingest/event-properties) ## Data Model FAQs We love any event that represents a conscious consumer choice. App opens, button clicks, form submissions, streaming, and similar product usage events are all excellent material to include in addition to the core conversion events of purchases or enrollments. Read more about [what makes for a quality event feed](/developer-guide/data-ingest/data-ingest-help/what-events-should-i-send-to-aampe).  They do not. User attributes are helpful and important for defining who is eligible to receive a given message, but all learning anchors against real human behavior observed through your event feed.  Every time agents interact with a customer, they look for a change in behavior in the customer's event feed. A higher resolution event feed makes it much easier for agents to detect subtle (but important) changes in behavior. This in turn leads to agents that can reach conclusions about your customers more quickly and accurately than if they were given only purchase events.  # Amplitude (Batch) Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/amplitude-batch Estimated setup time: 30 minutes ## Requirements * Amplitude Project with your Aampe datasets defined (users, events) * Service Account Key (optional) ## Batch Sync Amplitude and Aampe will sync data at a regular cadence (e.g. daily, hourly). You have several options for syncing an Amplitude dataset with Aampe. * Option 1: Amplitude pushes to Aampe-provisioned Google Cloud Storage * Option 2: Amplitude pushes to Aampe's BigQuery * Option 3: Aampe pulls data using the Amplitude API ## Option 1: Google Cloud Storage 1. In Amplitude, navigate to the **Data** tab 1 Main Page Pn 2. Find **Destinations** on the Data menu. Be sure you're using an Amplitude project with comprehensive data for all the users and events you wish to send to Aampe. 2 Destinations Pn 3. Find **Google Cloud Storage** in the catalog of available destinations. 5 Gcp Menu Pn 4. Select the following options from the `Export Data to Google Cloud` menu * Export events ingested today and moving forward * Export all merged Amplitude IDs * Select all events (or apply filters if needed) 5. Review the tables and click **Next**. 5a Gcp Settings Pn 6. Aampe will provide you with a Service Account Key file. If you have your key file ready, upload it to Amplitude along with the corresponding bucket information. Click **Next**. 5b Gcp Key Pn 7. Amplitude will attempt a test upload to check that the entered credentials work. If the upload is successful, click **Finish** to complete the GCS destination configuration and activation. ## Option 2: Google BigQuery 1. Amplitude will directly write data to the Aampe BigQuery instance 2. In Amplitude Data, click **Catalog** and select the **Destinations** tab. 1 Main Page Pn 3. In Amplitude Data, click **Catalog** and select the **Destinations** tab. 2 Destinations Pn 4. Add destination 3 Add Destination Pn 5. In the **Warehouse Destination** section, click **BigQuery**. 4 Add Bigquery Pn 6. On the **Getting Started** tab, select the data you'd like to export and the export's cadence. You can Export events ingested today and moving forward, Export all merged Amplitude IDs, or both. For events, you can also specify filtering conditions to only export events that meet certain criteria. 7. Review the schemas for the Event table and the Merge IDs table and click **Next**. 4a Bigquery Step1 Pn 8. Specify the BigQuery dataset to receive your Amplitude data, and upload the service account key file. This file must be in JSON format and will be provided by Aampe. 4b Bigquery Pn 9. Click **Next**. Amplitude attempts a test upload to test the credentials. If the upload is successful, click **Finish** to complete the BigQuery destination configuration and activation. ## Option 3: Amplitude Export API Aampe will pull data from the [Amplitude Export API](https://www.docs.developers.amplitude.com/analytics/apis/export-api/). The Amplitude Export API is only best for small datasets. For large data sets, please consider exporting to Google Cloud Storage or Google BigQuery. To send Aampe the necessary credentials 1. In your Amplitude workspace, click on **Settings** at the bottom left of your screen 6 Organization Settings Pn 2. Select **Projects** from the navigation bar that opens. 3. Select the project with the events you would like Aampe to ingest. 6b Project List Pn 4. Copy your **API Key** and **Secret Key** 6c Project Details Pn 5. Navigate to **System Settings > Integrations** and select the **Data Import** tab 7a Aampe Amplitude Pn 6. Select **Amplitude** and add your credentials 7b Aampe Amplitude Keys Pn 7. Click **Request Integration** to complete the integration between Aampe and Amplitude. Unsure what events to send to Aampe? See [here](/developer-guide/data-ingest/what-events-should-i-send-to-aampe) for what a "good" event feed looks like. # Amplitude (Cohort Sync) Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/amplitude-cohort Estimated setup time: 1 hour ## Requirements * A user cohort in Amplitude * Your Aampe API Key To get your Aampe API Key * Go to [https://compose.aampe.com/system-setup/integrations/events](https://compose.aampe.com/system-setup/integrations/events) * Login using your org credentials * Select **Connect** next to the **Aampe** endpoint * Copy the API key ## Aampe Official Destination 1. Select **Data** from the main menu 1 Main Page Pn 2. Click **Destinations** 2 Destinations Pn 3. Click the `+ Add Destination` button 3 Add Destination Pn 4. Search the destination catalog for **Aampe** and select **Cohorts** 9 Cohort Pn 5. In the `Connect to Aampe` menu, select `Add another destination` 9a Cohort Add Pn 6. Enter the following details: * **Name:** Aampe - Cohorts * **API key:** api\_key * Select the **User ID mapping**. Here you want to select user ID which connects to your other Aampe data. Aim for a non-PII identifier if available. Click **Save**. 9b Cohort Form Pn 7. From the Amplitude home page, go to **Users → Cohorts**. 11 User Cohort Pn 8. Select any Cohort that you want to sync. Click **Target Users** 11a Target Cohort Pn 9. **Sync to Aampe destination** 11b Target Aampe Pn 10. Choose the API you're sending data to 11c Select Api Pn 11. Finally, select **Real-time Sync** and hit **Sync** 11d Define Cadence Pn Congrats. Your Aampe cohort integration is live! *** # Amplitude (Streaming) Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/amplitude-streaming Estimated setup time: 30 minutes ## Requirements * Your Aampe API Key To get your Aampe API Key * Go to [https://compose.aampe.com/system-setup/integrations/events](https://compose.aampe.com/system-setup/integrations/events) * Login using your org credentials * Select **Connect** next to the **Aampe** endpoint * Copy the API key ## **Amplitude's Destination Catalog** 1. Go to **Menu -> Data** 1 Main Page Pn 2. Click on **Destinations** 2 Destinations Pn 3. Click Add a new destination. 8 Add New Destination Pn 4. Search for **Aampe** and select **Events and User Properties** 8 Destination Menu Aampe Pn 5. Enter the following details 1. Sync Name: Aampe - Data Integration 8b Name Pn 2. Set the following destination settings * Status: Enabled * Aampe API Key * User identifier: Select the User ID mapping. Here you want to select user ID which connects to your other Aampe data. Aim for a non-PII identifier if available. * Check that both **Send Users** and **Send Events** are selected 8d Destination Menu Pn 6. Select the events and properties you want to send. Unsure what events to send? See [here](/developer-guide/data-ingest/what-events-should-i-send-to-aampe) for guidance on what a "good" event stream looks like. 7. Hit **Save** Congrats. Your Amplitude-Aampe data integration is live! # CleverTap Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/clevertap Estimated setup time: 15 minutes ## Requirements * CleverTap admin permissions * A GCP bucket (provided by Aampe) * Service account key (provided by Aampe) If you would prefer to provision your own cloud storage, you can follow the guide using your own bucket and key. To complete the sync, simply provide Aampe with the bucket details. ## Step 1: Integrate Google Cloud as a Partner 1. Navigate to **Settings > Partners** from the CleverTap dashboard 1 Select Integration Pn 2. Hover on the **Google Cloud** icon and click **Integrate** 3. Click **+ Google Cloud Bucket** to add a bucket. You can also add multiple buckets from this popup 4. Enter the following details and click **Save Credentials**: * **Bucket nickname:** Enter a nickname for this bucket integration * **Service Account Key:** Copy/paste the JSON string provided by Aampe (or the contents of the service account key JSON file if you decided to set up your own bucket) * **Bucket name:** Enter the name of the storage bucket provided by Aampe (or that you created if you decided to set up your own bucket) 2 SA Key Pn The new integration will appear on the list of Google Cloud integrations. 5. Press on **Done** to continue ## Step 2: Create Data Export Aampe uses both event and profile data, and this guide walks you through setting up an export for both CleverTap User Events and User Profiles. 1. Navigate to **Settings > Partners** from the CleverTap dashboard 2. Click on the **Exports** tab 3. Click **+ Export** and select **Google Cloud** from the partners dropdown 3 Create Data Export Pn ### Create Export for Events 1. The **Export to Google Cloud** modal will show up on the right side of the screen. 2. Select the bucket defined in the previous step 3. Configure the following settings: * **Type:** All events * **Frequency:** Recurring * **Frequency of data export:** Every 24 Hours * **Format:** JSON 4 Export Menu Pn 4. Click **Export** On clicking, the popup closes, and the "Google export has initiated" message displays at the top of the Exports page. You will now be able to see the newly-created export for Google Cloud. 5 Initiate Export Pn ### Create Export for User Profiles We will now repeat steps 2 through 5 to create an export for User Profiles: 1. Click **+ Export** and select **Google Cloud** from the partners dropdown 2. The **Export to Google Cloud** window displays on the right side of the screen. Please select the bucket defined in integration earlier 3. Configure the following settings: * **Type:** All user profiles * **Frequency:** Recurring (Every 24 Hours) * **Format:** JSON 4. Click **Export** Congrats. Your CleverTap data integration is all set! *** # AWS S3 Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/cloud-storage-aws-s3 Estimated setup time: 1 hour ## Getting Started Connect your Amazon S3 bucket to Aampe to ingest event data for personalization. ## Bucket Ownership Options Aampe supports two approaches for S3 data ingestion: | Option | Best For | Setup Time | | - | - | - | | [Customer-Owned Bucket](/developer-guide/data-ingest/data-sources/cloud-storage-aws-s3-customer-owned) | Enterprise customers who need full control over data storage and security policies | \~1 hour | | [Aampe-Owned Bucket](/developer-guide/data-ingest/data-sources/cloud-storage-aws-s3-aampe-owned) | Customers who prefer a simpler setup without managing bucket permissions | \~30 minutes | ## Which Option Should I Choose? **Choose Customer-Owned Bucket if you:** * Have existing data pipelines writing to S3 * Need to comply with data residency requirements * Want full control over bucket lifecycle policies, encryption, and access logs * Prefer to manage data in your own AWS account **Choose Aampe-Owned Bucket if you:** * Want the fastest setup experience * Don't have existing S3 infrastructure * Prefer Aampe to manage the storage infrastructure ## Data Format Regardless of which option you choose, your event data should follow the [Aampe Data Model](/developer-guide/data-ingest/data-model). Supported file formats include: * JSON or Newline Delimited JSON (ndJSON) - *Preferred* * CSV * Parquet The Aampe team is happy to help with providing alternatives if the above is in conflict with your IT policies. Please send an email to [support@aampe.com](mailto:support@aampe.com) and we'll find a solution. # Cloud Storage Aws S3 Aampe Owned Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/cloud-storage-aws-s3-aampe-owned Estimated setup time: 1 hour Use this guide when you want Aampe to provide and manage the S3 bucket. You simply upload your event data to a bucket we provision for you. *** ## Getting Started ### Step 1: Request a Bucket from Aampe Contact the Aampe team at [support@aampe.com](mailto:support@aampe.com) to request a dedicated S3 bucket. We will provision a bucket and provide you with: * **Bucket name** * **Region** * **Write credentials** (one of the options below) ### Step 2: Choose a Credential Type Aampe can provide credentials in several formats depending on your infrastructure: | Credential Type | Best For | | - | - | | IAM User Access Keys | Direct S3 uploads, AWS CLI, most ETL tools | | Pre-signed URLs | One-time uploads, serverless workflows | | Cross-Account Role | Organizations that prefer role-based access from their AWS account | Let us know your preference when requesting the bucket. ### Step 3: Configure Your Data Pipeline Once you receive credentials, configure your data pipeline to write to the Aampe bucket. #### Using AWS CLI ```bash theme={null} aws s3 cp events_20240115.json s3:///events_20240115.json ``` #### Using AWS SDK (Python Example) ```python theme={null} import boto3 s3 = boto3.client( 's3', aws_access_key_id='', aws_secret_access_key='', region_name='' ) s3.upload_file('events_20240115.json', '', 'events_20240115.json') ``` #### Using Pre-signed URLs If Aampe provides pre-signed URLs, you can upload directly via HTTP: ```bash theme={null} curl -X PUT -T events_20240115.json "" ``` *** ## Step 4: Upload Your Data Upload event data in one of these formats: * JSON or Newline Delimited JSON (ndJSON) - *Preferred* * CSV * Parquet ### File Naming Convention Use a consistent naming pattern: * **Daily files:** `events_YYYYMMDD.json` (e.g., `events_20240115.json`) * **With partitions:** `/year=2024/month=01/day=15/events_20240115.json` ### Data Format Your event data should follow the [Aampe Data Model](/developer-guide/data-ingest/data-model). *** ## Verification Once you start uploading, the Aampe team will confirm that we are successfully receiving your data. *** ## Frequently Asked Questions ### Where is the data stored? Aampe buckets are hosted in AWS regions optimized for our data processing infrastructure. If you have specific data residency requirements, let us know and we will accommodate where possible. ### Can I use my existing data pipeline? Yes. Most ETL tools (Fivetran, Airbyte, dbt, etc.) and cloud services (AWS Glue, Lambda, Step Functions) support writing to S3 with access keys. ### What happens to my data? Aampe processes your event data according to our [data processing agreement](https://aampe.com/legal/dpa). Data is used solely for providing personalization services to your organization. *** Contact us at [support@aampe.com](mailto:support@aampe.com) if you need assistance setting up your data pipeline. *** ## Related Resources * [Aampe Data Model](/developer-guide/data-ingest/data-model) * [AWS S3: Customer-Owned Bucket](/developer-guide/data-ingest/data-sources/cloud-storage-aws-s3-customer-owned) # Cloud Storage Aws S3 Customer Owned Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/cloud-storage-aws-s3-customer-owned Estimated setup time: 1 hour Use this guide when your organization owns and manages the S3 bucket. Aampe will read event data from your bucket using one of several authentication methods. *** ## Getting Started ### Step 1: Create Your Bucket Create an S3 bucket in your AWS account. Share the following with Aampe: * **Bucket name** (e.g., `acme-events-bucket`) * **Region** (e.g., `us-east-1`) ### Step 2: Choose an Authentication Method Aampe supports three methods for accessing your bucket. Choose the one that best fits your security requirements: | Method | Security Level | Best For | | - | - | - | | [Cross-Account IAM Role](#option-a-cross-account-iam-role-recommended) | Highest | Production environments, enterprise security requirements | | [IAM User Access Keys](#option-b-iam-user-access-keys) | Moderate | Simple setups, quick proofs of concept | *** ## Option A: Cross-Account IAM Role (Recommended) This is the AWS-recommended approach for third-party access. It uses temporary credentials and includes an External ID to prevent the [confused deputy problem](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html). ### How It Works 1. You create an IAM role in your AWS account 2. The role trusts Aampe's AWS account to assume it 3. Aampe assumes the role using temporary credentials 4. An External ID ensures only Aampe can assume the role on your behalf ### Setup Instructions #### 1. Request Credentials from Aampe Contact Aampe to receive: * **Aampe AWS Account ID** * **External ID** (unique to your organization) > **Important:** The External ID is generated by Aampe and should not be shared or changed. This prevents other parties from assuming your role through Aampe. #### 2. Create an IAM Policy Create an IAM policy with read permissions for your bucket. Name it something like `AampeS3ReadAccess`. ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::", "arn:aws:s3:::/*" ] } ] } ``` Replace `` with your actual bucket name. #### 3. Create an IAM Role with Cross-Account Trust Create a new IAM role with the following trust policy: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam:::root" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "" } } } ] } ``` Replace: * `` with the Account ID provided by Aampe * `` with the External ID provided by Aampe Name this role something like `AampeS3AccessRole`. #### 4. Attach the Policy to the Role Attach the `AampeS3ReadAccess` policy (from step 2) to the `AampeS3AccessRole` (from step 3). #### 5. Share the Role ARN with Aampe Provide Aampe with the full role ARN: ``` arn:aws:iam:::role/AampeS3AccessRole ``` ### AWS Documentation * [IAM Tutorial: Delegate access across AWS accounts using IAM roles](https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html) * [How to use External ID when granting access to third parties](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_third-party.html) *** ## Option B: IAM User Access Keys This simpler approach grants an Aampe-managed IAM user direct access to your bucket via a bucket policy. > **Note:** While easier to set up, this method uses long-lived credentials. AWS recommends using IAM roles (Option A) when possible. See [Security best practices in IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html). ### How It Works 1. You create a bucket policy that grants access to an Aampe IAM user 2. Aampe provides the IAM user ARN to include in your policy 3. Aampe accesses your bucket using access keys for that user ### Setup Instructions #### 1. Request the Aampe User ARN Contact Aampe to receive the IAM user ARN that will access your bucket. #### 2. Add a Bucket Policy Apply the following bucket policy to grant Aampe read access: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "AampeReadAccess", "Effect": "Allow", "Principal": { "AWS": "" }, "Action": [ "s3:GetObject", "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::", "arn:aws:s3:::/*" ] } ] } ``` Replace: * `` with the ARN provided by Aampe * `` with your bucket name ### AWS Documentation * [Bucket policies for Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-policies.html) * [Actions, resources, and condition keys for Amazon S3](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3.html) *** ## Step 3: Upload Your Data Upload event data to your bucket in one of these formats: * JSON or Newline Delimited JSON (ndJSON) - *Preferred* * CSV * Parquet ### File Naming Convention Use a consistent naming pattern: * **Daily files:** `events_YYYYMMDD.json` (e.g., `events_20240115.json`) * **With partitions:** `/year=2024/month=01/day=15/events_20240115.json` ### Data Format Your event data should follow the [Aampe Data Model](/developer-guide/data-ingest/data-model). *** ## Verification Once configured, the Aampe team will confirm that we are successfully receiving your data. *** ## Troubleshooting ### Access Denied Errors * Verify the bucket name in your policy matches exactly (case-sensitive) * Ensure both bucket-level (`arn:aws:s3:::bucket`) and object-level (`arn:aws:s3:::bucket/*`) resources are included * Check that the IAM role/user ARN matches what Aampe provided ### Role Cannot Be Assumed * Verify the External ID matches exactly (for Option A) * Check that the trust policy Principal matches Aampe's account/user * Ensure the role has the policy attached (not just created) *** If these options conflict with your IT policies or you need an alternative approach, contact us at [support@aampe.com](mailto:support@aampe.com). We are happy to work with your security team to find a solution. *** ## Related Resources * [Aampe Data Model](/developer-guide/data-ingest/data-model) * [AWS S3: Aampe-Owned Bucket](/developer-guide/data-ingest/data-sources/cloud-storage-aws-s3-aampe-owned) # Google Cloud Storage Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/cloud-storage-gcs Estimated setup time: 30 minutes ## Getting Started ### Step 1: Create Your Bucket Create a storage bucket in your cloud provider (Google Cloud Storage). Share the following with Aampe: * Bucket name * Region/location ### Step 2: Grant Access The Aampe team will provide you with a service account email or identifier. Grant this account read access to your bucket. #### Google Cloud Storage - Service Account Access **Option 1: Command Line** ``` gsutil iam ch serviceAccount::roles/storage.objectViewer gs:// ``` **Option 2: IAM Policy JSON** ``` { "bindings": [ { "role": "roles/storage.objectViewer", "members": [ "serviceAccount:" ] }, { "role": "roles/storage.legacyBucketReader", "members": [ "serviceAccount:" ] } ] } ``` **Option 3: Console** 1. Go to your bucket in GCS Console 2. Click "Permissions" → "Grant Access" 3. Add the Aampe service account with "Storage Object Viewer" role #### Google Cloud Storage - Workload Identity Federation (Enhanced Security) For organizations requiring Workload Identity Federation, Aampe will provide a subject identifier. 1. **Create a custom IAM role** with these permissions: * `storage.buckets.get` * `storage.objects.get` * `storage.objects.list` 2. **Set up Workload Identity Federation**: ``` # Create a Workload Identity Pool gcloud iam workload-identity-pools create aampe-pool \ --location="global" \ --display-name="Aampe Integration Pool" # Create a Workload Identity Provider gcloud iam workload-identity-pools providers create-oidc aampe-provider \ --location="global" \ --workload-identity-pool="aampe-pool" \ --issuer-uri="https://accounts.google.com" \ --allowed-audiences="" \ --attribute-mapping="google.subject=assertion.sub" # Grant access to the bucket gcloud storage buckets add-iam-policy-binding gs:// \ --member="principal://iam.googleapis.com/projects//locations/global/workloadIdentityPools/aampe-pool/subject/" \ --role="" ``` ### Step 3: Upload Your Data Upload event data daily in one of these formats: * **JSON** or **Newline Delimited JSON (ndJSON)** - Preferred * **CSV** * **Parquet** Use this file naming pattern: * Daily: `events_YYYYMMDD.json` (e.g., `events_20240115.json`) * With partitions: `/year=2024/month=01/day=15/events_20240115.json` ## Data Format Your event data should include these required fields (see [Data Models](https://docs.aampe.com/docs/data-models) for the full schema): ## Congrats. The Aampe team will confirm that we're successfully receiving your data. *** # BigQuery (Google) Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/data-warehouse-bigquery Estimated setup time: 30 minutes There are 2 ways to send data to Aampe from BigQuery: * Direct Read Access from Your BQ Dataset * Push to Google Cloud Storage Bucket ### Option 1: Direct Read Access from Your BQ Dataset Aampe will access your BQ with a dedicated service account. To grant Aampe read-only access to your BQ dataset, you need to create an Aampe service account and grant the following permissions: * `bigquery.datasets.get` * `bigquery.readsessions.create` * `bigquery.readsessions.getData` * `bigquery.tables.get` * `bigquery.tables.getData` * `bigquery.tables.list` #### Step-by-step Procedure 1. **Create the custom role:** * Go to [https://console.cloud.google.com/iam-admin/roles](https://console.cloud.google.com/iam-admin/roles) 1 Create Role Pn * Fill the form (title: Aampe Custom BQ Read Access) 2 Role Menu Pn * Add the following permissions: * `bigquery.datasets.get` * `bigquery.readsessions.create` * `bigquery.readsessions.getData` * `bigquery.tables.get` * `bigquery.tables.getData` * `bigquery.tables.list` 2. **Grant permissions:** * Go to [https://console.cloud.google.com/iam-admin/iam?project=](https://console.cloud.google.com/iam-admin/iam?project=) * Press **Grant access** button Bigquery3 Grant Access Pn * Fill the form "add principal" with the Aampe service account that we will provide you (We will provide you the service account to use for integration via email) * Fill the form "assign role" with **Aampe Custom BQ Read Access** 4 Save Grant Pn You need to provide us with the following details: * Project ID * Dataset ID * Data location * Table names (of all the tables that you would like to provide us data of) ### Option 2: Push to Google Cloud Storage Bucket When the data size is very large we recommend that you push data to a cloud storage bucket. We will then copy this data from your GCS bucket to one set up by Aampe for ingestion to our system. If you prefer, you can directly push to a bucket created by Aampe. #### Steps 1. Create a bucket on GCP. 2. Provide Aampe with access to that bucket (We will provide you the details of the account that you need to provide access to) 3. Set up export from BQ to the cloud storage bucket: [Export Data](https://cloud.google.com/bigquery/docs/exporting-data) 4. Automate the export for each day of event data For reference on the data model, please refer to [Data Models](/developer-guide/data-ingest/data-model). # Databricks Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/data-warehouse-databricks Estimated setup time: 1 hour ## Prerequisites * Databricks workspace with Unity Catalog enabled * Metastore created and attached to your workspace * Appropriate admin privileges to create service principals and grant permissions ## Integration Methods ### Option 1: Direct Read Access via Service Principal This method allows Aampe to directly access your Unity Catalog tables using a dedicated service principal with OAuth authentication. #### Required Permissions The Aampe service principal needs the following Unity Catalog privileges: * USE CATALOG - Access to view metadata in the catalog * USE SCHEMA - Access to view metadata in schemas * SELECT - Read access to tables and views * BROWSE - Optional: View object metadata in Catalog Explorer #### Step-by-step Procedure 1. Create the Service Principal **Option A: Databricks-managed Service Principal** 1. Go to your Databricks account console 2. Navigate to **User management** > **Service principals** 3. Click **Add service principal** 4. Provide a name (e.g., aampe-integration-sp) 5. Click **Add** **Option B: Azure Entra ID Service Principal (Azure Databricks only)** 1. Create an app registration in Azure Portal 2. Note the Application (client) ID 3. Add the service principal to your Databricks account 4. Generate OAuth Credentials 5. In the service principal's details page, go to the **Secrets** tab 6. Under **OAuth secrets**, click **Generate secret** 7. Set the secret's lifetime (maximum 730 days) 8. Copy and securely store: * **Client ID** (same as Application ID) * **OAuth Secret** (shown only once) 9. Click **Done** 10. Grant Unity Catalog Permissions Run the following SQL commands in a Databricks notebook or SQL editor: ``` -- Grant catalog-level permissions GRANT USE CATALOG ON CATALOG TO ; -- Grant schema-level permissions GRANT USE SCHEMA ON SCHEMA . TO ; -- Grant table-level read permissions GRANT SELECT ON TABLE .. TO ; -- Alternatively, grant SELECT on all tables in a schema GRANT SELECT ON SCHEMA . TO ; ``` Replace: * \: Your Unity Catalog name * \: Your schema/database name * \: Specific table names (or use schema-level grant) * \: The Application ID of your service principal 5. Provide Connection Details to Aampe Share the following information with the Aampe team: * **Workspace URL**: `https://.cloud.databricks.com` * **Client ID**: Your service principal's Application ID * **OAuth Secret**: The generated secret (transfer securely) * **Catalog Name**: Your Unity Catalog name * **Schema Names**: List of schemas containing relevant tables * **Table Names**: Specific tables to sync (if not syncing entire schemas) * **Workspace ID**: Your Databricks workspace ID * **Account ID**: Your Databricks account ID (for account-level access) ### Option 2: Push to Cloud Storage For large-scale data transfers, you can export data to cloud storage that Aampe can access. #### AWS S3 Setup 1. Create External Location ``` -- Create storage credential CREATE STORAGE CREDENTIAL IF NOT EXISTS aampe_storage_cred WITH (   AWS_IAM_ROLE = 'arn:aws:iam:::role/' ); -- Create external location CREATE EXTERNAL LOCATION IF NOT EXISTS aampe_export_location URL 's3:///aampe-export/' WITH (STORAGE CREDENTIAL aampe_storage_cred); ``` 2. Grant Permissions ``` -- Grant write access to the service principal GRANT CREATE EXTERNAL TABLE ON EXTERNAL LOCATION aampe_export_location  TO ; GRANT WRITE FILES ON EXTERNAL LOCATION aampe_export_location  TO ; ``` 3. Set up Automated Export Create a Databricks Job or Delta Live Tables pipeline to export data ## Data Models Ensure your exported data conforms to the Aampe Data Models specification. # AWS Redshift Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/data-warehouse-redshift Estimated setup time: 30 minutes # Redshift Integration There are two recommended approaches for integrating Amazon Redshift with Aampe: * Unload data into Amazon S3 from Redshift * Provide direct access for Aampe user to your Redshift cluster (and tables) with event data ## Unloading Data into Amazon S3 from Redshift ### 1. Create IAM Role for Redshift Create an IAM role that allows Redshift to access S3. This follows AWS security best practices by using roles instead of hardcoded credentials. ```sql theme={null} -- First, create the IAM role in AWS Console or CLI with the following trust policy: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "redshift.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } ``` Attach the following IAM policy to the role (replace `your-bucket-name` with the actual bucket): ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::your-bucket-name", "arn:aws:s3:::your-bucket-name/*" ] } ] } ``` ### 2. Attach IAM Role to Redshift Cluster Associate the IAM role with your Redshift cluster: ```sql theme={null} -- Get the role ARN and attach it to your cluster -- This can be done via AWS Console or CLI aws redshift modify-cluster --cluster-identifier your-cluster-name --iam-roles arn:aws:iam::account-id:role/RedshiftS3Role ``` ### 3. Unload Data to S3 Use the UNLOAD command to export data from Redshift to S3. Redshift's UNLOAD command is optimized for parallel processing and handles large datasets efficiently. ```sql theme={null} -- Example: Unload event data to S3 in Parquet format UNLOAD ('SELECT * FROM events WHERE event_date >= current_date - 1') TO 's3://your-bucket-name/events/year=2024/month=09/day=18/' IAM_ROLE 'arn:aws:iam::account-id:role/RedshiftS3Role' FORMAT AS PARQUET PARTITION BY (event_date) ALLOWOVERWRITE PARALLEL ON; ``` **Key UNLOAD parameters for optimization:** * `FORMAT AS PARQUET`: Efficient columnar format, recommended for analytics * `PARTITION BY`: Organizes data for better query performance * `PARALLEL ON`: Enables parallel processing (default, but explicit is better) * `ALLOWOVERWRITE`: Overwrites existing files * `MAXFILESIZE`: Controls individual file size (default 6.2GB) * `MANIFEST`: Generates manifest file for data verification **Alternative formats:** ```sql theme={null} -- CSV format with header UNLOAD ('SELECT * FROM events WHERE event_date >= current_date - 1') TO 's3://your-bucket-name/events/csv/' IAM_ROLE 'arn:aws:iam::account-id:role/RedshiftS3Role' FORMAT AS CSV HEADER PARALLEL ON; -- JSON format UNLOAD ('SELECT * FROM events WHERE event_date >= current_date - 1') TO 's3://your-bucket-name/events/json/' IAM_ROLE 'arn:aws:iam::account-id:role/RedshiftS3Role' FORMAT AS JSON PARALLEL ON; ``` ### 4. Automate the Unloading Process **Option A: Using Redshift Scheduler (Recommended)** Create a scheduled query to automate daily unloads: ```sql theme={null} -- Create a scheduled query (available in Redshift Console) -- or use the Redshift Data API with EventBridge CREATE EVENT RULE daily_unload_events SCHEDULE 'cron(0 2 * * ? *)' -- Run at 2 AM daily DESCRIPTION 'Daily unload of events to S3' STATE ENABLED; ``` **Option B: Using AWS Lambda + EventBridge** Create a Lambda function that executes the UNLOAD command: ```python theme={null} import boto3 import json def lambda_handler(event, context): redshift_data = boto3.client('redshift-data') # Execute UNLOAD command response = redshift_data.execute_statement( ClusterIdentifier='your-cluster-name', Database='your-database', DbUser='your-db-user', Sql=f""" UNLOAD ('SELECT * FROM events WHERE event_date = current_date - 1') TO 's3://your-bucket-name/events/year={{{{ year }}}}/month={{{{ month }}}}/day={{{{ day }}}}/' IAM_ROLE 'arn:aws:iam::account-id:role/RedshiftS3Role' FORMAT AS PARQUET PARTITION BY (event_date) ALLOWOVERWRITE PARALLEL ON; """ ) return { 'statusCode': 200, 'body': json.dumps(f'Query submitted: {response["Id"]}') } ``` ### 5. Monitoring and Optimization **Monitor UNLOAD performance:** ```sql theme={null} -- Check UNLOAD query performance SELECT query, elapsed, aborted, substring(querytxt,1,60) as sql_text FROM stl_query WHERE querytxt LIKE '%UNLOAD%' ORDER BY starttime DESC LIMIT 10; ``` **Best practices for optimization:** * Use appropriate distribution keys on source tables * Consider VACUUM and ANALYZE operations before UNLOAD * Monitor S3 costs and implement lifecycle policies * Use compression (GZIP, BZIP2) for cost optimization if network transfer is a concern ## Provide Direct Access for Aampe User to Your Redshift Cluster This approach allows Aampe to directly query your Redshift cluster. Follow the principle of least privilege for security. ### Setup Steps: #### 1. Create a Custom Database Role (if needed) ```sql theme={null} -- Create a read-only role for Aampe CREATE ROLE aampe_read_role; ``` #### 2. Create Database User ```sql theme={null} -- Create user with password authentication CREATE USER aampe_user PASSWORD 'secure_password_here'; -- Alternative: Create user without password for IAM authentication CREATE USER aampe_user; ``` #### 3. Grant Role to User ```sql theme={null} -- Grant the read role to the user GRANT ROLE aampe_read_role TO aampe_user; ``` #### 4. Grant Table-Level Permissions ```sql theme={null} -- Grant access to specific schemas and tables -- Replace 'your_schema' and table names with your actual schema/tables -- Grant schema usage GRANT USAGE ON SCHEMA your_schema TO aampe_read_role; -- Grant table select permissions GRANT SELECT ON your_schema.events TO aampe_read_role; GRANT SELECT ON your_schema.users TO aampe_read_role; GRANT SELECT ON your_schema.sessions TO aampe_read_role; -- Or grant access to all tables in schema (current and future) GRANT SELECT ON ALL TABLES IN SCHEMA your_schema TO aampe_read_role; ALTER DEFAULT PRIVILEGES IN SCHEMA your_schema GRANT SELECT ON TABLES TO aampe_read_role; ``` #### 5. Configure Network Security **VPC Security Groups:** Ensure your Redshift cluster's security group allows connections from Aampe's IP addresses on port 5439. **Subnet Groups:** If using private subnets, ensure proper routing and NAT gateway configuration. ### Connection Details Required Provide Aampe with the following connection information: * **Host/Endpoint**: `your-cluster-name.abc123.us-east-1.redshift.amazonaws.com` * **Port**: `5439` (default Redshift port) * **Database Name**: Your database name * **Schema Name**: Schema containing event tables * **Username**: `aampe_user` * **Password**: The password you created * **Table Names**: List of tables containing event data ## Data Models Refer to the [Data Models](/developer-guide/data-ingest/data-model) documentation for the expected schema and data format requirements. # Snowflake Integration Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/data-warehouse-snowflake Estimated setup time: 1 hour When importing from databases, many customers create separate tables/views for Aampe that are limited to only the data necessary for Aampe to function. ## Connect Snowflake to Aampe (via Google Cloud Storage) ### 1. Create Snowflake Integration Integrations are named, first-class Snowflake objects that avoid the need for passing explicit cloud provider credentials such as secret keys or access tokens; instead, integration objects reference a Cloud Storage service account. Aampe will provide you with the bucket link mentioned below. ``` CREATE STORAGE INTEGRATION gcs_int TYPE = EXTERNAL_STAGE STORAGE_PROVIDER = GCS ENABLED = TRUE STORAGE_ALLOWED_LOCATIONS = ('gcs-bucket-link'); ``` ### 2. Obtain GCP Service Account A GCP Service account is created by Snowflake as part of Step 1: ``` DESC STORAGE INTEGRATION gcs_int; ``` In the output of the above command, the `STORAGE_GCP_SERVICE_ACCOUNT` property contains the GCP service account name. Example output: ``` +-----------------------------+---------------+-----------------------------------------------------------------------------+------------------+ | property | property_type | property_value | property_default | +-----------------------------+---------------+-----------------------------------------------------------------------------+------------------| | ENABLED | Boolean | true | false | | STORAGE_ALLOWED_LOCATIONS | List | bucket-link-that-we-will-send-you | [] | | STORAGE_GCP_SERVICE_ACCOUNT | String | service-account@project.iam.gserviceaccount.com | | +-----------------------------+---------------+-----------------------------------------------------------------------------+------------------+ ``` We will take this service account and provide it permissions to add/modify data to the GCS bucket. ### 3. Create File Formats A parquet file format can be created in Snowflake as follows to unload snowflake data in the parquet format. Any other format like CSV or JSON also can be created. Here is an example script to create parquet format: ``` USE DB_NAME; USE SCHEMA_NAME; CREATE OR REPLACE FILE FORMAT parquet_unload_file_format TYPE = PARQUET SNAPPY_COMPRESSION = TRUE COMMENT = 'FILE FORMAT FOR UNLOADING AS PARQUET FILES'; ``` More options and file types can be found [here](https://docs.snowflake.com/en/sql-reference/sql/create-file-format.html). ### 4. Create External Stage In Snowflake, an EXTERNAL STAGE object references data files stored in a location outside of Snowflake. ``` USE DB_NAME; USE SCHEMA SCHEMA_NAME; CREATE OR REPLACE STAGE parquet_unload_gcs_stage URL = 'gcs-bucket-link' storage_integration = gcs_int FILE_FORMAT = parquet_unload_file_format COMMENT = 'GCS Stage for the Snowflake external Parquet export'; ``` ### 5. Unload Data From Snowflake, use the COPY command in Snowflake to unload data from a Snowflake table into a GCS bucket. To retain the column names in the output file, use the `HEADER = TRUE` copy option. ``` COPY INTO @/ FROM DB_NAME.SCHEMA_NAME.TABLE_NAME HEADER = TRUE; ``` For example: ``` COPY INTO @parquet_unload_gcs_stage/region FROM SF01.TEST01.REGION HEADER=TRUE; ``` For the minimal fields we need, you can refer to [Data Models](/developer-guide/data-ingest/data-model). There are various parameters for COPY command which can limit the partitions and provide optimizations: [https://docs.snowflake.com/en/sql-reference/sql/copy-into-location.html](https://docs.snowflake.com/en/sql-reference/sql/copy-into-location.html) ### 6. Automate the Unloading What we recommend is to automate the COPY command that unloads each day's events (single partition) into the external stage by using Snowflake TASK for it to run at a schedule: [https://docs.snowflake.com/en/sql-reference/sql/create-task.html](https://docs.snowflake.com/en/sql-reference/sql/create-task.html) An example of same would be: ``` CREATE TASK copy_to_gcs SCHEDULE = 'USING CRON 0 10 * * * Europe/Rome' COMMENT = 'Test Schedule' AS COPY INTO @parquet_unload_gcs_stage/region from SF01.TEST01.REGION HEADER=TRUE; ``` If you use Airflow on your side for orchestration you can also use that to schedule this query at a regular interval. Alternatively we can fetch directly from Snowflake ## Snowflake Direct Access Setup Create a dedicated Aampe user with read-only access to your data warehouse. Once configured, we'll handle the ETL scheduling on our side. ### Step 1: Switch to Account Admin Run this in your Snowflake worksheet: ```sql theme={null} USE ROLE ACCOUNTADMIN; ``` ### Step 2: Create Role and User Create a read-only role for Aampe (skip if you have an existing read-only role you'd prefer to use): ```sql theme={null} CREATE ROLE AAMPE_ROLE; CREATE USER AAMPE_DEVELOPER PASSWORD = 'aampe_developer' EMAIL = 'developer@aampe.com' MUST_CHANGE_PASSWORD = FALSE DEFAULT_ROLE = AAMPE_ROLE DEFAULT_WAREHOUSE = ; GRANT ROLE AAMPE_ROLE TO USER AAMPE_DEVELOPER; ``` ### Step 3: Grant Read Access Grant the role access to your warehouse, database, and tables. Adjust the scope as needed: ```sql theme={null} USE ROLE ACCOUNTADMIN; -- Current objects GRANT USAGE ON WAREHOUSE TO ROLE AAMPE_ROLE; GRANT USAGE ON DATABASE TO ROLE AAMPE_ROLE; GRANT USAGE ON ALL SCHEMAS IN DATABASE TO ROLE AAMPE_ROLE; GRANT SELECT ON ALL TABLES IN DATABASE TO ROLE AAMPE_ROLE; GRANT SELECT ON ALL VIEWS IN DATABASE TO ROLE AAMPE_ROLE; -- Future objects (so new tables are automatically accessible) GRANT USAGE ON FUTURE SCHEMAS IN DATABASE TO ROLE AAMPE_ROLE; GRANT SELECT ON FUTURE TABLES IN DATABASE TO ROLE AAMPE_ROLE; GRANT SELECT ON FUTURE VIEWS IN DATABASE TO ROLE AAMPE_ROLE; ``` ### Step 4: Share Connection Details Send us the following: * **Account** (from your URL, e.g., `tzviibq-hr75240` from `https://tzviibq-hr75240.snowflakecomputing.com`) * **Warehouse Name** * **Database Name** * **Schema Name** * **Table Names** (tables containing your event data) Once we receive these, we'll configure the connection and begin syncing data at regular intervals. **References:** * [https://docs.snowflake.com/en/user-guide/data-load-gcs-config.html](https://docs.snowflake.com/en/user-guide/data-load-gcs-config.html) * [https://docs.snowflake.com/en/user-guide/data-unload-prepare.html](https://docs.snowflake.com/en/user-guide/data-unload-prepare.html) # AWS Aurora Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/database-aws-aurora Instruction to connect Aampe to your Aurora instance **AWS Aurora Integration Steps** 1. In your Aurora DB Instance, create an Aampe user and password: Screenshot 2025-10-31 at 10.57.58 AM.png 2. Navigate to the database where your data is stored: Screenshot 2025-10-31 at 10.58.32 AM.png 3. Grant the Aampe user the following permissions: Screenshot 2025-10-31 at 10.58.50 AM.png 4. Provide us with the following information: * `username (the username of the Aampe user you created)` * `password (the password for this user)` * `region (the region your Aurora DB Cluster is hosted in)` * `endpoint (found in Connectivity & security, see screenshot below)` * `port (found in Connectivity & security, see screenshot below)` * `database name (the database schema where your event/user data tables are located)` * `tables (the tables containing your event/user data)` Screenshot 2025-10-31 at 10.59.15 AM.png 5. Finally, in Aurora add the IP addresses provided by Aampe to your DB Instance Security Group Inbound and Outbound rules. We will send you the IPs. # AWS RDS Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/database-aws-rds Estimated setup time: 1 hour This guide provides instructions for granting Aampe access to your AWS RDS database for data synchronization. Grant Aampe direct read access to your RDS instance for real-time data synchronization. ### Requirements * AWS account with an existing RDS instance * Admin access to your AWS account * RDS instance endpoint and database name * Network access configured (Security Groups/VPC) ### Setup Steps #### 1. Create Database User for Aampe Connect to your RDS instance and create a read-only user: ``` -- For MySQL/MariaDB CREATE USER 'aampe_reader'@'%' IDENTIFIED BY 'STRONG_PASSWORD_HERE'; -- For PostgreSQL CREATE USER aampe_reader WITH PASSWORD 'STRONG_PASSWORD_HERE'; -- For SQL Server CREATE LOGIN aampe_reader WITH PASSWORD = 'STRONG_PASSWORD_HERE'; CREATE USER aampe_reader FOR LOGIN aampe_reader; ``` #### 2. Grant Read Permissions ``` -- For MySQL/MariaDB GRANT SELECT ON your_database.* TO 'aampe_reader'@'%'; FLUSH PRIVILEGES; -- For PostgreSQL GRANT CONNECT ON DATABASE your_database TO aampe_reader; GRANT USAGE ON SCHEMA public TO aampe_reader; GRANT SELECT ON ALL TABLES IN SCHEMA public TO aampe_reader; -- For future tables ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO aampe_reader; -- For SQL Server USE your_database; ALTER ROLE db_datareader ADD MEMBER aampe_reader; ``` #### 3. Configure Network Access Update your RDS Security Group to allow Aampe's IP addresses: ``` # Add ingress rule to your RDS security group aws ec2 authorize-security-group-ingress \ --group-id sg-YOUR_SECURITY_GROUP_ID \ --protocol tcp \ --port YOUR_DB_PORT \ --cidr AAMPE_IP_RANGE/32 \ --group-rule-description "Aampe data sync access" ``` **Note:** Your Aampe representative will provide the specific IP addresses or ranges to allowlist. #### 4. Enable SSL/TLS (Recommended) ``` # Download RDS CA certificate wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem # For MySQL, enforce SSL for the Aampe user mysql> ALTER USER 'aampe_reader'@'%' REQUIRE SSL; ``` #### 5. Provide Information to Aampe Share the following with your Aampe representative: * **Endpoint:** `your-instance.region.rds.amazonaws.com` * **Port:** (default: MySQL 3306, PostgreSQL 5432, SQL Server 1433) * **Database name** * **Username:** `aampe_reader` * **Password:** (share securely) * **SSL certificate:** (if using SSL) * **List of tables to sync** # Spanner (Google) Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/database-spanner Estimated setup time: 1 hour ## Option 1: Direct Read Access Grant Aampe direct read access to your Spanner instance for real-time data synchronization. ### Requirements * Google Cloud project with Spanner instance * Admin access to your GCP project * Spanner instance ID and database name ### Setup Steps #### 1. Create a Service Account for Aampe ``` # Create service account gcloud iam service-accounts create aampe-spanner-reader \ --display-name="Aampe Spanner Reader" \ --description="Service account for Aampe to read Spanner data" # Get the service account email gcloud iam service-accounts list --filter="displayName:Aampe Spanner Reader" ``` #### 2. Grant Spanner Access ``` # Grant database reader role gcloud spanner databases add-iam-policy-binding YOUR_DATABASE_NAME \ --instance=YOUR_INSTANCE_ID \ --member="serviceAccount:aampe-spanner-reader@YOUR_PROJECT.iam.gserviceaccount.com" \ --role="roles/spanner.databaseReader" ``` #### 3. Create and Download Service Account Key ``` # Create key gcloud iam service-accounts keys create aampe-spanner-key.json \ --iam-account=aampe-spanner-reader@YOUR_PROJECT.iam.gserviceaccount.com # The key file will be downloaded to your current directory ``` #### 4. Provide Information to Aampe Share the following with your Aampe representative: * Service account key file (aampe-spanner-key.json) * Project ID * Spanner instance ID * Database name * List of tables to sync ## Option 2: BigQuery Federation (Recommended) If you prefer not to grant direct Spanner access, you can set up BigQuery federation and grant access to BigQuery instead. ### Prerequisites * BigQuery API enabled in your project * Existing Spanner instance with data ### Setup Steps #### 1. Create External Connection in BigQuery ``` -- In BigQuery Console -- Navigate to "Add Data" > "External data source" -- Select "Cloud Spanner" as source type -- Configure: -- Connection ID: aampe-spanner-connection -- Location: [Match your Spanner region] -- Spanner instance: YOUR_INSTANCE_ID -- Spanner database: YOUR_DATABASE_NAME ``` #### 2. Create Views for Aampe Access ``` -- Create a dataset for Aampe views CREATE SCHEMA IF NOT EXISTS `your_project.aampe_views` OPTIONS( description="Views for Aampe data access", location="us-central1" ); -- Create view for customer data CREATE OR REPLACE VIEW `your_project.aampe_views.customers` AS SELECT * FROM EXTERNAL_QUERY( 'projects/YOUR_PROJECT/locations/YOUR_LOCATION/connections/aampe-spanner-connection', ''' SELECT customer_id, email, created_at, -- Include only fields needed by Aampe FROM Customers ''' ); ``` #### 3. Grant BigQuery Access to Aampe ``` # Grant access to the views dataset gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \ --member="serviceAccount:AAMPE_SERVICE_ACCOUNT" \ --role="roles/bigquery.dataViewer" \ --condition="expression=resource.name.startsWith('projects/YOUR_PROJECT/datasets/aampe_views'),title=Aampe Views Access" ``` #### 4. Share Connection Details Provide Aampe with: * Project ID * Dataset name (aampe\_views) * List of available views ## Option 3: Cross-Project Access for Dataflow If Aampe will be running Dataflow jobs to extract your data, grant the necessary permissions for cross-project access. ### Prerequisites * Aampe will provide you with a service account email that will run the Dataflow jobs ### Setup Steps #### 1. Enable Required APIs ``` # Enable APIs that Aampe's Dataflow jobs will need gcloud services enable spanner.googleapis.com --project=YOUR_PROJECT ``` #### 2. Grant Spanner Access Grant Aampe's service account permission to read your Spanner data: ``` # Grant database reader role to Aampe's service account gcloud spanner databases add-iam-policy-binding YOUR_DATABASE_NAME \ --instance=YOUR_INSTANCE_ID \ --member="serviceAccount:AAMPE_PROVIDED_SERVICE_ACCOUNT" \ --role="roles/spanner.databaseReader" # If you want to limit access to specific tables, create a custom role gcloud iam roles create aampeSpannerReader \ --project=YOUR_PROJECT \ --title="Aampe Spanner Reader" \ --description="Read access to specific Spanner tables for Aampe" \ --permissions="spanner.databases.select,spanner.sessions.create,spanner.sessions.get,spanner.sessions.delete" ``` #### 3. Network Configuration (if using VPC) If your Spanner instance is in a VPC-enabled configuration: ``` # Option A: Add Aampe's project to your VPC Service Controls perimeter gcloud access-context-manager perimeters update YOUR_PERIMETER \ --add-resources="projects/AAMPE_PROJECT_NUMBER" # Option B: Create ingress rule for Aampe gcloud access-context-manager perimeters update YOUR_PERIMETER \ --add-ingress-policies=ingress.yaml ``` **Example ingress.yaml:** ``` - ingressFrom: sources: - resource: projects/AAMPE_PROJECT_NUMBER identityType: ANY_IDENTITY ingressTo: operations: - serviceName: spanner.googleapis.com methodSelectors: - method: "*" resources: - projects/YOUR_PROJECT_NUMBER ``` Share the following details: * Project ID * Spanner instance ID * Database name * List of tables to sync * Any network restrictions or VPC configurations ## Option 4: Real-time Streams For real-time event streaming, set up Spanner change streams to publish to a Pub/Sub topic that Aampe can subscribe to / you can push to Aampe endpoints. ### Prerequisites * Pub/Sub API enabled * Spanner database with change streams support ### Setup Steps #### 1. Create Change Streams ``` -- In Spanner CREATE CHANGE STREAM aampe_customer_changes FOR Customers(customer_id, email, status, updated_at) OPTIONS ( retention_period = '7d', value_capture_type = 'NEW_AND_OLD_VALUES' ); ``` #### 2. Create Pub/Sub Topic ``` # Create topic gcloud pubsub topics create aampe-spanner-changes # Grant Aampe subscription permissions gcloud pubsub topics add-iam-policy-binding aampe-spanner-changes \ --member="serviceAccount:AAMPE_SERVICE_ACCOUNT" \ --role="roles/pubsub.subscriber" ``` #### 3. Set Up Change Stream Export Use the Dataflow template to export changes: ``` gcloud dataflow jobs run aampe-change-stream-export \ --gcs-location gs://dataflow-templates/latest/Spanner_Change_Streams_to_PubSub \ --region YOUR_REGION \ --parameters \ spannerInstanceId=YOUR_INSTANCE,\ spannerDatabase=YOUR_DATABASE,\ spannerChangeStreamName=aampe_customer_changes,\ pubsubTopic=projects/YOUR_PROJECT/topics/aampe-spanner-changes ``` #### 4. Configure Dead Letter Queue (Optional) ``` # Create dead letter topic gcloud pubsub topics create aampe-spanner-changes-dlq # Create subscription with dead letter policy gcloud pubsub subscriptions create aampe-changes-sub \ --topic=aampe-spanner-changes \ --dead-letter-topic=aampe-spanner-changes-dlq \ --max-delivery-attempts=5 ``` ## Performance Considerations * Ensure indexes exist on frequently queried columns (customer\_id, created\_at) * Consider partitioning large tables by date * Monitor Spanner CPU utilization during sync operations ## Next Steps Once configuration is complete: * Notify your Aampe representative * We'll verify the connection and begin initial data sync * Monitor the first sync for any performance impacts * Set up alerts for failed syncs (if using change streams) *** # Firebase Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/firebase Estimated setup time: 40 minutes The Firebase data integration first sets up a Firebase to BigQuery export and then follows the steps to integrate Aampe with BigQuery as [shown here](/developer-guide/data-ingest/data-warehouse-bigquery). ## Export Firebase Event Data to BigQuery 1. Go to the **Integrations** page in the Firebase console. 2. In the **BigQuery** card, click **Link**. 3. After having read the details on the first page, click **Next**. 4. Toggle **Google Analytics** (this will enable exporting your app's click stream data). 5. Select the GCP region where you wish this data to be stored. 6. Toggle **Cloud Messaging** (this will enable exporting your push event data). 7. Select the GCP region where you wish this data to be stored. This guide is based on: [https://firebase.google.com/docs/projects/bigquery-export](https://firebase.google.com/docs/projects/bigquery-export) Now that you've set up the Firebase to BigQuery export, you can proceed to set up the BigQuery integration with Aampe. # Mixpanel Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/mixpanel Estimated setup time: 15 minutes ## Requirements * Your Mixpanel's Project ID and Data Residency * Service account credentials (Username & Secret) Aampe will periodically pull from Mixpanel's [RAW data export API](https://developer.mixpanel.com/reference/raw-data-export-api). ## Getting the Project ID and Data Residency 1. Login to your Mixpanel account 2. Click on the **Settings** gearwheel placed at the top-right corner of your screen. 3. Select your organization under **ORGANIZATION SETTINGS**. 1 Organization Setting Pn 4. Navigate to **Projects** 2 Project Setting Pn 5. Choose the project with the data you wish Aampe to ingest. 6. You will find your **Project ID** and **Data Residency** in the table. 3 Project Id Pn ## Creating a Service Account 1. Log into your Mixpanel account 2. Click on the **Settings** gearwheel placed at the top-right corner of your screen. 3. Select your organization under **ORGANIZATION SETTINGS** 4 Service Account Pn 4. Navigate to **Service Accounts** 5. Click on **+ Add Service Account** at the top-right corner. 4b Service Account New Pn 6. Choose a **NAME** for your service account. For example: `aampe-data-export` 7. Select the **PROJECT(S)** with the data you wish Aampe to ingest. 8. Keep the **ORGANIZATION ROLE** as Member and **PROJECT ROLE** as Consumer. 5 Sa Role Pn 9. Click on **Create** 10. The service account Username and Secret will appear. 6 Secrets Sa Pn 11. Store the username and secret and then we will use these to query the Mixpanel API. 12. Navigate to **System Settings > Integrations > Data Import** 7 Aampe Mixpanel Pn 13. Select **Mixpanel** and add your credentials. 8 Aampe Menu Pn *** # Segment Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/segment Estimated setup time: 15 minutes There are 3 ways to set up Aampe as a segment destination: * Configure Aampe (Actions) Destination - Preferred * Configure Aampe Destination * Google Cloud Storage Destination ## Configure Aampe (Actions) Destination - Preferred The newer actions based destination allows you to send track, page, screen and identify events. It also allows you to use region of choice for data residency. 1. Login to your Segment account at [https://app.segment.com/](https://app.segment.com/) 2. Select **Destinations** under Connections on the left side panel 3. Click on **Add Destination** 4. Enter **Aampe (Actions)** in the Filter Destinations searchbox. 5. On the Settings page you will see various options: * Give the name of the destination: "Aampe-Actions" * Your API Key: Note that you will need the Aampe API key that you can fetch from: 1. Go to [https://compose.aampe.com/system-setup/integrations/events](https://compose.aampe.com/system-setup/integrations/events) 2. Login using your org credentials 3. Select **Connect** next to Aampe endpoint 4. Copy the API key 6. Paste in the Aampe api key 7. Select your **data residency** (your preferred data region) 8. Change the mappings as needed depending on your data (Optional) You should be all set sending data to Aampe. ## Configure Aampe Destination 1. Login to your Segment account at [https://app.segment.com/](https://app.segment.com/) 2. Select **Destinations** under Connections on the left side panel 3. Click on **Add Destination** 4. Enter **Aampe** in the Filter Destinations searchbox. 5. Click on the **Configure Aampe** button, select the data source for your app events, then click on **Next**. 6. Enter a destination name of your choice and click on **Save**. We recommend naming it "Aampe App Events" 7. Login to [Aampe Composer](https://compose.aampe.com/configure/integrations) in another browser tab, then go to **System Settings > Integrations** 8. Select **Segment**, and then copy the Segment API key from here. 9. Paste this key into the **API Key** field under Connection Settings on the Segment website (this is where we left off at Step 6.) ## Configure Google Cloud Storage Destination **Requirements:** * A Google Cloud bucket name. For example: `aampe-staging-data-xxx` * A credentials file. For example: `xxxxxxxx-4e120-14a6f2ac3c25.json` Aampe will provide both of these to you for setup. 1. Go to Segment's **Connection > Destinations** section 2. Click on **Add Destination** 3. Search for **Google Cloud Storage** in the catalog that appears 4. Click on the **Google Cloud Storage** destination 5. Click on **Configure Google Cloud Storage**. 6. Select the source you wish to share with Aampe and then click on **Confirm Source**. Source will be your data source housing the event data 7. You will then be redirected to a screen where you will provide the destination bucket details (provided by Aampe): * **Bucket:** the name of the bucket where the data will flow * **GCS Private Key File:** the content of the JSON with the access credentials to that bucket 8. After having input both details, you need to click on the toggle that enables this destination and the integration is completed *** # SFTP Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/sftp Estimated setup time: 1 day If you don't push your event data into a 3rd party service, or have a pipeline for pushing to FTP, we also support that. ## The General Steps to Get This Working Are: 1. Aampe provides you with SFTP credentials. 2. You transfer event data daily, usually in JSON, ndJSON, CSV or parquet formats (we're happy to support more formats if needed). 3. The reference data schema is provided at [Data Models](/developer-guide/data-ingest/data-model) *** # Aampe Streaming API Source: https://kb.aampe.com/developer-guide/data-ingest/data-sources/streaming-aampe-api Estimated setup time: 1 hour The Aampe Events API endpoint allows you to post real-time event data to Aampe's system. This endpoint is designed for streaming live user interactions and activities as they occur within your application. ## Authentication To use this API, you'll need an API key for authentication. [**You can find it by going to System Setup > Integrations > API keys and copying the Ingest API Key.**](/api-guide/api-keys) ## Endpoint Details * **URL:** [https://ingestion.api.aampe.com/v1/events](https://ingestion.api.aampe.com/v1/events) * **Method:** POST * **Content-Type:** application/json ## Request Parameters ### Required Parameters | Parameter | Type | Description | | :- | :- | :- | | contact\_id | string | Unique identifier for the user triggering the event. Must be at least 1 character long. | | event\_name | string | Name of the event that occurred. Must be at least 1 character long. | | timestamp | string | Unix timestamp when the event occurred. | ### Optional Parameters | Parameter | Type | Description | | :- | :- | :- | | event\_id | string | Optional unique identifier for the event. | | metadata | string | Optional JSON object containing additional event information. | ## Example Request ``` { "contact_id": "user123", "event_name": "button_click", "timestamp": 1638360000, "event_id": "evt_123456", "metadata": { "button_location": "header", "page": "home", "user_type": "premium" } } ``` ## Response Codes | Code | Description | | :- | :- | | 202 | Event successfully received and accepted for processing | | 422 | Validation error - request parameters are invalid | ## Error Response Example ``` { "detail": [ { "loc": ["body", "contact_id"], "msg": "field required", "type": "value_error.missing" } ] } ``` ## Further Details Find detailed developer documentation [here](/api-guide/api-guide). *** # Event Properties Source: https://kb.aampe.com/developer-guide/data-ingest/event-properties Learn how to structure and implement event properties for Aampe Event properties capture user interactions and activities within your application. This documentation outlines how to structure and use event properties effectively. ## Core Event Structure Each event in the system follows this data model: | Property | Type | Required | Description | | - | - | - | - | | contact\_id | String | Yes | A unique user identifier | | timestamp | Timestamp | Yes | When the event happened in UTC | | event\_name | String | Yes | Name corresponding to the triggered event | | event\_instance\_id | String | No | Unique identifier for this specific event instance | | metadata | JSON | No | Additional contextual data about the event | ## Common Event Types ### Navigation Events Events that track how users move through your application: * `page_viewed` * Metadata: * `page_name`: Name of the page (e.g., "homepage", "product\_detail") * `page_url`: Full URL of the page * `referrer_url`: Where the user came from * `session_id`: Current session identifier * `feature_accessed` * Metadata: * `feature_name`: Name of accessed feature (e.g., "search", "filters") * `feature_location`: Where in the app the feature appears ### Transaction Events Events that track commerce and financial activities: * `product_viewed` * Metadata: * `product_id`: Unique product identifier * `product_name`: Name of the product * `product_category`: Category of the product * `product_price`: Current price * `currency`: Currency code (e.g., "USD") * `product_added_to_cart` * Metadata: * `product_id`: Product identifier * `quantity`: Number of items added * `variant_id`: Product variant (if applicable) * `cart_id`: Current cart identifier * `cart_value`: New total cart value * `checkout_started` * Metadata: * `cart_id`: Cart identifier * `cart_value`: Total value * `item_count`: Number of items * `coupon_code`: Applied discount code (if any) * `order_completed` * Metadata: * `order_id`: Unique order identifier * `order_value`: Total value of order * `payment_method`: Method used (e.g., "credit\_card", "paypal") * `shipping_method`: Chosen shipping option * `products`: Array of purchased product IDs * `quantities`: Array of quantities (corresponding to products) ### Messaging Events Events that track communication and notification activities: * `message_sent` * Metadata: * `message_id`: Unique identifier for the message * `message_type`: Type of message (e.g., "text", "email", "push") * `recipient_id`: ID of the recipient * `template_id`: If using a template, the template identifier * `campaign_id`: Associated marketing campaign (if applicable) * `message_delivered` * Metadata: * `message_id`: Message identifier * `delivery_timestamp`: When message was delivered * `delivery_status`: Status (e.g., "delivered", "failed") * `message_opened` * Metadata: * `message_id`: Message identifier * `open_timestamp`: When message was opened * `device_type`: Device used to open (e.g., "mobile", "desktop") * `message_clicked` * Metadata: * `message_id`: Message identifier * `link_id`: Identifier for the specific link * `link_url`: Target URL * `click_timestamp`: When click occurred ### Engagement Events Events that track how users interact with content: * `content_viewed` * Metadata: * `content_id`: Content identifier * `content_type`: Type of content (e.g., "article", "video") * `category`: Content category * `duration`: Time spent viewing (seconds) * `video_played` * Metadata: * `video_id`: Video identifier * `video_title`: Video title * `duration`: Length of video in seconds * `play_position`: Starting position in seconds * `quality`: Selected video quality ### Account Events Events tracking user account activities: * `user_registered` * Metadata: * `registration_method`: How they signed up (e.g., "email", "google") * `referral_source`: Where they came from * `initial_plan`: Selected plan (if applicable) * `user_logged_in` * Metadata: * `login_method`: Authentication method used * `device_type`: Type of device used * `app_version`: Application version ## Industry-Specific Events ### E-commerce Events * `product_searched` * Metadata: * `search_term`: User's search query * `filter_applied`: Any filters used * `results_count`: Number of results * `category_context`: Category being searched * `wishlist_updated` * Metadata: * `product_id`: Product identifier * `action`: "added" or "removed" * `wishlist_count`: New total count * `review_submitted` * Metadata: * `product_id`: Product identifier * `rating`: Numerical rating (e.g., 1-5) * `review_length`: Character count of review * `has_media`: Contains photos/videos (boolean) ### Gaming Events * `game_started` * Metadata: * `game_mode`: Selected mode * `map_id`: Game environment * `character_id`: Selected character/class * `difficulty`: Game difficulty * `level_completed` * Metadata: * `level_id`: Level identifier * `score`: Achieved score * `time_taken`: Completion time in seconds * `attempts`: Number of attempts * `stars_earned`: Achievement rating * `in_app_purchase` * Metadata: * `item_id`: Purchased item identifier * `item_type`: Type of virtual item * `price`: Cost in real currency * `currency`: Currency code * `purchase_location`: Where in game purchase was made ### Food Delivery Events * `restaurant_searched` * Metadata: * `search_term`: Search keywords * `cuisine_filter`: Any cuisine filters applied * `location`: Search location * `results_count`: Number of results * `menu_item_viewed` * Metadata: * `restaurant_id`: Restaurant identifier * `item_id`: Menu item identifier * `item_category`: Food category * `item_price`: Price * `dietary_tags`: Array of applicable dietary tags * `delivery_status_changed` * Metadata: * `order_id`: Order identifier * `new_status`: Current status (e.g., "preparing", "out\_for\_delivery") * `estimated_arrival`: Updated ETA * `driver_id`: Delivery person identifier # Data sync and rate limits Source: https://kb.aampe.com/developer-guide/data-ingest/rate-limits Aampe Data Sync & Rate Limits ## Messaging Operations Expected delays and rate limits for sending messages ### Processing Times * Messages scheduled through the system require up to 30 minutes for processing and dispatch. Any message has to be scheduled at least 30 minutes into the future * No internal rate limits apply to message volumes from Aampe's infrastructure * External delivery tool limits must be observed **High Volume Handling** For high-volume messaging scenarios, we support message batching configuration to optimize delivery tool integration. ## Data Processing Windows Expected delays with new data being available in Aampe Composer for messages and Audience segmentation. **User Attribute Updates** New user attributes require up to 24 hours from initial ingestion to become available in the Aampe composer for audience segmentation purposes. Example: New custom fields like **loyalty\_tier** or **last\_purchase\_date** Actual processing times may be shorter depending on system load All timeframes represent maximum processing windows If your use case requires faster processing times feel free to contact us # User Events sample data Source: https://kb.aampe.com/developer-guide/data-ingest/user-events-sample-data A set of dummy events of several industries which help understand the Aampe data model ## Industry-Specific Event Samples This guide provides comprehensive event examples for different industries, showing how to structure your events using the Aampe API/data dump. Each sample includes complete JSON payloads that you can adapt for your specific use case. ## E-commerce & Retail ### Product Discovery Journey **Product Search Event** ```json json theme={null} { "contact_id": "user_456789", "event_name": "product_searched", "timestamp": 1672531200, "event_id": "search_001", "metadata": { "search_term": "wireless headphones", "category": "electronics", "filters_applied": ["brand:sony", "price:50-200"], "results_count": 24, "search_location": "header_search", "session_id": "sess_abc123" } } ``` **Product View Event** ```json json theme={null} { "contact_id": "user_456789", "event_name": "product_viewed", "timestamp": 1672531260, "event_id": "view_001", "metadata": { "product_id": "prod_WH001", "product_name": "Sony WH-1000XM4 Wireless Headphones", "product_category": "Electronics > Audio > Headphones", "product_price": 299.99, "currency": "USD", "brand": "Sony", "in_stock": true, "product_rating": 4.5, "total_reviews": 1247, "referrer_source": "search_results" } } ``` ### Shopping Cart Journey **Add to Cart Event** ```json json theme={null} { "contact_id": "user_456789", "event_name": "product_added_to_cart", "timestamp": 1672531320, "event_id": "cart_add_001", "metadata": { "product_id": "prod_WH001", "quantity": 1, "variant_id": "color_black", "cart_id": "cart_xyz789", "unit_price": 299.99, "cart_value": 299.99, "cart_item_count": 1, "add_source": "product_page", "discount_applied": false } } ``` **Checkout Started Event** ```json json theme={null} { "contact_id": "user_456789", "event_name": "checkout_started", "timestamp": 1672531500, "event_id": "checkout_001", "metadata": { "cart_id": "cart_xyz789", "cart_value": 299.99, "item_count": 1, "shipping_method": "standard", "payment_method_selected": "credit_card", "coupon_code": "WELCOME10", "discount_amount": 29.99, "final_amount": 269.99, "currency": "USD" } } ``` **Purchase Completed Event** ```json json theme={null} { "contact_id": "user_456789", "event_name": "order_completed", "timestamp": 1672531800, "event_id": "order_001", "metadata": { "order_id": "ORD_2023_001", "order_value": 269.99, "payment_method": "visa_ending_1234", "shipping_method": "standard_delivery", "products": ["prod_WH001"], "quantities": [1], "shipping_address": "New York, NY", "estimated_delivery": "2023-01-03", "is_first_purchase": false, "order_source": "web" } } ``` ## SaaS & Software ### User Onboarding Flow **Account Registration Event** ```json json theme={null} { "contact_id": "user_789012", "event_name": "user_registered", "timestamp": 1672532000, "event_id": "reg_001", "metadata": { "registration_method": "email", "email_domain": "company.com", "referral_source": "google_ads", "utm_campaign": "winter_promotion", "selected_plan": "starter", "team_size": "1-10", "industry": "marketing", "use_case": "email_campaigns", "trial_days": 14 } } ``` **Feature Adoption Event** ```json json theme={null} { "contact_id": "user_789012", "event_name": "feature_first_used", "timestamp": 1672532400, "event_id": "feature_001", "metadata": { "feature_name": "email_builder", "feature_category": "core", "time_to_adoption": 3600, "user_tenure_days": 1, "session_id": "sess_def456", "guided_tour_completed": true, "help_docs_accessed": ["getting_started", "email_templates"] } } ``` ### Engagement & Usage **Project Created Event** ```json json theme={null} { "contact_id": "user_789012", "event_name": "project_created", "timestamp": 1672532800, "event_id": "proj_001", "metadata": { "project_id": "proj_ABC123", "project_name": "Q1 Newsletter Campaign", "project_type": "email_campaign", "template_used": "newsletter_modern", "team_members_invited": 2, "estimated_send_volume": 5000, "creation_source": "dashboard" } } ``` **Subscription Upgraded Event** ```json json theme={null} { "contact_id": "user_789012", "event_name": "subscription_upgraded", "timestamp": 1672533200, "event_id": "sub_upgrade_001", "metadata": { "previous_plan": "starter", "new_plan": "professional", "upgrade_reason": "usage_limit_reached", "annual_billing": true, "upgrade_value": 588.00, "features_unlocked": ["advanced_analytics", "a_b_testing", "priority_support"], "trial_days_remaining": 7 } } ``` ## Media & Entertainment ### Content Consumption **Video Started Event** ```json json theme={null} { "contact_id": "user_345678", "event_name": "video_started", "timestamp": 1672534000, "event_id": "video_start_001", "metadata": { "video_id": "vid_DEF789", "video_title": "Advanced React Patterns", "video_duration": 2400, "video_category": "Technology", "series_id": "series_react_mastery", "episode_number": 5, "video_quality": "1080p", "device_type": "desktop", "player_version": "v2.1.0", "autoplay": false } } ``` **Content Engagement Event** ```json json theme={null} { "contact_id": "user_345678", "event_name": "video_progress", "timestamp": 1672534600, "event_id": "video_progress_001", "metadata": { "video_id": "vid_DEF789", "progress_percentage": 25, "watch_time": 600, "playback_speed": 1.0, "quality_changes": 0, "paused_count": 2, "seek_count": 1, "engagement_score": 0.85 } } ``` **Subscription Event** ```json json theme={null} { "contact_id": "user_345678", "event_name": "content_creator_followed", "timestamp": 1672535000, "event_id": "follow_001", "metadata": { "creator_id": "creator_tech_guru", "creator_name": "Tech Guru", "creator_category": "Technology", "follower_count": 125000, "follow_source": "video_page", "notification_enabled": true, "user_following_count": 47 } } ``` ## Food & Beverage Delivery ### Restaurant Discovery **Restaurant Search Event** ```json json theme={null} { "contact_id": "user_901234", "event_name": "restaurant_searched", "timestamp": 1672535400, "event_id": "rest_search_001", "metadata": { "search_term": "italian pizza", "location": "Manhattan, NY", "delivery_time_filter": "30_minutes", "price_range_filter": "$$", "cuisine_filters": ["italian"], "dietary_filters": ["vegetarian_options"], "results_count": 18, "sort_by": "delivery_time" } } ``` **Menu Browsing Event** ```json json theme={null} { "contact_id": "user_901234", "event_name": "menu_item_viewed", "timestamp": 1672535600, "event_id": "menu_view_001", "metadata": { "restaurant_id": "rest_mario_pizza", "restaurant_name": "Mario's Authentic Pizza", "item_id": "item_margherita_large", "item_name": "Large Margherita Pizza", "item_category": "Pizza", "item_price": 18.99, "item_description": "Fresh mozzarella, basil, tomato sauce", "dietary_tags": ["vegetarian"], "prep_time": 20, "popularity_rank": 2 } } ``` ### Order & Delivery Journey **Order Placed Event** ```json json theme={null} { "contact_id": "user_901234", "event_name": "order_placed", "timestamp": 1672536000, "event_id": "order_food_001", "metadata": { "order_id": "ORD_FOOD_001", "restaurant_id": "rest_mario_pizza", "order_value": 28.47, "delivery_fee": 2.99, "service_fee": 1.99, "tip_amount": 4.50, "total_amount": 28.47, "items": [ { "item_id": "item_margherita_large", "quantity": 1, "unit_price": 18.99, "customizations": ["extra_cheese"] } ], "delivery_address": "123 Main St, Apt 4B", "estimated_delivery": 35, "payment_method": "credit_card" } } ``` **Delivery Tracking Event** ```json json theme={null} { "contact_id": "user_901234", "event_name": "delivery_status_updated", "timestamp": 1672536800, "event_id": "delivery_update_001", "metadata": { "order_id": "ORD_FOOD_001", "status": "out_for_delivery", "previous_status": "preparing", "estimated_arrival": 1672537200, "driver_id": "driver_john_d", "driver_name": "John D.", "driver_rating": 4.8, "delivery_vehicle": "bicycle", "live_tracking_enabled": true } } ``` ## Gaming & Entertainment ### Game Session Events **Game Session Started Event** ```json json theme={null} { "contact_id": "user_567890", "event_name": "game_session_started", "timestamp": 1672537200, "event_id": "game_start_001", "metadata": { "game_id": "adventure_quest_mobile", "game_version": "v1.2.3", "player_level": 15, "character_class": "warrior", "game_mode": "story_campaign", "chapter_id": "chapter_3", "device_type": "mobile", "platform": "ios", "session_id": "game_sess_xyz123" } } ``` **Achievement Unlocked Event** ```json json theme={null} { "contact_id": "user_567890", "event_name": "achievement_unlocked", "timestamp": 1672537800, "event_id": "achievement_001", "metadata": { "achievement_id": "first_boss_defeated", "achievement_name": "Dragon Slayer", "achievement_category": "combat", "achievement_rarity": "rare", "points_earned": 100, "reward_type": "gold", "reward_amount": 500, "player_level": 15, "completion_percentage": 0.25 } } ``` **In-App Purchase Event** ```json json theme={null} { "contact_id": "user_567890", "event_name": "in_app_purchase_completed", "timestamp": 1672538400, "event_id": "iap_001", "metadata": { "product_id": "gold_pack_large", "product_name": "Large Gold Pack", "product_type": "consumable", "price_usd": 9.99, "local_price": 9.99, "currency": "USD", "store": "app_store", "purchase_location": "shop_main", "player_level": 15, "gold_amount": 10000, "bonus_percentage": 20 } } ``` ## Healthcare & Fitness ### Fitness Tracking **Workout Started Event** ```json json theme={null} { "contact_id": "user_111222", "event_name": "workout_started", "timestamp": 1672539000, "event_id": "workout_001", "metadata": { "workout_id": "workout_hiit_001", "workout_name": "30-Min HIIT Blast", "workout_type": "HIIT", "workout_category": "cardio", "instructor_id": "trainer_sarah_j", "estimated_duration": 1800, "difficulty_level": "intermediate", "equipment_needed": ["dumbbells", "mat"], "calories_target": 300, "device_type": "mobile" } } ``` **Health Metric Logged Event** ```json json theme={null} { "contact_id": "user_111222", "event_name": "health_metric_logged", "timestamp": 1672540800, "event_id": "health_log_001", "metadata": { "metric_type": "weight", "metric_value": 165.5, "metric_unit": "lbs", "measurement_date": "2023-01-01", "data_source": "manual_entry", "goal_progress": 0.75, "trend_direction": "decreasing", "notes": "Morning weigh-in after workout" } } ``` ## Financial Services ### Account Activity **Transaction Event** ```json json theme={null} { "contact_id": "user_333444", "event_name": "transaction_completed", "timestamp": 1672541400, "event_id": "txn_001", "metadata": { "transaction_id": "TXN_2023_001", "transaction_type": "transfer", "amount": 250.00, "currency": "USD", "from_account": "checking_001", "to_account": "savings_001", "category": "savings", "method": "mobile_app", "fee_amount": 0.00, "balance_after": 1750.00, "merchant_name": null, "is_recurring": false } } ``` **Investment Activity Event** ```json json theme={null} { "contact_id": "user_333444", "event_name": "investment_order_placed", "timestamp": 1672542000, "event_id": "invest_001", "metadata": { "order_id": "INV_ORD_001", "order_type": "buy", "security_symbol": "AAPL", "security_name": "Apple Inc.", "quantity": 10, "order_value": 1500.00, "order_method": "market_order", "account_type": "individual_taxable", "portfolio_percentage": 0.15, "investment_goal": "growth" } } ``` ## Education & Learning ### Course Engagement **Lesson Started Event** ```json json theme={null} { "contact_id": "user_555666", "event_name": "lesson_started", "timestamp": 1672542600, "event_id": "lesson_001", "metadata": { "course_id": "course_javascript_fundamentals", "course_name": "JavaScript Fundamentals", "lesson_id": "lesson_variables_functions", "lesson_name": "Variables and Functions", "lesson_number": 3, "lesson_type": "video", "estimated_duration": 900, "difficulty_level": "beginner", "prerequisites_completed": true, "course_progress": 0.20 } } ``` **Assessment Completed Event** ```json json theme={null} { "contact_id": "user_555666", "event_name": "assessment_completed", "timestamp": 1672543500, "event_id": "assessment_001", "metadata": { "assessment_id": "quiz_js_basics", "assessment_type": "quiz", "score": 85, "max_score": 100, "passing_score": 70, "passed": true, "questions_total": 20, "questions_correct": 17, "time_taken": 600, "attempts": 1, "lesson_id": "lesson_variables_functions" } } ``` These samples provide a foundation for implementing comprehensive event tracking across different industries. # User Attributes Source: https://kb.aampe.com/developer-guide/data-ingest/user-properties Learn how to structure and implement user attributes for Aampe User properties are attributes and characteristics associated with individual users in your application. They help Aampe 1. understand user behavior and associate patterns with other users 2. segment your audience 3. personalize experiences and messaging copy. This documentation outlines common user properties and their usage. This is just a comprehensive list of all possible user properties. You can share whatever data is available in your system and is applicable as per your use-cases. ## Core User Properties ### Identification Properties * `user_id`: Unique identifier for the user (e.g., "usr\_12345") * `anonymous_id`: Temporary identifier for users before they sign up (e.g., "anon\_789xyz") * `notification_token`: Notification token of the user for push ### Profile Information * `full_name`: Complete name (e.g., "John Doe") * `username`: Chosen username (e.g., "johndoe123") * `language`: Preferred language (e.g., "en-US") * `timezone`: User's timezone (e.g., "America/New\_York") ### Demographics * `gender`: User's gender (e.g., "female", "male", "non-binary") * `country`: Country of residence (e.g., "United States") * `city`: City of residence (e.g., "San Francisco") ## Account Properties ### Subscription Data * `plan_name`: Current subscription plan (e.g., "premium", "basic", "enterprise") * `plan_price`: Price of current plan (e.g., 99.99) * `billing_interval`: Billing frequency (e.g., "monthly", "annual") * `trial_end_date`: When free trial expires (e.g., "2024-02-15") * `subscription_status`: Current status (e.g., "active", "canceled", "past\_due") ### Account Status * `account_type`: Type of account (e.g., "individual", "business") * `account_status`: Current standing (e.g., "active", "suspended", "deleted") * `created_at`: Account creation date (e.g., "2023-01-15T10:30:00Z") * `last_login_at`: Most recent login timestamp * `is_verified`: Email verification status (e.g., true/false) ## Behavioral Properties ### Engagement Metrics * `login_count`: Number of times logged in (e.g., 42) * `last_active_at`: Last activity timestamp * `total_sessions`: Total number of sessions * `average_session_duration`: Average time per session (in minutes) ### Transaction Data * `lifetime_value`: Total revenue from user (e.g., 599.99) * `total_purchases`: Number of completed purchases * `last_purchase_date`: Date of most recent purchase * `average_order_value`: Average spending per order * `payment_method`: Preferred payment method (e.g., "credit\_card", "paypal") ## Industry-Specific Properties ### E-commerce Properties * `cart_value`: Current shopping cart total (e.g., 129.99) * `abandoned_cart_count`: Number of abandoned carts (e.g., 3) * `favorite_categories`: Array of preferred shopping categories (e.g., \["electronics", "fashion"]) * `last_viewed_products`: Array of recently viewed product IDs * `shipping_addresses`: Array of saved delivery addresses * `preferred_payment_methods`: Array of saved payment methods * `wishlist_count`: Number of items in wishlist * `return_rate`: Percentage of returned items (e.g., 2.5) * `average_order_frequency`: Days between purchases (e.g., 45) * `loyalty_points`: Current reward points balance (e.g., 500) * `vip_tier`: Loyalty program level (e.g., "gold", "platinum") * `lifetime_value`: (users total lifetime value) * `currency`: (users transaction currency) ### Gaming Properties * `player_level`: Current game level (e.g., 42) * `total_playtime`: Time spent in game (in hours) * `current_rank`: Competitive ranking (e.g., "diamond") * `achievement_count`: Number of completed achievements * `preferred_game_modes`: Array of favorite modes (e.g., \["battle\_royale", "team\_deathmatch"]) * `in_game_purchases`: Total spent on virtual items * `active_character`: Current character/class being used * `win_rate`: Percentage of games won * `last_match_played`: Timestamp of last game session * `inventory_items`: Array of owned virtual items * `friend_count`: Number of in-game friends * `preferred_server`: Default game server region ### Food Delivery Properties * `dietary_preferences`: Array of dietary requirements (e.g., \["vegetarian", "gluten-free"]) * `favorite_cuisines`: Array of preferred cuisine types (e.g., \["italian", "thai"]) * `average_order_size`: Typical number of items per order * `delivery_address_count`: Number of saved addresses * `order_frequency`: How often they order (e.g., "weekly") * `favorite_restaurants`: Array of frequently ordered restaurant IDs * `last_cuisine_ordered`: Type of food in last order * `delivery_instructions`: Special delivery notes * `meal_preferences`: Preferred meal types (e.g., \["lunch", "dinner"]) * `group_order_frequency`: How often they place group orders * `review_count`: Number of restaurant reviews submitted * `average_rating`: Average rating given to restaurants ## Custom Properties ### Marketing Data * `acquisition_source`: How user found you (e.g., "google\_ads", "referral") * `acquisition_campaign`: Specific campaign (e.g., "summer\_2024") * `email_subscription`: Newsletter status (e.g., true/false) * `marketing_preferences`: JSON object with communication preferences ## Example JSON Representation ```Text json theme={null} { "user_id":"usr_5f3a9c2e", "first_name":"Sarah", "last_name":"Parker", "account_type":"business", "plan_name":"enterprise", "created_at":"2023-12-01T08:30:00Z", "last_login_at":"2024-01-26T15:45:22Z", "company_name":"TechCorp Solutions", "role":"Product Manager", "country":"United States", "timezone":"America/Los_Angeles", "lifetime_value":2499.99, "login_count":157, "is_verified":true, "subscription_status":"active", "trial_end_date":"2024-02-15", "marketing_preferences":{ "email":true, "sms":false }, "feature_usage":{ "dashboard":42, "reports":15 }, "acquisition_source":"google_ads", "last_purchase_date":"2024-01-15", "plan_price":299.99, "company_size":"100-500", "language":"en-US", "avatar_url":"https://assets.company.com/avatars/sp.jpg" } ``` # Developer Guide Source: https://kb.aampe.com/developer-guide/developer-guide Explore our integration guides and examples Aampe supports all the common SSO platforms. Find integration guides and data modelling suggestions. Aampe works through your existing stack for email, push, and SMS. Personalize the text or layout elements in your app or website. Embed product recommendations into Aampe messages. Bring Aampe user-profile and decision data into your data warehouse. ### Featured Pages * [What kind of data do we send to Aampe?](/developer-guide/data-ingest/data-integrations) * [What data does Aampe share with us?](/developer-guide/aampe-data/data-share-overview) # Integrations Source: https://kb.aampe.com/developer-guide/integrations What kind of integration would you like to set up today? What data should we send? What should it look like? Batch or streaming? Connect to 20 popular messaging partners (Braze, CleverTap, etc.) or set up a custom integration. # Adobe Campaign Classic Integration Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/adobe-campaign-classic Connect Aampe to Adobe Campaign Classic Message Center for Email and Push notifications Estimated setup time: 30-45 minutes This guide is for **Adobe Campaign Classic (ACC)** using the Message Center module. For Adobe Campaign Standard (ACS), see [Adobe Campaign Integration](/developer-guide/message-providers/message-pages/adobe-campaign-standard). ## Connection Requirements Aampe connects to ACC Message Center via the SOAP API (PushEvent method). ### Required from Customer | Requirement | Description | | - | - | | Message Center URL | Execution instance URL (e.g., `https://mc.customer.campaign.adobe.com`) | | API User Credentials | Username and password for Message Center access | | Whitelisted IPs | Aampe IPs must be added to trusted IP addresses | | Unsubscribe Endpoint URL | Your unsubscribe endpoint that supports One-Click List-Unsubscribe | Contact your Aampe representative to receive the list of IP addresses that need to be whitelisted. ### Locate Your Message Center URL 1. Log in to Adobe Campaign Classic console 2. Navigate to **Administration** → **Platform** → **External accounts** 3. Find the **Message Center execution instance** account 4. Copy the **Server** URL ### Unsubscribe Endpoint Requirements Your unsubscribe endpoint must: * Accept POST requests (for One-Click List-Unsubscribe from Gmail/Yahoo) * Accept a user identifier parameter that Aampe will populate per message * Return HTTP 200 on successful unsubscribe Provide Aampe with: * **Base URL:** e.g., `https://yourcompany.com/unsubscribe` * **Parameter name:** e.g., `uid`, `token`, or `email` Aampe will construct the full URL per message: `https://yourcompany.com/unsubscribe?uid={user_identifier}` ## Email Channel Setup ### Step 1: Create Event Type 1. Navigate to **Administration** → **Platform** → **Enumerations** 2. Find **Event type (eventType)** enumeration 3. Click **Add** to create a new value: * **Name:** `aampe_email` * **Label:** `Aampe Email` ### Step 2: Create Message Template 1. Navigate to **Message Center** → **Transactional message templates** 2. Right-click and select **New** 3. Select **Email delivery** as the template type 4. Configure template properties: * **Label:** `Aampe Marketing Email` * **Internal name:** `aampe_email` * **Event type:** Select `aampe_email` from dropdown ### Step 3: Configure Sender Details In the template's **From** section, configure sender to use Aampe payload: | Field | Value | | - | - | | From email | `<%= rtEvent.ctx.fromEmail %>` | | From name | `<%= rtEvent.ctx.fromName %>` | ### Step 4: Configure Email Content Aampe sends complete HTML content in the API payload. Configure your template as a passthrough: **Subject Line:** ``` <%= rtEvent.ctx.subject %> ``` **HTML Content (Source tab):** ```html theme={null} <%= rtEvent.ctx.body %> ``` Do not add any additional HTML structure. Aampe provides the complete email content including all styling and layout. ### Step 5: Configure SMTP Headers for Unsubscribe This step is required for email deliverability compliance (Gmail/Yahoo One-Click Unsubscribe). 1. Open template properties (click **Properties** button) 2. Go to **SMTP** tab 3. In **Additional SMTP headers**, add: ``` List-Unsubscribe: <<%= rtEvent.ctx.unsubscribeUrl %>> List-Unsubscribe-Post: List-Unsubscribe=One-Click ``` ### Step 6: Publish Template 1. Click **Publish** in the template toolbar 2. Select target execution instance(s) 3. Confirm publication Your email template is now ready. Aampe will send events to trigger personalized emails. *** ## Push Notification Channel Setup ### Step 1: Create Event Type for Push 1. Navigate to **Administration** → **Platform** → **Enumerations** 2. Find **Event type (eventType)** enumeration 3. Click **Add** to create a new value: * **Name:** `aampe_push` * **Label:** `Aampe Push` ### Step 2: Verify Mobile App Channel Push notifications require the Mobile App Channel package to be installed on your ACC instance. Contact your Adobe administrator if not available. Verify your mobile app is configured: 1. Navigate to **Administration** → **Channels** → **Mobile app** 2. Confirm your iOS/Android apps are registered ### Step 3: Create Push Message Template 1. Navigate to **Message Center** → **Transactional message templates** 2. Right-click and select **New** 3. Select **Mobile delivery (Push notification)** as the template type 4. Configure template properties: * **Label:** `Aampe Push Notification` * **Internal name:** `aampe_push` * **Event type:** Select `aampe_push` from dropdown ### Step 4: Configure Push Content Configure push content fields to use Aampe payload: | Field | Value | | - | - | | Title | `<%= rtEvent.ctx.title %>` | | Body | `<%= rtEvent.ctx.body %>` | | Deep link / URL | `<%= rtEvent.ctx.link %>` | | Image URL (iOS/Android) | `<%= rtEvent.ctx.imageUrl %>` | In iOS-specific settings: * **Alert title:** `<%= rtEvent.ctx.title %>` * **Alert body:** `<%= rtEvent.ctx.body %>` * **Media URL:** `<%= rtEvent.ctx.imageUrl %>` In Android-specific settings: * **Title:** `<%= rtEvent.ctx.title %>` * **Message:** `<%= rtEvent.ctx.body %>` * **Image URL:** `<%= rtEvent.ctx.imageUrl %>` ### Step 5: Publish Template 1. Click **Publish** in the template toolbar 2. Select target execution instance(s) 3. Confirm publication Your push notification template is now ready. ## Field Reference ### Email Payload Fields Aampe sends these fields in the `ctx` element: | Field | Type | Description | | - | - | - | | `fromEmail` | String | Sender email address | | `fromName` | String | Sender display name | | `subject` | String | Email subject line | | `body` | String | Complete HTML email content | | `unsubscribeUrl` | String | Full unsubscribe URL with user identifier | ### Push Payload Fields | Field | Type | Description | | - | - | - | | `title` | String | Push notification title | | `body` | String | Push notification body text | | `link` | String | Deep link or URL to open | | `imageUrl` | String | Rich media image URL | ## Validation Checklist Before going live, verify with your Aampe representative: * **API Connectivity** - Aampe can successfully authenticate to your Message Center instance * **Test Email** - Send a test email and verify sender name/email, content rendering, subject line, unsubscribe header presence (check raw email headers), and link tracking * **Test Push** - Send a test push notification and verify title/body text, deep link opens the correct screen, and image loads (if using rich push) * **Test Unsubscribe** - Click the unsubscribe link and verify your endpoint receives the request and the user is successfully unsubscribed in your system ## Official Documentation For more details on Adobe Campaign Classic Message Center: * [Event description (PushEvent API)](https://experienceleague.adobe.com/en/docs/campaign-classic/using/transactional-messaging/processing/event-description) * [Design transactional message templates](https://experienceleague.adobe.com/en/docs/campaign-classic/using/transactional-messaging/message-templates/creating-the-message-template) * [Transactional messaging architecture](https://experienceleague.adobe.com/en/docs/campaign-classic/using/transactional-messaging/configure-transactional-messaging/transactional-messaging-architecture) * [ACC Technical recommendations (SMTP headers)](https://experienceleague.adobe.com/en/docs/deliverability-learn/deliverability-best-practice-guide/additional-resources/campaign/acc-technical-recommendations) # Adobe Campaign Standard Integration Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/adobe-campaign-standard Connect Aampe to Adobe Campaign Standard for Email and Push notifications Estimated setup time: 30-45 minutes This guide is for **Adobe Campaign Standard (ACS)**. For Adobe Campaign Classic (ACC), see [Adobe Campaign Classic Integration](/developer-guide/message-providers/message-pages/adobe-campaign-classic). ## Connection Requirements Aampe connects to ACS via the REST API using OAuth Server-to-Server authentication. ### Required from Customer | Requirement | Description | | - | - | | Adobe Developer Console Access | Admin access to create API credentials | | Client ID | From Adobe Developer Console | | Client Secret | From Adobe Developer Console | | Organization ID | Your Adobe Organization ID | | Unsubscribe Endpoint URL | Your unsubscribe endpoint that supports One-Click List-Unsubscribe | ### Create API Credentials 1. Go to [Adobe Developer Console](https://developer.adobe.com/console/) 2. Create a new project or select an existing one 3. Click **Add API** and select **Adobe Campaign Standard API** 4. Select **OAuth Server-to-Server** authentication 5. Save your credentials: * Client ID * Client Secret * Organization ID ### OAuth Configuration | Parameter | Value | | - | - | | Token URL | `https://ims-na1.adobelogin.com/ims/token/v3` | | Scope | `urn:adobecloud:campaign` | ### Unsubscribe Endpoint Requirements Your unsubscribe endpoint must: * Accept POST requests (for One-Click List-Unsubscribe from Gmail/Yahoo) * Accept a user identifier parameter that Aampe will populate per message * Return HTTP 200 on successful unsubscribe Provide Aampe with: * **Base URL:** e.g., `https://yourcompany.com/unsubscribe` * **Parameter name:** e.g., `uid`, `token`, or `email` Aampe will construct the full URL per message: `https://yourcompany.com/unsubscribe?uid={user_identifier}` ## Email Channel Setup ### Step 1: Create Event Configuration 1. Click Adobe logo → **Marketing plans** → **Transactional messages** → **Event configuration** 2. Click **Create** ### Step 2: Configure Event Properties | Field | Value | | - | - | | Label | `Aampe Email` | | ID | `aampeEmail` | | Type | Transactional event | | Targeting dimension | Real-time event | | Channel | Email | ### Step 3: Add Custom Fields In the **Fields** section, add these exact field names: | Field Name | Type | | - | - | | `aampeFromEmail` | Text | | `aampeFromName` | Text | | `aampeSubject` | Text | | `aampeBody` | Text | | `aampeUnsubscribeUrl` | Text | ### Step 4: Publish Event Configuration Click **Save** then **Publish** ### Step 5: Configure Email Template After publishing, ACS creates a transactional message template. Open it to configure: **Sender Details:** | Field | Value | | - | - | | From email | `$(ctx.aampeFromEmail)` | | From name | `$(ctx.aampeFromName)` | **Subject Line:** ``` $(ctx.aampeSubject) ``` **Email Content (HTML source):** ```html theme={null} $(ctx.aampeBody) ``` Do not add any additional HTML structure. Aampe provides the complete email content including all styling and layout. ### Step 6: Configure SMTP Headers for Unsubscribe This step is required for email deliverability compliance (Gmail/Yahoo One-Click Unsubscribe). In the email template properties, add SMTP headers: ``` List-Unsubscribe: <$(ctx.aampeUnsubscribeUrl)> List-Unsubscribe-Post: List-Unsubscribe=One-Click ``` ### Step 7: Publish Template Click **Publish** to activate the transactional message. Your email template is now ready. Aampe will trigger personalized emails via the API. *** ## Push Notification Channel Setup ### Step 1: Create Event Configuration 1. Click Adobe logo → **Marketing plans** → **Transactional messages** → **Event configuration** 2. Click **Create** ### Step 2: Configure Event Properties | Field | Value | | - | - | | Label | `Aampe Push Notification` | | ID | `aampePush` | | Type | Transactional event | | Targeting dimension | Real-time event | | Channel | Mobile (Push notification) | ### Step 3: Add Custom Fields In the **Fields** section, add these exact field names: | Field Name | Type | | - | - | | `aampeTitle` | Text | | `aampeBody` | Text | | `aampeLink` | Text | | `aampeImg` | Text | ### Step 4: Publish Event Configuration Click **Save** then **Publish** ### Step 5: Configure Push Template After publishing, ACS creates a transactional message template. Open it to configure: **Push Content:** | Field | Value | | - | - | | Title | `$(ctx.aampeTitle)` | | Body | `$(ctx.aampeBody)` | | Deep link | `$(ctx.aampeLink)` | | Media URL | `$(ctx.aampeImg)` | ### Step 6: Publish Template Click **Publish** to activate the transactional message. Your push notification template is now ready. *** ## Field Reference ### Email Payload Fields Aampe sends these fields in the event context: | Field | Type | Description | | - | - | - | | `aampeFromEmail` | String | Sender email address | | `aampeFromName` | String | Sender display name | | `aampeSubject` | String | Email subject line | | `aampeBody` | String | Complete HTML email content | | `aampeUnsubscribeUrl` | String | Full unsubscribe URL with user identifier | ### Push Payload Fields | Field | Type | Description | | - | - | - | | `aampeTitle` | String | Push notification title | | `aampeBody` | String | Push notification body text | | `aampeLink` | String | Deep link or URL to open | | `aampeImg` | String | Rich media image URL | *** ## Validation Checklist Before going live, verify with your Aampe representative: * **API Connectivity** - Aampe can successfully authenticate with your ACS instance using OAuth credentials * **Test Email** - Send a test email and verify sender name/email, content rendering, subject line, unsubscribe header presence (check raw email headers), and link tracking * **Test Push** - Send a test push notification and verify title/body text, deep link opens the correct screen, and image loads (if using rich push) * **Test Unsubscribe** - Click the unsubscribe link and verify your endpoint receives the request and the user is successfully unsubscribed in your system *** ## Official Documentation For more details on Adobe Campaign Standard: * [Transactional messaging](https://experienceleague.adobe.com/en/docs/campaign-standard/using/communication-channels/transactional-messaging/getting-started-with-transactional-msg) * [Event configuration](https://experienceleague.adobe.com/en/docs/campaign-standard/using/communication-channels/transactional-messaging/event-configuration) * [Transactional message templates](https://experienceleague.adobe.com/en/docs/campaign-standard/using/communication-channels/transactional-messaging/transactional-template) * [REST API documentation](https://experienceleague.adobe.com/en/docs/campaign-standard/using/working-with-apis/about-campaign-standard-apis/about-campaign-standard-apis) # Airship Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/airship Estimated setup time: 15 minutes ## Requirements * Your Airship project's App Key * An API Access Token Aampe will call the [Airship API](https://docs.airship.com/api/ua/#operation-api-push-post) on your behalf. ## Generating an API Token 1. Once logged in to Airship, select the project that you are integrating with Aampe. 2. Select **Settings** from the navigation bar at the top of the screen. Settings Bar Pn 3. Scroll down to **Project configuration** until you find the **Tokens** section. Tokens Section Pn 4. Click on the **Manage** button. 5. In the Tokens screen that opens, click on **Create token** at the top right corner. Create Token Pn 6. Choose a name for this token. For example: `aampe-push-token` 7. Select **All Access** under Role. All Access Pn 8. Click on **Create token**. 9. Copy both the App Key and Access Token. Copy Token Pn ## Configure Your Airship Push Integration on Aampe 1. Go to [https://compose.aampe.com](https://compose.aampe.com) and login using your Aampe credentials 2. Navigate to **Configure > Push Provider** 3. Toggle the Airship integration 4. Fill in the App Key and Access Token where indicated 5. Press **Activate** Congrats! Aampe is now all set up to send push notifications via Airship! *** # Apple Push Notification service (APNs) Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/apns Estimated setup time: 1 hour ## Requirements * An encryption key (.p8 file) and its key ID * Your team ID and bundle ID/APNs topic * APNs user device tokens ## Setup Aampe will essentially establish a [token-based connection to APNs](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token-based_connection_to_apns) and use that to trigger notifications on your behalf. ## Generating Key 1. Login to your Apple developer account. 2. In [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources), click **Keys** in the sidebar, then click the add button (+) on the top left. 3. Under **Key Name**, enter a unique name for the key. 4. Select the checkbox next to the services you want to enable, then click **Continue**. * If the Media Services checkbox is disabled, you need to register a media identifier first. * If you register more than one media identifier, click **Configure** next to the checkbox. * On the next page, choose the media identifier you want to use from the pop-up menu, then click **Continue**. 5. Review the key configuration, then click **Confirm**. 6. Click **Download** to generate and download the key. This is the key you will share with Aampe. 7. Click **Done**. ## APNs User Device Tokens For Aampe to be able to trigger push notifications for your users, it needs to have a device token for each user. You can share those directly using a daily dump to an S3/GCP bucket. If you are already sharing user properties data with Aampe, the APNs device token for each user can simply be added as a column to the shared attribute table. See [Properties](https://aampe-docs.readme.io/docs/data-models#user-properties). If you are not sharing attribute data with Aampe then the easiest way to share APNs device tokens is by setting up a daily job that dumps a file with the tokens to a storage bucket. Aampe will take care of ingesting those tokens and always taking the latest value for each user in case of token updates. ### Data Format | Name | Data Type | Description | | :- | :- | :- | | contact\_id | String | A unique user identifier | | notification\_token | String | APNs device token | *** # AWS SNS Integration Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/aws-sns Estimated setup time: 1 hour ## Requirements * A pair of Access Key ID and Secret Access Key with SNS Publish credentials * The message format that you use Aampe will call the [AWS SNS Publish API](https://docs.aws.amazon.com/sns/latest/api/API_Publish.html) on your behalf. Configuring SNS for Aampe includes two steps: * Create an SNS Publish Policy * Create an IAM User for Aampe ## Create an SNS Publish Policy 1. In your AWS console, go to the **Identity and Access Management (IAM)** service. 2. Select **Policies** in the navigation menu. 3. Click on **Create Policy** 4. Click on **Choose a service** Create Policy Pn 5. Select **SNS** 6. In the **Actions** field, type **Publish** then select the Publish action that appears Publish Policy Pn 7. Click on **Resources**, keep the **Specific** radio button selected and tick the **Any in this account** checkbox 8. Click **Next: Tags** and then **Next: Review** 9. Choose a name for your policy, such as `SNSPublish` 10. Click on **Create Policy** ## Create an IAM User for Aampe 1. In your AWS console, go to the **Identity and Access Management (IAM)** service. 2. Select **Users** in the navigation menu. 3. Click on **Add users** 4. Choose a username, such as `aampe-sns` 5. Tick the **Access key - Programmatic access** checkbox Programmatic Access Pn 6. Click on **Next: Permissions** 7. Click on the **Attach existing policies directly** button Attach Existing Policy Pn 8. In the **Search** text field, type the name of the policy you created in the previous step and select it when it appears in the list under Policy name. Attach Existing Policy With Name Pn 9. Click on **Next: Tags** and **Next: Review** 10. Review your choices and click on **Create user** 11. You will then be led to a success screen, from which you can copy your **Access key ID** and **Secret access key** Copy Key Pn # Braze Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/braze Estimated setup time: 30 minutes ## Requirements * An API Key for your app group. * Braze App Id * The Braze [dashboard URL](https://www.braze.com/docs/api/basics/#api-definitions) that you use (dashboard-01 to 08 or [dashboard-01.braze.eu](http://dashboard-01.braze.eu)) **Note:** App Id setup is required only for email channel. Aampe will call the [Braze API for sending messages](https://www.braze.com/docs/api/endpoints/messaging/send_messages/post_send_messages/) on your behalf. ## Create an API Key 1. From the Braze dashboard go to **Settings > API Keys** 2. On this page you will find the **Rest API Keys** table 3. Click on **+ Create New API Key** Rest Api Pn Rest Api Pn 4. Choose a name for your new key, such as `aampe-personalization`. You'll see a list of checkboxes.\ \ 📘 **Note 2:** If required, contact your Aampe customer success manager or onboarding representative for the IP access range to enable IP whitelisting on your API allow list. Checkboxes Pn Checkboxes Pn 5. Tick the checkboxes to add following permissions: | Braze Permission | Braze REST API endpoint | | | :- | :- | - | | users.export.ids | [GET /users/export/ids](https://www.braze.com/docs/api/endpoints/export/user_data/post_users_identifier/#prerequisites) | For exporting current users | | users.export.segment | [GET /users/export/segment](https://www.braze.com/docs/api/endpoints/export/user_data/post_users_segment/#prerequisites) | For exporting current user attributes | | users.export.global\_control\_group | [POST/users/export/global\_control\_group](https://www.braze.com/docs/api/endpoints/export/user_data/post_users_global_control_group) | For exporting global control group membership | | sends.id.create | [POST /sends/id/create](https://www.braze.com/docs/api/endpoints/messaging/send_messages/post_create_send_ids/#prerequisites) | For creating IDs for message performance tracking | | messages.send | [POST /messages/send](https://www.braze.com/docs/api/endpoints/messaging/send_messages/post_send_messages) | Trigger an API Campaign in Braze. | | campaigns.trigger.send | [POST /campaigns/trigger/send](https://www.braze.com/docs/api/endpoints/messaging/send_messages/post_send_triggered_campaigns) | Trigger a send for a campaign that is configured for API-triggered delivery. | | campaigns.trigger.schedule.create | [POST /campaigns/trigger/schedule/create](https://www.braze.com/docs/api/endpoints/messaging/schedule_messages/post_schedule_triggered_campaigns/#prerequisites) | Trigger a send for a campaign that is configured for scheduled API-triggered delivery. | | campaigns.list | [GET /campaigns/list](https://www.braze.com/docs/api/endpoints/export/campaigns/get_campaigns) | Retrieve the list of all the campaigns configured in Braze and their associated metadata. | | campaigns.data\_series | [GET /campaigns/data\_series](https://www.braze.com/docs/api/endpoints/export/campaigns/get_campaign_analytics) | Retrieve the analytics data of a specific Braze campaign. | | campaigns.details | [GET /campaigns/details](https://www.braze.com/docs/api/endpoints/export/campaigns/get_campaign_details) | Retrieve the details of a specific Braze campaign. | | segments.list | [GET /segments/list](https://www.braze.com/docs/api/endpoints/export/segments/get_segment) | Retrieve the list of all the segments configured in Braze and their associated metadata. | | segments.data\_series | [GET /segments/data\_series](https://www.braze.com/docs/api/endpoints/export/segments/get_segment_analytics) | Retrieve the size of the Braze segment. | | segments.details | [GET /segments/details](https://www.braze.com/docs/api/endpoints/export/segments/get_segment_details) | Retrieve the details of a specific Braze segment. | | email.unsubscribe | | For retrieving email unsubscribe information | | email.hard\_bounces | [GET /email/hard\_bounce](https://www.braze.com/docs/api/endpoints/email/get_list_hard_bounces#query-or-list-hard-bounced-emails) | To retrieve email bounce information | | [templates.info](http://templates.info) | [GET /templates/email/info](https://www.braze.com/docs/api/endpoints/templates/email_templates/get_see_email_template_information) | Retrieve the details of a specific Braze HTML email template. | | templates.list | [GET /templates/email/list](https://www.braze.com/docs/api/endpoints/templates/email_templates/get_list_email_templates) | Retrieve the list of all the Braze HTML email templates configured in Braze and their subject line and HTML content. | | subscription.status.get | [GET /subscription/user/status](https://www.braze.com/docs/api/endpoints/subscription_groups/get_list_user_subscription_groups) | To get subscription status | | subscription.groups.get | [GET/ subscription.groups.get](https://www.braze.com/docs/api/endpoints/subscription_groups/get_list_user_subscription_groups/#prequisites) | To get subscription groups | 6. Finalize the key creation. 7. You can then view the newly created key by clicking on the gear icon at the right 8. Copy the API key value. 9. Login into the [Aampe](https://compose.aampe.com/auth/login) composer using your Aampe credentials. 10. Enter **System setup -> Integrations -> Braze**. Activate Braze Aampe Composer Pn 11. Enter the API key and dashboard URL copied from the earlier steps. Your connection is now ready to deliver messages ## App Id Setup 1. To find your app\_id go to your Braze account and navigate to **Settings > API and Identifiers > App Identifiers** 2. Copy your app\_id and navigate back to Aampe Braze integration page select the email channel and add your app\_id Braze Api Key Creation Braze Api Key Creation 📘 **Note:** App Id is not required if you do not intend to use the email channel through Braze. ## API Campaign Setup Setting up an API Campaign in Braze allows you to send transactional messages by generating a campaign\_id for use in your API calls. This process involves creating the campaign in the Braze dashboard and then using the campaign\_id in your API requests to send messages. Here's a step-by-step guide to setting up an API Campaign: ### Access the Braze Dashboard: 1. Log in to your Braze account. Navigate to the **Messaging** section and select **Campaigns**. 2. **Create a New Campaign:** Click on **Create Campaign**. Choose **API Campaigns** as the campaign type. Api Campaigns Pn Api Campaigns Pn ### Configure the Campaign: 1. **Name the Campaign:** Provide a descriptive title to easily identify the campaign later. 2. **Add Message:** Click on **Select Message**. Select the message types (channels) you intend to use (e.g., Email, SMS, Push Notifications). This action will generate a campaign\_id and a message variation ID for each channel. Save Api Campaign Pn Save Api Campaign Pn 3. **Optional - Add Conversion Events:** If you wish to track specific user actions as conversions, you can add a conversion event to the campaign. ### In the Campaign Limits: * **User re-eligible for messages:** Check this box and select 0 minutes as we would like Aampe agents to be re-eligible based on user's best time. * Select **Don't Count this towards intelligent Frequency limits** as we don't want Aampe to be affected by Braze limits ### Save the Campaign: 1. After configuring the necessary settings, click on **Save Campaign**. ### **Configure in Aampe:** 1. **Campaign ID:** After saving your campaign, retrieve the campaign\_id and add to the Braze Integration Settings in Composer. 2. **Variation IDs:** If you added messages to your campaign for each channel, note down the variation\_id for each and send it to your onboarding representative at Aampe. Braze Variation Id **You are all set with Braze!** # CleverTap Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/clevertap Estimated setup time: 10 minutes ## Requirements To perform the following steps, you need a CleverTap admin account for your project. ## Step 1: Create a User for Aampe 1. Login to your CleverTap dashboard as an admin user 2. Go to **Settings** at the bottom left corner of the screen Admin Menu Pn 3. Select **Users** Settings Menu Pn 4. Click on **+Users** Add Users Pn 5. Enter `developer+@aampe.com` in the Email address textbox 6. Check the **Creator** role from the dropdown list and click **Apply** 7. Click on **Invite** Invite User Pn ## Step 2: Generate a User-Passcode for Aampe 1. Login to your CleverTap dashboard as an admin user 2. Go to **Settings** at the bottom left corner of the screen Admin Menu Pn 3. Select **Users** Settings Menu Pn 4. Type `aampe` in the search box to find the user you previously created for Aampe 5. Press the 3 dot icon at the right side of the table once the Aampe user is found Fine New User Pn 6. Select **Grant Passcode** Grant Passcode Pn 7. Select **Forever** and click on **Grant** Grant Passcode Forever Pn **Note:** The granted passcode can always be revoked by pressing the 3 dot icon and selecting **Revoke Passcode**. ## Step 3: Configure Your CleverTap Push Integration on Aampe 1. Go to [https://compose.aampe.com](https://compose.aampe.com/) and login using your Aampe credentials 2. Navigate to **System setup > Integrations** 3. The **Messaging Provider** tab will open Aampe Composer Provider Menu Pn 4. Click on **CleverTap** Aampe Clevertap Page Pn 5. To get the account ID: Go to **Settings** (in Clevertap) and then **Project**. Copy the **Project ID** Clevertap Project Id Pn 6. For the passcode input the user passcode you just generated for Aampe in the above steps. 7. Press **Activate** **CleverTap - Aampe is all set up!** *** # Custom API Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/custom-api Aampe guidelines for all the one-of-a-kind message providers ## Custom Email Provider Connect Aampe to your own email infrastructure when you need full control over delivery or when using an email provider not yet supported by [Aampe's native integrations](/developer-guide/message-providers/message-providers). ## Requirements Aampe will need the following in order to deliver messages on your behalf: * A publicly accessible HTTP endpoint that accepts POST requests * An authentication method (API key, Bearer token, or custom header) * The ability to process JSON payloads and return JSON responses ## API Contract Your endpoint should implement the following contract for Aampe to successfully deliver email messages. ### Request Format Aampe sends a `POST` request with the following headers: ```text theme={null} POST https://your-endpoint.com/send-email Content-Type: application/json Authorization: ``` ### Payload Structure (Minimal) ```text theme={null} { "to": "recipient@example.com", "from": "Sender Name ", "content": { "subject": "Your email subject", "body": "

HTML email content

" }, "metadata": { "message_id": "uuid-string" } } ``` ### Field Reference | Field | Type | Required | Description | | :- | :- | :- | :- | | `to` | string | Yes | Recipient email address | | `from` | string | Yes | Sender name and email address | | `content.subject` | string | Yes | Email subject line | | `content.body` | string | Yes | Email body in HTML format | | `metadata` | object | No | Additional data for tracking or debugging | | `metadata.message_id` | string | No | Unique identifier for the message | > **Note:** The `body` field contains HTML content. Plain text fallback support is optional on your end. ### Response Format Your endpoint should return JSON responses with appropriate HTTP status codes. **Success (200 OK):** ```text theme={null} { "status": "accepted", "provider_message_id": "your-internal-id" } ``` **Error (4xx/5xx):** ```text theme={null} { "status": "error", "message": "Description of what went wrong" } ``` ## Example Implementation Here's a sample cURL request showing a common payload format: ```text theme={null} curl -X POST https://api.your-provider.com/send-email \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_KEY" \ -d '{ "to": "john.doe@example.com", "from": "Acme Support ", "content": { "subject": "Order Confirmation", "body": "

Hi John,
Your order has been confirmed.

" }, "metadata": { "message_id": "b3b1c2e4-8f12-4c9a-9a0d-acde12345678" } }' ``` ## Troubleshooting **Connection test fails** * Verify your endpoint is publicly accessible (not behind a firewall or VPN) * Check that your authentication credentials are correct * Ensure your endpoint returns valid JSON responses **Messages not delivering** * Confirm your endpoint returns a `200 OK` status for successful requests * Check your endpoint logs for incoming requests from Aampe * Verify the `provider_message_id` is being returned in successful responses **Need help?** Contact your Aampe representative or email [support@aampe.com](mailto:support@aampe.com) for assistance with your custom integration. # Customer.io Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/customerio Estimated setup time: 15 minutes ## Requirements * API Key created for Aampe * Your Region of setup for [Customer.io](http://Customer.io) ## Steps Aampe will call the [Customer.io](http://Customer.io)[ API](https://customer.io/docs/api/app/#operation/sendPush) on your behalf. ### Create API Key for Aampe 1. Once logged in to [Customer.io](http://Customer.io) account, navigate to **Settings Icon > Account Settings** Account Settings Pn 2. Select **API Credentials** from the menu bar. 2 Api Credentials Pn 3. Click on the 2nd tab i.e **Add API keys > Create App API Key** 3 Api Keys Pn 4. Give it the name you like (e.g `aampe_push_access`) and select your prod workspace and then hit **Create App API Key** 4 Create Api Key Pn 5. Copy the API Key shown on the prompt - you won't be able to access it again. ### Configure Your [Customer.io](http://Customer.io) Push Integration on Aampe 1. Go to [https://compose.aampe.com](https://compose.aampe.com) and login using your Aampe credentials 2. Navigate to **System setup > Integrations** 3. The **Messaging Provider** tab will open 5 Integration Menu Pn 4. Click on [**Customer.io**](http://Customer.io) 5. Input the API Key, Region in the respective textboxes * If you want to check your region you can go to **Settings -> Privacy and Data Settings** and check the **Data Center Region** 6 Aampe Customerio Page Pn 6. Click on **Connect** ### Step 2: Create Campaign for Aampe 1. Go to **Transactional** in the navigation bar 7 Campaign Pn 2. On Transactional, select **Create a Message** 3. Feed in the title and description for Aampe message and click on **Add Content** 8 Create Message Pn 4. Select **Push** or **Email** as appropriate. No need to add content -> Click on **Save & Continue** 9 Add Content Pn 5. Keep the default settings and launch the message 6. On the Transactional messages dashboard you will get an ID for the new message you created. Send the ID to Aampe. 10 Campaign Id Pn Congrats! You're all set with integrating Aampe and Customer.io. *** # Emarsys Integration Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/emarsys Estimated setup time: 30 minutes ## Requirements * An API user name and secret * A dedicated push campaign with personalization tokens and an external event Aampe will first guide you through the creation of a dedicated transaction externally triggered push campaign. It will then message your users by calling the Emarsys [External Event trigger API](https://dev.emarsys.com/docs/emarsys-api/61b720d3c0be6-trigger-an-external-event) on your behalf. ## Set Up API Permissions ### Create an API User and Secret The following steps need to be performed by an Emarsys account owner. 1. Login to the Emarsys application 2. Go to **Admin > Security Settings** 3. Press on **Create API user** 1 Admin Pn 4. Copy the resulting User name and Secret (note that the secret will no longer be available for copying once you leave this screen) 2 Api Key Pn ### Set Up API Permissions for the Newly Created User 1. Go to **Management > Security Settings > API Users** 2. Click on the edit button (in the form of a pencil) 3. You will reach a menu with toggles with all the possible Emarsys API permissions 3 Api Permissions Pn 4. Find and toggle the `externalevent.trigger` permission 5. Click on **Edit** at the bottom of the page to apply the permission modifications ## Create a Dedicated Campaign for Aampe A 4-step procedure to create a transaction push campaign that Aampe can use to personalize your notifications. ### Step 1: Create Personalization Tokens 1. Go to **Content > Personalization** 2. Click on **Create Token** 3. Select **External Data** in the Choose Personalization Capability window 4 Campaign Selection Pn 4. Enter "Aampe Title" under Name and "Aampe" under Category. Create "Aampe" category by clicking **Create Category** 5 Category Pn 5. Click on **New** in the JSON object section 6. Enter "aampe" under Name 7. Copy/paste the following JSON snippet into the code editor: ```json theme={null} { "aampe": { "title": "Push notification title", "body": "Push notification body" } } ``` 8. Press on **Modify** 9. Select `event.aampe.title` in the Field section 10. Click **Save** 11. Click on **Create Token** again selecting **External Data** in the Choose Personalization Capability window 12. This time, enter "Aampe Body" under Name and "Aampe" Category for this token. 13. Select the "aampe" JSON object that you just created 14. Select `event.aampe.body` in the Field section 15. Click **Save** ### Step 2: Create Push Campaign 1. Go to **Channels > Mobile Engage** 2. Select the **Push Messages** tab 3. Click **Create Push campaign** 4. In the campaign wizard, enter "Aampe" in Message name, select your mobile app and set Recipient Source to "Automation Center" 6 Push Campaign Pn 5. Drag the Aampe Title and Aampe Body personalization tokens respectively to the title and body sections of the notification in the preview pane. 6. Click **Ready to Launch** ### Step 3: Set Up an External Event 1. Go to **Management > External Events** 2. Click on **Create External Event** 3. Enter "Aampe push trigger" under External event name 4. Click on **Create** 5. Copy the external event ID number, it will be needed later when setting up the integration in the Aampe composer ### Step 4: Create an Interactions Program 1. Go to **Automation > Automation** 2. Click on **Create Program** 3. Select **Behavior-focused journeys (Interactions)** 4. Select **External event** as the trigger 7 Trigger Pn 5. Double click on the External event node 6. Select the external event created in Step 3 7. Click **OK** 8. Click on the **Decision** node to add it to the program 9. Double click on the new Decision node that you just added 10. Add the segment with all of your users 11. Click **OK** 12. Click on the **Mobile Engagement push message** channel to add it to the program 13. Double click on the Mobile Engagement push message node 14. Select the "Aampe" push campaign that you created in Step 2 using the dropdown menu 15. Click **OK** 16. Click **Activate** ## Set Up the Integration in the Aampe Composer 1. Go to [https://compose.aampe.com](https://compose.aampe.com) and login using your Aampe credentials 2. Navigate to **Configure > Push Provider** 3. Open the Emarsys integration 4. Input your API User name, Secret, and External Event ID you generated during this setup tutorial Congrats! You have successfully integrated Aampe and Emarsys. # Firebase Cloud Messaging Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/firebase-cloud Estimated setup time: 1 hour ## Requirements * Your Firebase project ID * Service account JSON credentials Aampe will essentially call the Firebase [Cloud Messaging API](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send) on your behalf. Setting up Firebase Cloud Messages involves four steps: 1. Enable the Cloud Messaging API 2. Create a custom role for message creation 3. Create a service account 4. Generate a key All the next steps assume that you start from your Google Cloud Platform console with your Firebase project selected. ## Enable the Cloud Messaging API 1. Type "Firebase Cloud Messaging API" in the Search products and resources textbox. 2. Choose **Firebase Cloud Messaging API** from the list. 1 Cloud Message Api Pn 3. Click on the blue **Enable** button. 2 Enable Pn ## Create a Custom Role for Message Creation A custom Role must be created in the system to allow Aampe to trigger notifications using the API because the default roles do not provide granular enough permissions. 1. Type "Roles" in the Search products and resources textbox and select the **Roles (IAM and Admin)** option. This will take you to the Roles page. 3 Iam Pn 2. Click on **+ CREATE ROLE** 3. Fill in the role Title, Description and ID according to your conventions. 4 Create Role Pn 4. Click on **+ADD PERMISSIONS** 5. In the window that opens, search for `cloudmessaging.messages.create` using the textbox to the right of the Filter and select the cloudmessaging.messages.create entry that appears. 5 Add Permissions Pn 6. Tick the `cloudmessaging.messages.create` permission and click on **ADD** 6 Add Permissions Create Pn 7. Once added, click on **CREATE** to complete the role creation. 7 Save Permissions Pn ## Create a Service Account 1. Type "Service accounts" in the Search products and resources textbox. 2. Choose **Service accounts (IAM and admin)** from the list. This will take you to the service account page. 8 Service Account Pn 3. Click on **+ CREATE SERVICE ACCOUNT** 9 Service Account Form Pn 4. Fill in the Service account name, ID and description according to your conventions. 10 Service Account Filled Out Pn 5. Click on **CREATE AND CONTINUE** 6. On the Grant this service account access to the project screen, click on the **Select a role** dropdown. 7. Under **Quick access > Custom**, select the custom role you created in the previous step. 11 Select Custom Pn 8. Click on **+ ADD ANOTHER ROLE** and then on the **Select a role** dropdown. 9. Type "Firebase Cloud Messaging Admin" in the box near the Filter. 10. Select the suggested **Firebase Cloud Messaging Admin** role. 12 Messaging Admin Role Pn 11. Click on **DONE** to complete the service account creation. 13 Done Pn ## Generate a Key This step assumes that you created a service account and assigned the "Firebase Cloud Messaging Admin" and custom message creation roles to it. 1. In your "Service accounts" page, find the service account you created for Aampe and click on the three dots in the Actions column and select **Manage keys** 14 Generate Key Pn 2. Click on **ADD KEY** and select **Create new key** 15 Copy Key Pn 3. Select the **JSON** Key type and click on **CREATE** 16 Json Key Pn 4. Your browser will download a `.json` file with the key needed by Aampe. Congrats! Keep your json key secure and let your Aampe technical counterparts know it is ready. They will take it from here. *** # Iterable Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/iterable Estimated setup time: 10 minutes ## Requirements * An API Key Aampe will message your users by calling Iterable's [Push target API](https://api.iterable.com/api/docs#push_target) on your behalf. ## Create an API Key 1. Sign in to your Iterable project as an org admin (or as a member that has the Third-Party Integrations, API Keys and Webhooks permission). 2. Go to **Integrations > API Keys**. 3. Click **New API Key**. 4. In the Create API Key window, enter a name for the key. Prefixing your key name with "Aampe" will be helpful for identifying it in the future. 5. Select **Server-side** as the key type. 6. Click **Create**. 7. Copy your new API Key. ## Save API Key in Aampe 1. Log into Aampe. Go to the **Integrations** page. Find the **Messaging provider** tab and click on **Iterable**. Aampe Iterable Pn *** # Leanplum Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/leanplum Estimated setup time: 15 minutes ## Requirements * Your App ID * Your Production API Key * (Optional) Your Data Export API Key Aampe will call the [Leanplum API](https://docs.leanplum.com/reference/post_api-action-sendmessage) on your behalf. ## Get Your App ID and API Keys 1. Once logged in to Leanplum, navigate to **Development > App Settings** 1 Admin Menu Pn 2. Select **API Keys** from the left navigation bar. 3. Copy the App ID, Production and Data Export keys. 2 Apikey Pn ## Configure Your Leanplum Push Integration on Aampe 1. Go to [https://compose.aampe.com](https://compose.aampe.com) and login using your Aampe credentials 2. Navigate to **System setup > Integrations** 3. The **Messaging Provider** tab will open 3 Aampe Menu Pn 4. Toggle the **Activate** knob on the Leanplum row 5. Input the App ID, Production and Data Export keys into their respective textboxes 4 Leanplum Aampe Pn 6. Click on **Connect** 🎉 **You're all set!** *** # MailChimp Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/mailchimp Estimated setup time: 15 minutes ## Prerequisites * Active Mailchimp account with Standard or Premium plan * Mandrill (Transactional Email) add-on enabled * Verified sending domain ## Required Credentials * API Key ## How to Get Your API Key: 1. Log in to your Mailchimp account 2. Navigate to **Account & Billing → Extras → Transactional Email** 3. Click **Launch App** to access Mandrill 4. Go to **Settings → SMTP & API Info** 5. Click **+ New API Key** 6. Provide a description and copy the generated key *** # Mandrill Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/mandrill Estimated setup time: 15 minutes ## Prerequisites * Active Mailchimp account with Standard or Premium plan * Mandrill (Transactional Email) add-on enabled * Verified sending domain ## Required Credentials * API Key ## How to Get Your API Key: 1. Log in to your Mailchimp account 2. Navigate to **Account & Billing → Extras → Transactional Email** 3. Click **Launch App** to access Mandrill 4. Go to **Settings → SMTP & API Info** 5. Click **+ New API Key** 6. Provide a description and copy the generated key # MoEngage Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/moengage Estimated setup time: 15 minutes ## Requirements * Your API ID and API SECRET * What you use as `targetUserAttributes` * \[Optional] The way you've implemented notification actions (deeplinking, navigation, etc.) Aampe will essentially call the [MoEngage Push API](https://docs.moengage.com/docs/push-api-v2) on your behalf. # Netcore Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/netcore Estimated setup time: 10 minutes ## Requirements * API Key ## Steps 1. Login to the CEE panel 2. Go to **Assess > API Triggers** 3. Copy the API key # OneSignal Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/onesignal Estimated setup time: 15 minutes ## Requirements * Your OneSignal App ID * Your Rest API Key Aampe will call the [OneSignal API](https://documentation.onesignal.com/reference/rest-api-overview) on your behalf. ## Getting Your App ID and API Key 1. Once logged in to OneSignal, select the application you want to integrate with Aampe 1 Adminmenu Pn 2. Select **Settings** from the navigation bar at the top of the screen. 2 Settings Pn 3. Select **Keys & IDs** from the sub-navigation bar that opens. 4. Copy the **OneSignal App ID** and **Rest API Key**. ### Copy Your Keys Into Aampe 1. Navigate to **System Settings > Integrations** on Aampe and select OneSignal 3 Aampe Integration Menu Pn 2. Add your credentials to the respective fields 4 Aampe Onesignal Pn # Sendgrid (Twilio) Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/sendgrid Estimated setup time: 15 minutes ## Creating an API Key 1. Navigate to **Settings** on the left navigation bar, and then select **API Keys**. 2. Click **Create API Key**. 3. Give your API key a name. For example: Aampe. 4. Select **Full Access**. For more information, see SendGrid API key permissions. 5. Click **Create & View**. 6. Copy your API key. # Salesforce Marketing Cloud (SFMC) Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/sfmc Estimated setup time: 30 minutes ## Requirements **All channels** * `client_id` * `client_secret` * Instance subdomain (e.g. `mc563885gzs27c5t9-63k636ttgm`) **Email channel (additional)** * `definition_key` — created during email setup below Aampe calls the [SFMC](https://developer.salesforce.com/docs/marketing/marketing-cloud/guide/integration-s2s-client-credentials.html) server-to-server REST API on your behalf using OAuth 2.0 client credentials. Tokens are fetched and cached automatically — you only need to supply the client ID and secret. The access token SFMC issues expires after 20 minutes. Aampe handles token refresh automatically — do not share access tokens directly. *** ## Step 1 — Create an Installed Package 1. Log in to SFMC and open **Setup** (gear icon, top right). 2. Under **Platform Tools**, expand **Apps** and select **Installed Packages**. 3. Click **New** and name the package `aampe-integration`. 4. Under **Components**, click **Add Component** and select **API Integration**. 5. Select **Server-to-Server** as the integration type. 6. Grant the following permissions: | Scope | Permission | Required for | | - | - | - | | Email | Read, Write, Send | Email sends | | Push | Read, Write, Send | Push sends | | Channels | Read, Write | Channel access | | Automations | Read, Write, Execute | Tracking Extract jobs (email only) | 7. Click **Save**. 8. Copy the **Client ID**, **Client Secret**, and **REST Base URI** from the package summary page. These are the credentials you will enter in Aampe. Your REST Base URI will be in the format `https://YOUR_SUBDOMAIN.rest.marketingcloudapis.com` — only the subdomain portion is required. *** ## Step 2 — Push channel setup No additional SFMC configuration is required beyond the Installed Package above. Enter the following into your Aampe integration settings: * `client_id` * `client_secret` * `subdomain` * `message_id` — the SFMC push message ID Aampe should send against *** ## Step 3 — Email channel setup Email requires three one-time setup steps in SFMC. ### 3a — Create the shell template in Content Builder Aampe generates the full HTML email body per user and passes it at send time. The Content Builder template acts as a thin delivery wrapper — Aampe owns all content and styling. 1. In SFMC, go to **Content Builder** and click **Create** > **Email** > **HTML Paste**. 2. Name it `aampe-email-shell`. 3. Set the subject line to: ``` %%=AttributeValue("subject")=%% ``` 4. Set the body to: ```html theme={null} %%=TreatAsContent(AttributeValue("msg_body"))=%% ``` 5. Replace the comment with your organisation's unsubscribe footer. The built-in SFMC profile centre link can be used: ``` Unsubscribe ``` 6. Save the template. `TreatAsContent()` evaluates the passed HTML as live content. Aampe automatically escapes `%%` sequences in message bodies to prevent unintended AMPscript evaluation. ### 3b — Create a send definition 1. Create a send definition referencing the shell template from step 3a. See [createSendDefinition](https://developer.salesforce.com/docs/marketing/marketing-cloud/references/mc_rest_transactional_messaging_email/createSendDefinition.html). 2. Ensure `options.tracking` has `opens` and `clicks` set to `true` — these are required for Aampe's reward signal. 3. Copy the **Definition Key** and enter it into your Aampe integration settings. One send definition is sufficient for all Aampe email sends — you do not need one per campaign or segment. ### 3c — Configure Tracking Extracts for event data Aampe reads delivery events (sent, opens, clicks, bounces, unsubscribes) via daily Automation Studio Tracking Extracts. 1. In **Automation Studio**, create a new Automation. 2. Add a **Tracking Extract** activity for each event type: Sent, Opens, Clicks, Bounces, Unsubscribes. 3. Set the output destination to S3 or SFTP. 4. Schedule the automation to run daily. The `messageKey` field in the extract output is the join key between SFMC tracking rows and Aampe send records — ensure it is included in the extract schema. See [Tracking Extract reference](https://help.salesforce.com/s/articleView?id=sf.mc_as_tracking_extract.htm\&type=5). *** **You are all set with SFMC!** # Twilio Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/twilio Estimated setup time: 15 minutes ## Requirements * Twilio Account SID * Twilio Auth Token ## Get Your Twilio Account SID & Auth Token 1. Navigate to [https://www.twilio.com/console/projects/summary](https://www.twilio.com/console/projects/summary) 2. Click the account your app is connected to. 1 App Menu Pn 3. On the account page scroll down to **Account Info** section to get your account SID and Authentication Token 2 Account Pn ## Add Twilio Account SID & Auth Token to Aampe 1. Log into the Aampe Composer. Navigate to **Integrations** and find the **Message provider** menu. Click on **Twilio**. 3 Aampe Menu Pn 2. Insert the Twilio keys into the relevant fields. 4 Aampe Twilio Pn Congrats on setting up Twilio for message delivery with Aampe. # WebEngage Source: https://kb.aampe.com/developer-guide/message-providers/message-pages/webengage Estimated setup time: 15 minutes ## Requirements * Your WebEngage License Code * Your REST API Key * Your Dashboard URL ([dashboard.webengage.com](http://dashboard.webengage.com) or [dashboard.in.webengage.com](http://dashboard.in.webengage.com)) Aampe will call the [WebEngage Transaction Campaign API](https://docs.webengage.com/docs/sending-transactional-campaigns) on your behalf. ## Getting Your License Code and API Key For the following steps, please use an admin account that has the Account Management and Update Data permissions. 1. On the WebEngage dashboard click on **Data Platform > Integrations** on the side nav. Open the **Rest API** menu. 1 Main Menu Pn 2. Copy your **LICENSE CODE** & **API KEY** 2 Apikey Pn ## Add Keys to Aampe 1. Select **WebEngage** in **System Settings > Integrations** 3 Aampe Menu Pn 2. Add your credentials to the respective fields. 4 Aampe Webengage Pn # Message Delivery Partners Source: https://kb.aampe.com/developer-guide/message-providers/message-providers Aampe supports the following message delivery partners for each of the following channels ## Channel Support Matrix | Partner | Push | SMS | Email | Notes | | :- | :- | :- | :- | :- | | [**Adobe Campaign Standard**](/developer-guide/message-providers/message-pages/adobe-campaign-standard) | ✓ | ✓ | ✓ | Full marketing automation platform with all channels | | [**Adobe Campaign Classic**](/developer-guide/message-providers/message-pages/adobe-campaign-classic) | ✓ | ✓ | ✓ | Full marketing automation platform with all channels | | [**Airship**](/developer-guide/message-providers/message-pages/airship) | ✓ | ✓ | ✓ | Full omnichannel platform with push, SMS, and email capabilities | | [**Apple Push Notification service**](/developer-guide/message-providers/message-pages/apns) | ✓ | - | - | Apple Push Notification service - iOS push notifications only | | [**AWS SNS**](/developer-guide/message-providers/message-pages/aws-sns) | ✓ | ✓ | ✓ | Amazon Simple Notification Service supports all three channels | | [**Braze**](/developer-guide/message-providers/message-pages/braze) | ✓ | ✓ | ✓ | Customer engagement platform with push, SMS, email, and in-app | | [**Clevertap**](/developer-guide/message-providers/message-pages/clevertap) | ✓ | ✓ | ✓ | Full-stack engagement platform supporting all channels | | [**Customer.io**](/developer-guide/message-providers/message-pages/customerio) | ✓ | ✓ | ✓ | Multi-channel automation supporting push, SMS, email, and in-app | | [**Emarsys**](/developer-guide/message-providers/message-pages/emarsys) | ✓ | ✓ | ✓ | Omnichannel customer engagement platform | | [**Firebase Cloud Messaging**](/developer-guide/message-providers/message-pages/firebase-cloud) | ✓ | - | - | Google's push notification service for Android, iOS, and web | | [**Iterable**](/developer-guide/message-providers/message-pages/iterable) | ✓ | ✓ | ✓ | Cross-channel platform with push, SMS, email, and web push | | [**Leanplum**](/developer-guide/message-providers/message-pages/leanplum) | ✓ | ✓ | ✓ | Mobile engagement platform with multi-channel messaging | | [**Mailchimp**](/developer-guide/message-providers/message-pages/mailchimp) | - | ✓ | ✓ | Primarily email marketing with SMS capabilities | | [**MoEngage**](/developer-guide/message-providers/message-pages/moengage) | ✓ | ✓ | ✓ | Customer engagement platform with all channels | | [**NetCore**](/developer-guide/message-providers/message-pages/netcore) | ✓ | ✓ | ✓ | Full-stack martech platform | | [**OneSignal**](/developer-guide/message-providers/message-pages/onesignal) | ✓ | ✓ | ✓ | Push notification service expanded to SMS and email | | [**Salesforce Marketing Cloud (SFMC)**](/developer-guide/message-providers/message-pages/sfmc) | ✓ | ✓ | ✓ | Enterprise platform with Mobile Studio, Email Studio, and SMS | | [**Twilio**](/developer-guide/message-providers/message-pages/twilio) | - | ✓ | ✓\*\* | SMS primary focus, email via SendGrid acquisition | | [**WebEngage**](/developer-guide/message-providers/message-pages/webengage) | ✓ | ✓ | ✓ | Full-stack retention platform | | [**Custom API**](/developer-guide/message-providers/message-pages/custom-api) | \* | \* | \* | Depends on implementation - can support any/all channels | ### Legend * **✓** : Service is provided * **-** : Service is not provided * **\*** : Varies based on implementation * **\*\*** : Email through SendGrid (Twilio-owned) # Setting up Single Sign-On (SSO) Source: https://kb.aampe.com/developer-guide/sso/setting-up-sso Aampe supports the following providers for both authentication (SAML) and user provisioning (SCIM): * [Google](/developer-guide/sso/sso_google) * [JumpCloud](/developer-guide/sso/sso_jumpcloud) * [Microsoft Entra ID (formerly Azure AD)](/developer-guide/sso/sso_microsoft_entra_id) * [Okta](/developer-guide/sso/sso_okta) * [OneLogin](/developer-guide/sso/sso_onelogin) Additional authentication-only options include: * [Auth0](/developer-guide/sso/sso_auth0) * [OpenID Connect Provider](/developer-guide/sso/sso_openid_connect) * [PingOne](/developer-guide/sso/sso_pingone) * [Rippling](/developer-guide/sso/sso_rippling) For additional support in setting up or configuring SSO, please contact Aampe Customer Support. # Auth0 SAML SSO Source: https://kb.aampe.com/developer-guide/sso/sso_auth0 Here we will show you how to set up your own custom SAML application for Auth0 SAML ## Create application 1. From your Auth0 dashboard, click **Applications** from the left navigation menu. 2. If your application is already created, choose it from the list and move to the section [Configure Application](#configure-application) Sso Auth0 1 Pn 3. If you haven't created a SAML application, click the **Create Application** button to create a new application. 4. Give your application a **Name** and click **Create**. Sso Auth0 2 Pn ## Configure application 1. Click the tab **Addons** and enable **SAML2 WEB APP** add-on. Sso Auth0 3 Pn 2. Enter your **Application Callback URL** on the next screen and click **Enable**. Replace the values with the ones you have received from Aampe. Sso Auth0 4 Pn 3. Click the tab **Usage** and download the **Identity Provider Metadata**. Sso Auth0 5 Pn ## Attribute mapping No additional attribute mapping is required for Auth0 SAML to work with Aampe. ## Next steps You've successfully configured your custom SAML application for Auth0 SAML. At this stage, you can assign users to your application and start using it. # Google SAML SSO Source: https://kb.aampe.com/developer-guide/sso/sso_google In this section, we will show you how to set up your own custom SAML application for Google SAML ## Create application 1. From your Google Admin console, click **Apps** from the sidebar then click **Web and mobile apps** from the list. 2. If your application is already created, choose it from the list and move to the section [Configure Application](#configure-application) 3. If you haven't created a SAML application, click **Add custom SAML app** from the menu. Sso Google 1 Pn 4. Give your application an **App name** and click **Continue**. Sso Google 2 Pn ## Configure application 1. From the next screen, click **DOWNLOAD METADATA** to download the metadata XML file, then click **Continue**. Sso Google 3 Pn 2. Enter the following values in the **Service provider details** section: * **ACS URL** * **Entity ID** Replace the values with the ones you have received from Aampe and click **Continue** to save the configuration. Sso Google 4 Pn ## Attribute mapping 1. Under the **Attributes** section, you have to configure the following attributes: | App attributes | Google directory attributes | | :- | :- | | Primary email | | | `firstName` | `First name` | | `lastName` | `Last name` | See the screenshot below. 2. After you have configured the attributes, click **Finish** to save the configuration. Sso Google 5 Pn 3. From the next screen, click **User access** to configure the application to allow users to log in. Sso Google 6 Pn 4. Check the **ON for everyone** checkbox and click **Save**. Sso Google 7 Pn ## Next steps You've successfully configured your custom SAML application for Google SAML. At this stage, you can assign users to your application and start using it. # JumpCloud SAML SSO Source: https://kb.aampe.com/developer-guide/sso/sso_jumpcloud Here we will show you how to set up your own custom SAML application for JumpCloud SAML ## Create application 1. From your JumpCloud Admin console, click **SSO** from the left navigation menu. 2. If your application is already created, choose it from the list and move to the section [Configure Application](#configure-application) 3. If you haven't created a SAML application, click **plus icon** and then **Custom SAML App** to create a new application Sso Jumpcloud 1 Pn 4. Give your application a **Display Label**. Sso Jumpcloud 2 Pn ## Configure application 1. Next click on the **SSO** tab and enter the following values: * **IdP Entity ID** * **SP Entity ID** * **ACS URL** Replace the values with the ones you have received from Aampe. Sso Jumpcloud 3 Pn ## Attribute mapping Under the **Attributes** section, you have to configure the following attributes: See the screenshot below. | Service Provider Attribute Name | JumpCloud Attribute Name | | :- | :- | | `firstName` | `firstname` | | `lastName` | `lastname` | Make sure you have checked the **Declare Redirect Endpoint** checkbox. Finally, click **Activate** to save the application configuration. Sso Jumpcloud 4 Pn Now go back to the SAML app you just created, click the tab **SSO**, and click the button **Export Metadata** to download the metadata XML file. Sso Jumpcloud 5 Pn ## Next steps You've successfully configured your custom SAML application for JumpCloud. At this stage, you can assign users to your application and start using it. # Microsoft Entra ID (formerly Azure AD) SAML SSO Source: https://kb.aampe.com/developer-guide/sso/sso_microsoft_entra_id Here we show you how to set up your own custom SAML application for Microsoft Entra ID SAML ## Create application From your Azure Admin console, click **Enterprise applications** from the left navigation menu. Sso Microsoft Entraid1 Pn If your application is already created, choose it from the list and move to the section [Configure Application](#configure-application). If you haven't created a SAML application, click **New application** from the top to create a new application. Sso Microsoft Entraid2 Pn From the next screen, click **Create your own application**. Give your application a **Name** and click **Create**. Sso 3 Pn ## Configure application Select **Single Sign On** from the **Manage** section of your app and then **SAML**. Sso 4 Pn Click **Edit** on the **Basic SAML Configuration** section. Sso Microsoft Etraid5 Pn Enter the following values in the **Basic SAML Configuration** section on the next screen: * **Identifier (Entity ID)** * **Reply URL (Assertion Consumer Service URL)** Replace the values with the ones you have received from Aampe. Click **Save** to save your changes. Sso 6 Pn ## Attribute mapping Click **Edit** on the **Attributes & Claims** section. Sso 7 Pn You have to configure the following attributes under the **Attributes & Claims** section: | Name | Value | | :- | :- | | `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress` | `user.mail` | | `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname` | `user.givenname` | | `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name` | `user.userprincipalname` | | `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname` | `user.surname` | See the screenshot below. Sso 8 Pn Go to the **SAML Signing Certificate** section and download the **Federation Metadata XML**. Sso 9 Pn ## Next steps You've successfully configured your custom SAML application for Microsoft Entra ID SAML. At this stage, you can assign users to your application and start using it. # Okta SAML SSO Source: https://kb.aampe.com/developer-guide/sso/sso_okta Here we will show you how to set up your own custom SAML application for Okta SAML ## Create application 1. From your Okta account, click **Applications** from the left navigation menu. 2. If your application is already created, choose it from the list and move to the section [Configure Application](#configure-application) 3. If you haven't created a SAML application, click the **Create App Integration** button to create a new application. Sso Okta 1 Pn 4. Choose **SAML 2.0** from the next screen and click **Next**. Sso Okta 2 Pn 5. Give your application an **App Name** and click **Next**. Sso Okta 3 Pn ## Configure application 1. Enter the following values in the **SAML Settings** section on the next screen: * **Single sign on URL** * **Audience URI (SP Entity ID)** * Select **EmailAddress** from the **Name ID format** dropdown. Replace the values with the ones you have received from Aampe. Sso Okta 4 Pn ## Attribute mapping 1. Under the **Attribute Statements** section, you have to configure the following attributes: | Name | Value | | :- | :- | | `id` | `user.id` | | `user.email` | | | `firstName` | `user.firstName` | | `lastName` | `user.lastName` | See the screenshot below. Sso Okta 5 Pn 2. On the next screen select **I'm an Okta customer adding an internal app** and click **Finish**. Sso Okta 6 Pn 3. From your application, click **Sign On** tab and go to the section **SAML Signing Certificates** 4. Click the **Actions** dropdown for the correct certificate and click **View IdP metadata**. A separate window will open with the metadata XML file, you can copy it to your clipboard. Sso Okta 7 Pn ## Next steps You've successfully configured your custom SAML application for Okta SAML. At this stage, you can assign users to your application and start using it. # OneLogin SAML SSO Source: https://kb.aampe.com/developer-guide/sso/sso_onelogin Here we will show you how to set up your own custom SAML application for OneLogin SAML ## Create application 1. From your OneLogin account, click **Applications** from the top navigation menu. 2. If your application is already created, choose it from the list and move to the section [Configure Application](#configure-application) 3. If you haven't created a SAML application, click the **Add App** button to create a new application. Sso Onelogin 0 Pn 4. Search for **SAML Test Connector** in the Find Applications section. Select **SAML Custom Connector (Advanced)** from the search results. Sso Onelogin 1 Pn 5. Give your application a **Display Name** and click **Save**. Sso Onelogin 2 Pn ## Configure application 1. From your application, click the **Configuration** tab on the left to configure the application. 2. You have to enter the following values in the **Application details** section: * **Audience (Entity ID)** * **ACS (Consumer) URL Validator** * **ACS (Consumer) URL** * Select the **Service Provider** from the SAML initiator dropdown. Sso Onelogin 4 Pn Replace the values with the ones you have received from Aampe. 3. Click **Save** to save the configuration. Sso Onelogin 3 Pn 4. Click the dropdown menu **More Actions** from the top right corner and click **SAML Metadata** to download the metadata XML file. Sso Onelogin 5 Pn ## Attribute mapping 1. From your application, click the **Parameters** tab on the left. 2. You have to configure the following attributes: | SAML Custom Connector Field | Value | | :- | :- | | `id` | `UUID` | | `firstName` | `First Name` | | `lastName` | `Last Name` | See the screenshot below. Sso Onelogin 6 Pn See the below screenshot to see how to map the id attribute to UUID. 3. Enter `id` in the **Field name** input and check the **Include in SAML assertion** checkbox. Click **Save** to continue. Sso Onelogin 7 Pn 4. On the next screen, select **UUID** from the **Value** dropdown and click **Save**. Sso Onelogin 8 Pn 5. Do the same for the other attributes (email, firstName, lastName). ## Next steps You've successfully configured your custom SAML application for OneLogin SAML. At this stage, you can assign users to your application and start using it. # Open ID Connect (OIDC) Source: https://kb.aampe.com/developer-guide/sso/sso_openid_connect Here we outline how to set up SSO via OIDC To allow apps or clients to login using an OIDC Identity Provider, we must register an application with the IdP. The process might slightly vary from one IdP to another (refer to your IdP documentation), but there are a few things in common. * **Callback URL**: This will be the URL where Aampe receives the OIDC Authorization response. Make sure to set the full HTTPS URL obtained by concatenating Aampe endpoint with [oidcPath](https://www.ory.sh/docs/polis/deploy/env-variables#oidcpath) env. The default value set for `oidcPath` in Aampe service is `/api/oauth/oidc`. * **Client/Application ID**: The OIDC Identity Provider normally will generate a unique identifier for the registered App. Make a note of this to be used later when creating the SSO connection with Aampe. * **Client Secret**: Along with the Client ID, the IdP also generates a client secret which is used to authenticate the client while issuing tokens. Make a note of this to be used later when creating the SSO connection with Aampe. Since Aampe acts as a proxy between the app and the OIDC Identity Provider, here the application/client is Aampe. # PingOne SAML SSO Source: https://kb.aampe.com/developer-guide/sso/sso_pingone Here we will show you how to set up your own custom SAML application for PingOne SAML ## Create application 1. From your PingOne account, click **Connections > Applications** from left navigation menu. 2. If your application is already created, choose it from the list and move to the section [Configure Application](#configure-application) 3. If you haven't created a SAML application, click **plus button** to create a new application. Sso Pingone 1 Pn 4. Give your application an **Application Name**, choose **SAML Application** from the **Application Type** and click **Configure**. ## Configure application Sso Pingone 2 Pn 1. From the next screen, you have to enter the following values in the **SAML Configuration** section: * **ACS URLs** * **Entity ID** Replace the values with the ones you have received from Aampe. 2. Click **Save** to save the configuration. Sso Pingone 3 Pn 3. Click the **Configuration** tab from the top and click **Download Metadata** to download the metadata XML file. Sso Pingone 4 Pn ## Attribute mapping 1. Click the **Attribute Mappings** tab from the top and you have to configure the following attributes: | SAML App | PingOne | | :- | :- | | `id` | `User ID` | | `Email Address` | | | `firstName` | `Given Name` | | `lastName` | `Family Name` | See the screenshot below. Sso Pingone 5 Pn 2. Make sure you have enabled your app so that it can be used by the users. You can do this by clicking the **Toggle button** next to your app. Sso Pingone 6 Pn ## Next steps You've successfully configured your custom SAML application for PingOne SAML. At this stage, you can assign users to your application and start using it. # Rippling SAML SSO Source: https://kb.aampe.com/developer-guide/sso/sso_rippling Here we will show you how to set up your own custom SAML application for Rippling SAML ## Create application 1. You'll need to create a new Custom App from your Rippling account if the app is not already in the Rippling app directory. 2. Go to **IT Management > Custom App** from the left navigation menu. Sso Rippling 1 Pn 3. Click **Create New App** button to create a new application. 4. From the next screen, fill in the following fields: * **App Name** * **Select Categories** * **Upload Logo** * **What type of app would you like to create?** - Make sure you select **Single Sign-On (SAML)** from the list. Sso Rippling 2 Pn ## Configure application 1. Copy the **IdP Metadata URL** or **IdP Metadata XML** from the next screen. You'll need this value to configure your SAML connection. Sso Rippling 3 Pn 2. You'll need to enter the **ACS URL** and **Entity ID**. You'll get these values from Aampe. Sso Rippling 4 Pn 3. Make sure that **Service Provider Entity ID** matches the **SAML Audience (SP Entity ID)** value from Aampe. 4. From the **Settings** tab of custom app you just created. Go to **Advanced SAML Settings** section and check the box for **Disable InResponseTo field in assertions for IdP initiated SSO**. This is because Rippling sends dummy values for the InResponseTo field and Aampe will try to validate the InResponseTo field if it is present. Sso Rippling 5 Pn ## Next steps You've successfully configured your custom SAML application for Rippling SAML. At this stage, you can assign users to your application and start using it. # Deploy a Surface into an App or Website Source: https://kb.aampe.com/developer-guide/surfaces/surfaces-embed To go live with Surfaces, just call the Surfaces API from within your application. ## How Do I Deploy Surfaces in My App or Website? There are two common ways that Aampe customers deploy Surfaces * Direct Integration * Embedded into third-party experiences (e.g. popups via Braze) ### Direct Integration Suppose you have the following text and image elements at the top of your layout. Surfaces Demo Fitness1 Pn \ The subtitle was the winner from an A/B test last year, but you also know that some customers identify with other aspects of your business. You would love to give each customer the words that inspire them to use your product. You have already set up the Surface in Aampe, along with several subtitles that speak to various reasons users have for using your product (click [here](/user-guide/using-aampe/product-surfaces/surfaces-composer) to revisit how to set up a Surface in Aampe). \ \ To insert Aampe-generated subtitles when a page loads, make use of code similar to the following. ```javascript theme={null} // remember this is for logged-in experiences and we know the user const contact_id = 'user_12345'; const surface_id = 'eb1efacd-cccf-492d-94ae-4fc68515cdee'; const defaultValues = { subtitle: 'Welcome back!' }; // call the Aampe API to get subtitle (or any field in the Surface) fetch(`https://asia-southeast1.content.api.aampe.com/v1/surface/content?surface_id=${surface_id}&contact_id=${contact_id}`, { headers: { 'Authorization': `X-API-KEY ${}` } }) .then(res => res.json()) .then(data => { const alt = data?.[0]?.alternates?.[0] || {}; const subtitle = alt.subtitle || defaultValues.subtitle; // Set page elements to values provided by Aampe document.getElementById('top-subtitle').innerText = subtitle; }) .catch(() => { // Fallback in case of error document.getElementById('top-subtitle').innerText = defaultValues.subtitle; }); ``` You’ll need three things to call your Surfaces API: * proper authorization - an API key * `surface_id`- available within the Aampe composer for each surface * `contact_id` - the unique person identifier used by Aampe In this example, the API results will populate the subtitle. You can follow the same pattern to personalize other elements such as colors, image URLs, or layout. ### **Third-Party Integration** Suppose you wanted to populate the same subtitle, but instead of the landing page we're updating an in-app popup. You follow the same steps of (1) calling the Aampe API and (2) returning the parsed results to whatever tool renders the screen. ```html theme={null} {{connected_content method: "GET", url: "https://api.aampe.com/v1/surface_content", headers: { "Authorization": "Bearer your_api_key_here" }, params: { "user_id": "{{${user_id}}}", "surface_id": "eb1efacd-cccf-492d-94ae-4fc68515cdee" }, fallback: "Smarter workouts, better results" }} {{#if content.[0].subtitle}} {{content.[0].subtitle}} {{else}} "Smarter workouts, better results" {{/if}} ``` ## API Documentation See [here](https://docs.aampe.com/reference/get_surface_content_v1_surface_content_get) for the full Surfaces API documentation. # Surface Integration Details Source: https://kb.aampe.com/developer-guide/surfaces/surfaces-embed-advanced Multi-region deployments, fallbacks, latency, and more ## Surfaces API Overview The Surfaces API is a low-latency solution for retrieving personalized content to display in your application. This is typically an app or website, but could be any application. \ \ When you make a request to the Surfaces API for one of your users, the API will respond with the recommended content to display to that user for each specified surface. * If you'd like a refresher on the Surfaces product, [read our User Guide overview](/user-guide/using-aampe/quickstart/quickstart-surfaces). * For instructions on calling the Surfaces API, [see our API documentation](https://kb.aampe.com/api-reference/surface). ## App Integration The Surfaces API integrates into your application in one of two ways: * Real time * Pre-fetched and cached There are some tradeoffs to consider when deciding how to integrate. ### Real Time * **How it works:** your application calls the Surfaces API whenever a page loads with Aampe-generated content Realtime Flow Diagram Pn * **Advantages:** simple to implement, and you only make calls only when needed * **Disadvantages:** potentially higher latency from calling out to an external service A common technique used when implementing this approach is to display a spinner or a [skeleton screen](https://www.nngroup.com/articles/skeleton-screens/) while the data is loading. ### Periodically Cached * **How it works:** your application calls the Surfaces API and caches the results, then pulls content from the cache whenever a page loads with Aampe-generated content Prefetch Flow Diagram Pn * **Advantages:** no latency risk, can implement retries and other backups * **Disadvantages:** some extra work to maintain a fresh cache of content ### Real Time vs Cached | | Cached | Real Time | | :- | :- | :- | | Call Type | Background call, on app open | Blocking call, on component rendering | | Batching | Get content for several surfaces at once | One or more surfaces at a time | | Timeout | Longer timeout options | Shorter timeout | | Retrying | Time to retry in case of network issues | Not recommended | | Fallback | Static content in case of failed retries, timeouts, or API returning 404 | Static content in case of failed retries, timeouts, or API returning 404 | | Control | App controls if, what, when to render | App or Aampe can control if, what, when to render | | Tracking | App is responsible for propagating a unique id to tracking event metadata | Aampe can approximate tracking without extra instrumentation | ## Surface API Latency ### Latency SLA The Surfaces API comes with a Service Level Agreement of P95 round trip latency of \<=150ms for requests originating from within the same region as the API. ### Reducing Latency We recommend the following steps to reduce latency within your app when interacting with the Surfaces API * Pre-fetch surface content, when possible * Include multiple surface IDs when calling the Surface API * Enforce strict timeouts such that the page will load with default content in the rare cases where the API takes longer than expected to respond * Call the Surfaces API from within the same geographic region as the service calling the API. ### Multi Region Deployments To minimize latency, we recommend locating your Surfaces API deployment in the same geographic region as the service from which you will be calling the API. For instance, if your application backend runs in the `us-east1` region, we can locate your Surfaces API deployment within the same region. Differences between cloud provider regions may impact our ability to directly match your region, but we will locate it as close as possible. When calling the Surfaces API, you will include the region identifier in the URL. For example, if you are using the `asia-southeast1` region, you will make calls to `https://content.asia-southeast1.api.aampe.com`. Work with your customer success manager to ensure your Surfaces API deployment is located in the correct region. ### Fallback Content In cases where the Surfaces API does not have any content to show a user for a specific Surface (e.g. if the user isn't eligible for any of the content defined on that Surface), it's important to define fallback content, so you still have something to show the user. Fallback content should be defined within your application. If no content is available for a given `contact_id` and `surface_id`, the API will return a 404 response. ## Configuration Options The Surfaces API response contains a `control` key which includes values you can use to govern how frequently specific Surface content is displayed in your application. ```json theme={null} "control": { "min_gap_days": 3, "max_displays_per_day": 1, "display_every_x_times": 5, } ``` * `min_gap_days:`minimum number of days between surface content views * `max_displays_per_day:` maximum number of personalized surface displays per day * `display_every_x_times:`a rule to show personalized surface content every X page loads (otherwise, the surface loads with fallback content). # Frequently Asked Questions (FAQs) Source: https://kb.aampe.com/help/faqs How many messages is enough? Can I share content across channels? What data should we send? How often? In what format? Batch or streaming? Do label weights change agent behavior permanently? Is +50 enough? What data can Aampe share back? How often does the data update? How does the plugin work? Do I need a special Figma license? Does Aampe need PII? What does Aampe do to stay compliant with privacy laws? What is an agent's reward function? How can we help agents learn more quickly? Can I get an LLM to write my content for me? Is an Aampe agent an LLM? What frameworks are compatible with Aampe? What fallbacks exist? Can I run an AB test using Aampe? How do I create a randomized holdout group? # Aampe Glossary Source: https://kb.aampe.com/help/glossary Lots of funny new words when working with agentic infrastructure **Agent**\ An AI entity assigned to each user, learning preferences through interactions and optimizing messaging in real time. **Agentic Infrastructure**\ The system of agents, content, and labels that enables true 1:1 personalization at scale. **Agentic Learning**\ Continuous adaptation through trial, feedback, and reinforcement, balancing exploration and exploitation. **Alternates**\ Variant options for a single message component (e.g., three different CTAs) that agents mix and match. **Components**\ Building blocks of a message (Greeting, Value Proposition, Offering, Incentive, CTA, Tone, Other). **Content as Infrastructure**\ Approach where messages are not campaigns but a reusable, labeled library that fuels learning. **Content Coverage Map**\ A structured inventory mapping product features and workflows to Topics, showing where content exists and where gaps remain. **Contextual Bandit**\ A machine learning framework where agents select actions (e.g., messages) based on user context, balancing personalization with learning. **Evergreen Content**\ Always-on messages (e.g., feature education, FAQs) that form the bulk of agent learning. **Exploration vs. Exploitation**\ The balance agents strike between testing new content and using proven, high-performing variants. **Inner Voice Statement**\ User-centered phrasing of a value proposition (e.g., “I want to feel in control of my finances”). **Labels**\ Semantic tags (e.g., “Convenience,” “Urgency”) applied to alternates, enabling agents to reason and compare systematically. **Message Group**\ A set of related messages made by combining alternates and labels, tested together for personalization. **Offering**\ The specific product, feature, or benefit highlighted in a message. **Reinforcement Learning (RL)**\ Learning approach where agents adjust strategies based on observed user behavior and outcomes. **Reward Function**\ The feedback mechanism agents use to determine whether a message or action led to a desirable outcome. **Surfaces**\ Channels or placements where messages appear (push, email, SMS, in-app, web). **TACIR (Target Agent-Customer Interaction Rate)**\ The ideal frequency of interactions per week between agents and users, used as a diagnostic for content sufficiency. **Thompson Sampling**\ A probabilistic method agents use to select actions, balancing exploration of new options with exploitation of known successes. **Three-Fold Agentic Content Strategy**\ Framework dividing content into Evergreen (60%), Triggered (30%), and Tactical (10%) categories. **Topic**\ A high-level communication category (e.g., “Welcome,” “Churn Rescue”) that bundles offerings, audiences, and contexts. **Triggered Content**\ Event-based messages delivered in response to user actions (e.g., cart abandonment, subscription pause). **Value Proposition**\ The core reason users should care about an offering, often tied to human values (e.g., Control, Trust). # Aampe Documentation Source: https://kb.aampe.com/index Explore our knowledge base to learn how to set up, use, and manage Aampe successfully Aampe like a pro! Quickstart guides for new users. Advanced topics for the Aampe pros. You'll have Aampe set up in no time! SSO, data management, security FAQs, and more. Detailed API documentation for data ingestion, surfaces, and more. Not sure where to go? We'll help you find your way. # Aampe App Library Source: https://kb.aampe.com/tools/aampe-app-library The place to try out new Aampe features before they are generally ## What is the Aampe App Library? We have a lot of grand ideas at Aampe. We can't build them all at once in the Aampe composer, but we can stand up prototypes and gather some early feedback. The tools you'll find at [apps.aampe.com ](https://apps.aampe.com/)make use of externally available Aampe APIs, but are otherwise standalone apps. If you wanted, you could build these kinds of apps yourself. ## What kind of apps are available? Check back often, as we are adding (and removing) apps frequently based on user feedback. The following apps are currently available at [apps.aampe.com](https://apps.aampe.com/): ### **Aampe Email Variable Config** Paste your email HTML and Aamplify it by carving out the fields required to add agentic copy. * Upload your HTML * Highlight the elements you want to personalize * Download the results or copy/paste back into the Aampe composer Aampe Email Template Config ### **Aampe Surface Creator** Connect your Aampe account to Figma to quickly turn your creative assets into Aampe-ready content Read more about the [Aampe Surface Creator](/tools/figma-plugin). ### **Surface Template Builder** Define a new Surface templates and test the JSON with a live API. This is great for developers who want to experiment with a live Surfaces API. * Give your new surface a name and description * Add elements to the surface template * Test the Surfaces API and examine the results Surface Template Builder ### **Relay** Harness the power of LLMs to generate content at scale while preserving all the conventions required for labels, learning, and clean measurement. * Upload API keys for Aampe and your LLM of choice * Fill out two forms * One is about your company: What is your brand voice? What does your company do? What components and labels do you want to use? * The next is about your content: What kind of messages do you want to make? How do you want to frame your offerings and calls to action? * Preview the results and send the content director to the Aampe composer Relay # AamPT Source: https://kb.aampe.com/tools/aampt Your always-ready Aampe LLM ## Introducing AamPT