# Authentication Source: https://docs.animo.co/authentication # Authentication The Animo API uses **Laravel Passport** (OAuth2) with bearer tokens. Every request to `/api/v1/*` must include a valid access token. ## Sending the token Include the token in the `Authorization` header: ```http theme={null} GET /api/v1/user HTTP/1.1 Host: {your-domain} Authorization: Bearer {access_token} Accept: application/json ``` ## Obtaining a token ### Personal access token (recommended for integrations) 1. Sign in to Animo as the user whose data the integration will access. 2. Open the admin panel and navigate to user API token settings. 3. Create a new personal access token and select the required scopes. 4. Copy the token immediately — it is shown only once. Personal access tokens expire after **one year** (`Passport::personalAccessTokensExpireIn`). ### OAuth2 authorization code flow For third-party apps that act on behalf of users: 1. Register an OAuth client in Passport. 2. Redirect the user to authorize: ```http theme={null} GET /api/oauth/authorize ``` 3. Exchange the authorization code for an access token: ```http theme={null} POST /api/oauth/token Content-Type: application/json { "grant_type": "authorization_code", "client_id": "{client_id}", "client_secret": "{client_secret}", "redirect_uri": "{redirect_uri}", "code": "{authorization_code}" } ``` OAuth routes are mounted at `/api/oauth`. ### Token refresh Authenticated users can refresh tokens via: ```http theme={null} POST /api/oauth/token/refresh ``` (Requires an active web session.) ## Default scopes When creating a token without explicitly selecting scopes, these are granted by default: | Scope | Description | | ---------------- | ----------------------------------- | | `user:read` | Retrieve the user info | | `companies:read` | Get the companies available to user | ## All scopes | Scope | Description | | ----------------------- | ----------------------------------------------------------------------------------- | | `user:read` | Retrieve the user info | | `companies:read` | Get the companies available to user | | `activations:read` | Access activations and the submissions and leads through them | | `activations:subscribe` | Subscribe to activation events (new submissions) | | `events:read` | Access events from the companies of the user | | `events:create` | Create new events for the companies of the user | | `events:update` | Update events belonging to the companies of the user | | `events:delete` | Delete events belonging to the companies of the user | | `forms:read` | Access forms from the companies of the user | | `forms:create` | Create form on any of the user's companies | | `forms:update` | Modify forms on any of the user's companies | | `forms:delete` | Delete any of the forms available on the the user's companies | | `leads:read` | Access leads from the companies of the user | | `leads:subscribe` | Subscribe to lead events (creation, update, deletion) | | `meetings:read` | Access booked meetings and the submissions and leads through them | | `meetings:subscribe` | Subscribe to meeting events (new bookings, cancellations, no-show, ratings...) | | `orders:read` | Access the list of orders for any of the events organized by the user's companies | | `orders:cancel` | Allow to cancel free orders for any of the events organized by the user's companies | | `orders:curate` | Approve or deny any pending orders from the users events | | `orders:create` | Register tickets on the name of your attendees from the API. | | `submissions:read` | Access submissions from leads belonging to companies of the user | | `submissions:subscribe` | Subscribe to submission events (new submissions) | | `ticket-types:read` | Access ticket types from the events of the user | | `ticket-types:create` | Create ticket types on any of the user's events | | `ticket-types:update` | Modify ticket types on any of the user's events | | `ticket-types:delete` | Delete ticket types available on the the user's events | > **Note:** Scopes `leads:read`, `submissions:read`, and `meetings:read` are registered but have no matching `GET` endpoints yet. ## Plan requirement API access requires an active **Pro** plan on each company you integrate with. Manage billing at **[https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing)**. | Internal gate | Animo plan | Grants | | ------------------ | ---------- | --------------------------------- | | `USE_API` | **Pro** | All company-scoped REST endpoints | | `USE_INTEGRATIONS` | **Pro** | Webhook attach/update/detach | Without Pro: * `GET /api/v1/companies` returns `200` with an **empty** `data` array (easy to misread as "no companies") * Direct calls to `/api/v1/{company}/…` return `403` ## Scope requirements per route group Routes enforce scopes via middleware. A token must include the scope listed on each endpoint. Company-scoped routes additionally require the Pro plan (`USE_API` gate). Webhook endpoints also require the Integrations feature (`USE_INTEGRATIONS` gate). ## Error responses | Status | Meaning | | ------------------ | --------------------------------------------------------------------------- | | `401 Unauthorized` | Missing, expired, or invalid token | | `403 Forbidden` | Valid token but missing required scope, subscription gate, or policy denial | ## Related * [Conventions](conventions.md) * [API index](index.md) # Business case Source: https://docs.animo.co/business-case # Animo + API vs. building it yourself **For event organisers and businesses that use events in their marketing mix** *** ## The question You run events — launches, demos, conferences, webinars, partner days. You need registrations, payments, confirmations, and attendee data. Which path fits? | Path | Summary | | ------------------------------------ | -------------------------------------------------------------- | | **A — Fully managed** | API sets up and operates; Animo hosts everything attendees see | | **B — Custom site + Animo checkout** | Your branded site; tickets and forms link/embed to Animo | | **C — Build your own** | Rebuild event commerce with AI (not recommended for marketers) | **Pro plan: €99/month** — API, webhooks, hosted pages, payments, emails, attendee CRM. *** ## How to read the cost estimates All figures are **order-of-magnitude** guides, not quotes. | Line item | What it means | | --------------------- | -------------------------------------------------------------------------------- | | **Platform** | Animo Pro subscription (€99/mo → **€1,188/yr**) | | **Integration labor** | **Human hours × hourly rate** — setup, build, QA (assumed **€80–100/hr** loaded) | | **AI API spend** | Token/subscription cost for coding agents — **separate from labor** | | **Maintenance** | Ongoing human time over the year | | **Infra** | Hosting for a custom site or webhook receiver (Path B only) | Labor and AI spend are listed separately because on Paths A and B, **people cost dominates**; token spend is typically hundreds of euros, not thousands. *** ## What Animo already is Animo is a full events platform — not a thin API wrapper. | Included in Pro | You never rebuild | | ------------------------------- | ----------------------------- | | Hosted event & ticket pages | Checkout & Mollie payments | | Paid + free tickets, approvals | VAT, invoicing, platform fees | | Registration & activation forms | Lifecycle emails | | Attendee CRM, QR check-in | Lead/submission storage | | Webhooks | Ticket issuance & scanning | With the API, the rule is **orchestrate and present** — not reimplement money, tax, or data-of-record flows. See [Animo for AI agents](for-ai-agents.md). *** ## Path A — Animo Pro + API, fully managed **Best for:** fast turnaround, small or no dev team, private/internal events, “just get it live.” **What you build:** almost nothing attendees see. API calls to create events, ticket types, forms, email copy, event pages. Attendees go to `https://app.animo.co/{company-slug}/event/{event-slug}`. | | Typical range | | ---------------------------- | --------------------------------------------------------------------------------------------------- | | **Time to first event live** | **1–3 days** | | **Human hours (one-time)** | **4–16 hours** | | | API token, Pro plan, Mollie connect, create one event via API or agent, smoke-test free + paid flow | | **Integration labor** | **€500–€2,500** (hours × rate above) | | **Code you own** | **0–500 LOC** (optional setup scripts; no public site) | | **AI API spend (one-time)** | **€200–€500** (\~50k–150k tokens) | | **Ongoing maintenance** | **1–2 hrs/month** | | **Year 1 total** | **\~€2k–€4k** | **Example jobs:** Provision event from a brief · Operate (approve pending, shift dates) · Optional webhook to Slack. Maps to **Mode 1** in [for-ai-agents.md](for-ai-agents.md). *** ## Path B — Animo Pro + API, custom site + Animo checkout **Best for:** marketing-led businesses with brand standards — bespoke landing page, own domain, still correct payments and registrations. **What you build:** presentation layer only. Marketing site (schedule, speakers, brand). Every **money** and **data-of-record** path links or embeds to Animo — ticket signup URLs, activation forms, checkout redirect for paid orders. | | Typical range | | ---------------------------- | -------------------------------------------------------------------------------------------------------- | | **Time to first event live** | **2–6 weeks** | | **Human hours (one-time)** | **40–120 hours** | | | Design/dev site, `GET` sync from API, webhook receiver with persistence, CRM mapping, error handling, QA | | **Integration labor** | **€4k–€12k** (hours × rate above) | | **Code you own** | **1,000–3,000 LOC** (site + webhook app + optional sync jobs) | | **AI API spend (one-time)** | **€500–€2k** (\~200k–800k tokens) | | **Infra (year 1)** | **€60–€600** (hosting for site + webhook receiver) | | **Ongoing maintenance** | **2–6 hrs/month** | | **Year 1 total** | **\~€6k–€18k** | **What you still don’t build:** cart, Mollie UI, VAT, invoices, custom forms that POST into Animo, parallel lifecycle emails. **Example jobs:** Branded launch page pulling ticket types from API · Sponsorship form embedded via activation URL · HubSpot/Slack via webhooks. Maps to **Mode 2** in [for-ai-agents.md](for-ai-agents.md). *** ## Path C — Build “Animo-like” with AI **Best for:** only if event infrastructure **is** your product — not if events are a marketing channel. **Scope of “Animo-lite”:** events, tickets, checkout, VAT, forms, admin, API — without meetings, marketplace, hubs, or years of production edge cases. | | Animo-lite (AI-assisted) | Full Animo parity | | -------------------------- | ------------------------------ | ----------------- | | **Calendar to production** | **9–18 months** | **18–36+ months** | | **Human hours** | **1,200–2,500** | **3,000–6,000+** | | **Build labor (year 1)** | **€80k–€200k** | **€250k–€500k+** | | **AI API spend (project)** | **€15k–€50k** (\~8–20M tokens) | **€40k–€100k+** | | **Code you maintain** | **25k–45k LOC** | **60k+ LOC** | | **Ongoing engineering** | **€35k–€90k/yr** (0.2–0.5 FTE) | **€70k–€150k/yr** | | **Payment / VAT risk** | **Yours** | **Yours** | AI speeds scaffolding; it does not reliably shortcut payment state machines, EU VAT, or webhook idempotency — where Animo’s production codebase already encodes years of edge cases. *** ## Three-path comparison (year 1) | | **A — Fully managed** | **B — Custom site** | **C — Build your own** | | ---------------------------- | --------------------- | -------------------------------- | ------------------------- | | **Platform** | €1,188 | €1,188 | €0 (you are the platform) | | **Integration labor** | €500–€2,500 | €4k–€12k | €80k–€200k | | **AI API spend** | €200–€500 | €500–€2k | €15k–€50k | | **Maintenance labor** | €1k–€2k | €2k–€6k | €35k–€90k | | **Infra** | — | €60–€600 | €5k–€20k+ | | **Year 1 total** | **\~€2k–€4k** | **\~€6k–€18k** | **\~€130k–€340k** | | **Human hours (one-time)** | **4–16** | **40–120** | **1,200–2,500** | | **Time to first paid event** | **Days** | **Weeks** | **Months** | | **Attendee-facing UX** | Animo hosted | Your site + Animo checkout/forms | All yours | | **Compliance risk** | Animo’s | Animo’s (money + data) | Yours | **Savings vs Path C (year 1):** * Path A saves **\~€125k–€335k** * Path B saves **\~€115k–€325k** — still **\~10× cheaper** than building, with full brand control on marketing **A vs B:** same €99/mo platform. The gap is **\~35–100 extra human hours** and **\~€4k–€14k** more integration labor for design, site, webhooks, and CRM wiring. Choose B only when hosted pages cannot meet brand requirements. *** ## Who each path is for | Profile | Path | | ------------------------------------------------------------ | ------------------------------------ | | Solo organiser, internal team event, speed over brand | **A** | | Marketing team, 4–20 events/yr, brand guidelines, own domain | **B** | | Agency provisioning many clients with light automation | **A** or **B** (per-client branding) | | No dev capacity at all | **A** — may not even need the API | | Funded startup whose product *is* event infrastructure | **C** | *** ## What AI does on each Animo path | Job | Path A | Path B | | ------------- | ------------------------------------------------ | ---------------------------------------------- | | **Provision** | Agent creates event, tickets, forms from a brief | Same + generates site structure | | **Operate** | Approve orders, update copy via API | Same | | **Integrate** | Optional thin webhook | **Required** — CRM/Slack with persistence | | **Sync** | N/A (hosted page is canonical) | Regenerate calendar/landing from `GET /events` | Path A: **tens of thousands** of tokens per engagement.\ Path B: **hundreds of thousands** — still far below Path C’s **millions**. *** ## Risks you buy down with Paths A & B * VAT & invoicing (business vs consumer, country, line items) * Payment lifecycle (order → hosted checkout → webhook → ticket issued) * No submission ingest API — hosted forms + webhooks is the supported model * Email deliverability (confirmation, pending, approved, denied) * Production edge cases (approval + failed payment, QR at door, activation flows) *** ## Recommendation For **event organisers and marketing-led businesses**: 1. **Default to Path A** — live in days, €99/mo, minimal build. 2. **Move to Path B** only when brand/domain requirements clearly outweigh **\~40–120 hours** of extra integration work. 3. **Avoid Path C** unless events infrastructure is the business itself. > €99/month ≈ **one hour** of senior contractor time. One delayed launch or one payment/VAT mistake usually costs more than a year of Pro. *** ## Next steps 1. **Pro** — [https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing) 2. **API token** — User settings → API tokens 3. **Read first** — [Animo for AI agents](for-ai-agents.md) 4. **Path A first win** — one event, one ticket type, hosted URL 5. **Path B add-on** — one webhook to CRM, then branded landing that links out *** ## Related * [API index](index.md) * [Animo for AI agents](for-ai-agents.md) — integration modes and what never to rebuild * [Conventions — Public URLs](conventions.md#public-urls) * [Webhooks](endpoints/webhooks.md) *** *Estimates based on Animo codebase scale (\~62k LOC application PHP, 134 migrations, Mollie/Stripe, multi-locale product surface) and typical integration depth. Adjust for your agency rates and CRM complexity.* # Conventions Source: https://docs.animo.co/conventions # Conventions Cross-cutting rules that apply across all Animo API v1 endpoints. ## Base URL and versioning * All endpoints are prefixed with `/api/v1`. * Versioning is URL-based only. There is no `Accept` header versioning. * Future versions would use a new prefix (e.g. `/api/v2`). ## Identifiers Public identifiers in API responses are **not** database primary keys: | Resource | ID format | Example field | | ----------- | --------- | ----------------------------- | | User | sqid | `id` | | Company | sqid | `id` (path param `{company}`) | | Event | slug | `id` (path param `{event}`) | | Activation | slug | `id` | | Form | sqid | `id` | | Form field | sqid | `id` | | Order | sqid | `id` | | Cohost | sqid | `id` | | Ticket type | slug | `slug` | | Webhook | UUID | `id` | Use the `id` (or `slug` for ticket types) returned by the API in subsequent requests. Route model binding resolves these public identifiers automatically. ## Timestamps Datetime fields in responses use ISO 8601 with a `+00:00` offset. See [Timestamps (actual format)](#timestamps-actual-format) for the real shape — do not assume a `Z` suffix or fixed millisecond width. **Input format for event dates** (create/update) uses a separate format: `Y-m-d H:i` (e.g. `2026-04-25 09:00`). ## Pagination List endpoints return Laravel's standard paginated JSON: ```json theme={null} { "data": [ ... ], "links": { "first": "https://{domain}/api/v1/{company}/events?page=1", "last": "https://{domain}/api/v1/{company}/events?page=3", "prev": null, "next": "https://{domain}/api/v1/{company}/events?page=2" }, "meta": { "current_page": 1, "from": 1, "last_page": 3, "per_page": 15, "to": 15, "total": 42 } } ``` Default page size is **15**. Use the `page` query parameter to navigate. ## Company context Most resources are scoped under a company: ```http theme={null} GET /api/v1/{company}/events ``` * `{company}` is the company **sqid** from `GET /api/v1/companies`. * Middleware restricts all queries to that company. * The authenticated user must belong to the company. * The company must have an active **Pro** plan (enforced via the `USE_API` subscription gate). ## Subscription gates API access is a paid feature. In the Animo admin, this is the **Pro** plan. Subscribe or upgrade at **[https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing)**. | Internal gate | Animo plan | Required for | | ------------------ | -------------------------------- | --------------------------------------------------------------- | | `USE_API` | **Pro** (Stripe tier: `starter`) | All company-scoped REST resources (events, forms, orders, etc.) | | `USE_INTEGRATIONS` | **Pro** and above | Webhook attach/update/detach | Free and Business plans do **not** grant API access. Upgrade at **[https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing)**. ### Entitlement behavior | Endpoint | Without Pro plan | | ----------------------------------------------------------- | ------------------------------------------------------------------------------- | | `GET /api/v1/companies` | `200` with empty `data: []` — looks like "no companies", not a permission error | | `GET /api/v1/{company}/…` (and other company-scoped routes) | `403 Forbidden` | If your token is valid but `GET /companies` returns an empty list, check the company's plan before debugging scopes or token setup. ## Public URLs The API does not return public-facing URLs in responses today. Construct them from slugs in the API response. ### Event page ``` https://app.animo.co/{company-slug}/event/{event-slug} ``` | Segment | API source | | ---------------- | ----------------------------------- | | `{company-slug}` | `slug` from `GET /api/v1/companies` | | `{event-slug}` | `id` from event resources | ### Ticket signup page Ticket types created via the API can be linked on the public Animo ticket shop. These URLs are separate from the API base URL and use **slugs**, not sqids. **Pattern:** ``` https://app.animo.co/{company-slug}/event/{event-slug}/{ticket-type-slug} ``` | Segment | Source | API field | | -------------------- | ---------------- | --------------------------------- | | `{company-slug}` | Company URL slug | `slug` on `GET /api/v1/companies` | | `{event-slug}` | Event URL slug | `id` on event resources | | `{ticket-type-slug}` | Ticket type slug | `slug` on ticket type resources | **Optional query parameter:** | Parameter | Description | | --------- | ------------------------------------------------------------- | | `qty` | Pre-select ticket quantity on the signup page (e.g. `?qty=1`) | **Example:** ``` https://app.animo.co/aidemodays/event/ai-demo-days-hr-edition/general-admission?qty=1 ``` The ticket signup flow continues at `/form` on the same path when a registration form is attached to the ticket type. ### Order checkout page (paid tickets) After creating a paid order via the API, send the attendee to Mollie checkout: ``` https://app.animo.co/{company-slug}/event/{event-slug}/o/{order-id}/checkout ``` | Segment | API source | | ------------ | ----------------------------------- | | `{order-id}` | `id` from the order resource (sqid) | `paid_at` remains `null` until Mollie payment succeeds (async webhook). ### Activation form page To embed or link an Animo-hosted activation form (sponsorship, waitlist, contact) from a custom site, get the activation `slug` from `GET /api/v1/{company}/activations`. There are two URL variants. **Prefer the company-scoped URL by default** — it always works for published activations. Use the event-scoped form only when you need to attribute the signup to a specific event. **Company-scoped (default):** ``` https://app.animo.co/{company-slug}/{activation-slug} ``` **Event-scoped** (when the activation is linked to an event): ``` https://app.animo.co/{company-slug}/{event-slug}/{activation-slug} ``` | Segment | API source | | ------------------- | --------------------------------------------------------------------------------- | | `{company-slug}` | `slug` from `GET /api/v1/companies` | | `{activation-slug}` | `id` or `slug` from `GET /api/v1/{company}/activations` | | `{event-slug}` | `id` from the linked event (**not** returned on `ActivationResource` — see below) | > **Unlike tickets, event activations do *not* use `/event/` in the path** — it's `/{company}/{event}/{activation}`, not `/{company}/event/{event}/{activation}`. **Sub-paths** (the flow, same as tickets): the base URLs above are the entry point. `/form` (form step) and `/thank-you` (confirmation) are reached *after* sign-up — link cold traffic to the **base URL, not `/form`**; visitors who hit `/form` without signing up are redirected back to the base URL. An event-scoped URL whose activation isn't actually attached to that event also redirects to the company-scoped URL. **API gap:** `ActivationResource` does not expose the linked event's slug, so you can't build the event-scoped URL from `GET /activations` alone — fetch the event slug from `GET /events`, or use the company-scoped URL, which always works. ### Slug behavior (events vs ticket types) | Resource | On create | On update | | ----------- | ---------------------------------------------------------------- | --------------------------------- | | Event | `id`/`slug` derived from `name` — supplied `slug` is **ignored** | `slug` can be changed via `PATCH` | | Ticket type | Supplied `slug` is **honored** | `slug` can be changed via `PATCH` | ## Timestamps (actual format) Responses use ISO 8601 datetimes with a `+00:00` UTC offset, not a `Z` suffix: ``` 2026-04-25T09:00:00.4040+00:00 ``` Fractional-second precision is **variable** (e.g. `.4040`, `.011`, `.088`). Do not assume exactly three millisecond digits. Parse as ISO 8601 with flexible fractional seconds. ## Search filter Several list endpoints support a `q` query parameter for case-sensitive substring search: | Endpoint | Searches | | ----------------------------------- | -------- | | `GET /api/v1/{company}/activations` | `title` | | `GET /api/v1/{company}/events` | `name` | | `GET /api/v1/{company}/forms` | `name` | ## Optional includes | Query parameter | Endpoints | Effect | | -------------------- | --------------------- | -------------------------------------------------- | | `include_form` | Ticket type list/show | Embeds the related form and fields | | `include_submission` | Order list/show | Embeds submission (with form and answers) and lead | ## Request bodies * Send JSON with `Content-Type: application/json`. * `PATCH` endpoints accept partial updates — only include fields you want to change. * `POST` create endpoints use paths like `/create` (e.g. `POST /api/v1/{company}/events/create`). ## Validation errors Invalid request bodies return `422 Unprocessable Entity`: ```json theme={null} { "message": "The name field is required. (and 1 more error)", "errors": { "name": [ "The name field is required." ], "date_start": [ "The date start field is required." ] } } ``` Refer to each endpoint page for field requirements and validation rules. ## Response envelope Single resources are wrapped in a `data` key: ```json theme={null} { "data": { "id": "my-event", "name": "My Event" } } ``` Collections (non-paginated) follow the same pattern. Paginated collections use the structure described in [Pagination](#pagination). ## Related * [Authentication](authentication.md) * [API index](index.md) # Activations Source: https://docs.animo.co/endpoints/activations # Activations ## Overview Activations are lead-generation experiences (forms, campaigns) belonging to a company. Use these endpoints to list and inspect activations and their associated forms. To link or embed the **hosted** activation form from your own site, see [Conventions — Activation form page](../conventions.md#activation-form-page) for the public URL patterns. There is no endpoint to submit into an activation — data of record is captured on the hosted flow and delivered via [Webhooks](webhooks.md). ## Endpoints ### List activations * **Method:** `GET` * **Path:** `/api/v1/{company}/activations` * **Scope:** `activations:read` * **Subscription:** `USE_API` #### Path parameters | Name | Type | Description | | --------- | ------ | ------------ | | `company` | string | Company sqid | #### Query parameters | Name | Type | Description | | ------ | ------- | --------------------------------- | | `q` | string | Filter by title (substring match) | | `page` | integer | Page number (default: 1) | #### Response `200` ```json theme={null} { "data": [ { "id": "summer-campaign", "redirect_url": "https://example.com/thanks", "slug": "summer-campaign", "status": "published", "title": "Summer Campaign", "type": "Form", "created_at": "2026-01-20T08:00:00.000+00:00" } ], "links": { "...": "..." }, "meta": { "...": "..." } } ``` The `form` relation is **not** included in list responses. #### Response fields | Field | Type | Description | | -------------- | ------------ | ------------------------------------ | | `id` | string | Activation slug | | `redirect_url` | string\|null | Post-submission redirect URL | | `slug` | string | URL slug | | `status` | string | Activation status | | `title` | string | Display title | | `type` | string | Human-readable activation type label | | `created_at` | string | ISO 8601 timestamp | #### Errors | Status | When | | ------ | ---------------------------------------------- | | `401` | Missing or invalid token | | `403` | Missing scope, subscription, or company access | *** ### Show activation * **Method:** `GET` * **Path:** `/api/v1/{company}/activations/{activation}` * **Scope:** `activations:read` * **Subscription:** `USE_API` #### Path parameters | Name | Type | Description | | ------------ | ------ | --------------- | | `company` | string | Company sqid | | `activation` | string | Activation slug | #### Query parameters None. #### Response `200` ```json theme={null} { "data": { "id": "summer-campaign", "redirect_url": "https://example.com/thanks", "slug": "summer-campaign", "status": "published", "title": "Summer Campaign", "type": "Form", "created_at": "2026-01-20T08:00:00.000+00:00", "form": { "id": "f2Kd8", "name": "Contact form", "created_at": "2026-01-15T10:00:00.000+00:00", "fields": [ { "id": "ff1Ab", "type": "email", "label": "Email", "position": 0 } ] } } } ``` The show response includes the nested `form` with `fields`. See [Forms](forms.md) for form field structure. #### Errors | Status | When | | ------ | ---------------------------------------------- | | `401` | Missing or invalid token | | `403` | Missing scope, subscription, or company access | | `404` | Activation not found | ## Related * [Authentication](../authentication.md) * [Conventions — Activation form page](../conventions.md#activation-form-page) — public URL patterns for the hosted form * [Animo for AI agents](../for-ai-agents.md) — when and how to link activation forms * [Forms](forms.md) * [Webhooks](webhooks.md) — `activations:subscribe` scope # Comms Source: https://docs.animo.co/endpoints/comms # Comms templates (event emails) ## Overview Manage customizable email templates sent to attendees during the event registration lifecycle — confirmation, pending approval, approved, denied, etc. Templates are scoped to an **organizing event** under `/event/{organizingEvent}/comms`. Each template is identified by a `template` slug (e.g. `registration_confirmation`). **Subscription:** Pro plan required. Upgrade at [https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing). ## Available templates | Template | Value | When sent | | -------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------- | | Registration confirmation | `registration_confirmation` | After registration (paid tickets, or types that don't require approval) | | Pending approval | `pending_approval` | After registration when approval is required | | Registration approved | `registration_approved` | When an organizer approves a pending order | | Registration approved (failed payment) | `registration_approved_with_failed_payment` | When approved but payment subsequently fails | | Registration denied | `registration_denied` | When an organizer rejects a pending order | | Registration abandoned | `registration_abandoned` | When a registrant abandons checkout | Templates marked as "private event" (`pending_approval`, `registration_approved`, `registration_approved_with_failed_payment`, `registration_denied`) are **only listed** when the event has at least one ticket type with `approval_required: true`. They can still be accessed individually via `show` regardless. ## Template variables Use these placeholders in `subject` and `body`. They are replaced at send time: | Variable | Description | | ---------------- | --------------------------------------- | | `{event}` | Event name | | `{first_name}` | Attendee first name | | `{last_name}` | Attendee last name | | `{email}` | Attendee email | | `{ticket_type}` | Ticket type name | | `{order_url}` | Link to the order page | | `{order_button}` | Styled button linking to the order page | ## Default vs custom templates * `is_default: true` — no custom override saved; `subject` and `body` reflect the system default for the event's locale * `is_default: false` — a custom override exists in the database `DELETE` resets a template back to the system default. ## Endpoints ### List comms templates * **Method:** `GET` * **Path:** `/api/v1/{company}/event/{organizingEvent}/comms` * **Scope:** `events:read` * **Subscription:** Pro plan (`USE_API`) Returns all applicable templates for the event, merging saved overrides with defaults. #### Response `200` ```json theme={null} { "data": [ { "template": "registration_confirmation", "subject": "🎫 Your registration for {event} has been confirmed!", "body": "

Hi {first_name},

You're registered for {event}.

", "is_default": true }, { "template": "pending_approval", "subject": "⏳ Registration pending approval for {event}", "body": "

Hi {first_name}, your registration is being reviewed.

", "is_default": false } ] } ``` #### Response fields | Field | Type | Description | | ------------ | ------- | ----------------------------------------------------- | | `template` | string | Template identifier — use as `{template}` path param | | `subject` | string | Email subject line (may contain variables) | | `body` | string | Email HTML body (may contain variables) | | `is_default` | boolean | `true` if using system default, `false` if customized | *** ### Show comms template * **Method:** `GET` * **Path:** `/api/v1/{company}/event/{organizingEvent}/comms/{template}` * **Scope:** `events:read` * **Subscription:** Pro plan (`USE_API`) #### Path parameters | Name | Type | Description | | ---------- | ------ | ------------------------------------------------ | | `template` | string | Template value, e.g. `registration_confirmation` | #### Response `200` Single `MessageResource` in `data` envelope. #### Errors | Status | When | | ------ | ---------------------- | | `404` | Unknown template value | *** ### Update comms template * **Method:** `PATCH` * **Path:** `/api/v1/{company}/event/{organizingEvent}/comms/{template}` * **Scope:** `events:update` * **Subscription:** Pro plan (`USE_API`) Creates a custom override if none exists, or updates the existing one. #### Request body See the request body table below for field requirements. At least one of `subject` or `body` is required. ```json theme={null} { "subject": "🎫 You're in! {event}", "body": "

Hi {first_name},

Your spot at {event} is confirmed.

{order_button}

" } ``` | Field | Required | Description | | --------- | ------------------- | ----------------------------- | | `subject` | One of subject/body | Email subject (max 255 chars) | | `body` | One of subject/body | Email HTML body | #### Response `200` Returns the updated `MessageResource` with `is_default: false`. *** ### Reset comms template * **Method:** `DELETE` * **Path:** `/api/v1/{company}/event/{organizingEvent}/comms/{template}` * **Scope:** `events:update` * **Subscription:** Pro plan (`USE_API`) Removes the custom override. The template reverts to the system default on next `show` or `index`. #### Response `200` ```json theme={null} { "status": "Deleted" } ``` ## Related * [Authentication](../authentication.md) * [Conventions](../conventions.md) * [Events](events.md) * [Event pages](event-pages.md) * [Orders](orders.md) # Companies Source: https://docs.animo.co/endpoints/companies # Companies ## Overview Lists companies the authenticated user belongs to that have an active **Pro** plan. Upgrade at [https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing). * Use the returned `id` (sqid) as the `{company}` path parameter for all API endpoints. * Use the returned `slug` when constructing [public URLs](../conventions.md#public-urls) (e.g. `https://app.animo.co/{slug}/event/...`). If your companies are on Free or Business plans, this endpoint returns `200` with `data: []` — not `403`. That usually means the plan needs upgrading, not that your token is wrong. ## Endpoints ### List companies * **Method:** `GET` * **Path:** `/api/v1/companies` * **Scope:** `companies:read` * **Subscription:** Pro plan (`USE_API`) — company must be on Pro to appear in the list #### Path parameters None. #### Query parameters None. #### Response `200` ```json theme={null} { "data": [ { "id": "c9Xk2", "created_at": "2025-03-10T14:00:00.000+00:00", "backdrop": "https://example.com/backdrop.jpg", "backdrop_mobile": "https://example.com/backdrop-mobile.jpg", "brand_color": "#FF5500", "linkedin_url": "https://linkedin.com/company/acme", "privacy_policy": "https://example.com/privacy", "logo": "https://example.com/logo.png", "name": "Acme Events", "slug": "acme-events", "tagline": "Events that connect", "url": "https://acme.example.com" } ], "links": { "...": "..." }, "meta": { "...": "..." } } ``` #### Response fields | Field | Type | Description | | ----------------- | ------------ | ----------------------------------------- | | `id` | string | Company sqid — use as `{company}` in URLs | | `created_at` | string | ISO 8601 timestamp | | `backdrop` | string\|null | Desktop backdrop image URL | | `backdrop_mobile` | string\|null | Mobile backdrop image URL | | `brand_color` | string | Hex brand color | | `linkedin_url` | string\|null | Company LinkedIn URL | | `privacy_policy` | string\|null | Privacy policy URL | | `logo` | string\|null | Logo image URL | | `name` | string | Company name | | `slug` | string | URL slug | | `tagline` | string\|null | Company tagline | | `url` | string\|null | Company website URL | #### Errors | Status | When | | ------ | ------------------------------------ | | `401` | Missing or invalid token | | `403` | Token missing `companies:read` scope | ## Related * [Authentication](../authentication.md) * [Conventions](../conventions.md) * [Events](events.md) # Event pages Source: https://docs.animo.co/endpoints/event-pages # Event pages ## Overview Event pages control the public-facing content shown on an event's landing page at `https://app.animo.co/{company-slug}/event/{event-slug}` — tagline, description, header image, support email, and attendee visibility. These endpoints apply to **organizing events** only, nested under `/event/{organizingEvent}`. **Subscription:** Pro plan required. Upgrade at [https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing). The `page` object is also embedded on organizing event `show` and `update` responses — see [Events](events.md). ## Endpoints ### Show event page * **Method:** `GET` * **Path:** `/api/v1/{company}/event/{organizingEvent}/page` * **Scope:** `events:read` * **Subscription:** Pro plan (`USE_API`) Returns the event page if one exists, or an empty page object with default/null values if none has been created yet. #### Path parameters | Name | Type | Description | | ----------------- | ------ | --------------------- | | `company` | string | Company sqid | | `organizingEvent` | string | Organizing event slug | #### Response `200` ```json theme={null} { "data": { "tagline": "The future of HR tech", "description": "

Join us for a full day of demos and networking.

", "support_email": "events@example.com", "header_image": "https://cdn.example.com/events/header.jpg", "show_attendees": true, "unsplash_data": null } } ``` #### Response fields | Field | Type | Description | | ---------------- | ------------ | --------------------------------------------------------------------------- | | `tagline` | string\|null | Short headline on the event page | | `description` | string\|null | HTML description (sanitized on read) | | `support_email` | string\|null | Contact email shown on the public page | | `header_image` | string\|null | Header image URL | | `show_attendees` | boolean | Whether registered attendees are visible | | `unsplash_data` | object\|null | Unsplash attribution data — present only when header image is from Unsplash | #### Errors | Status | When | | ------ | ---------------------------------------------- | | `401` | Missing or invalid token | | `403` | Missing scope, subscription, or company access | | `404` | Event not found or not an organizing event | *** ### Update event page * **Method:** `POST` * **Path:** `/api/v1/{company}/event/{organizingEvent}/page` * **Scope:** `events:update` * **Subscription:** Pro plan (`USE_API`) Creates the event page if it does not exist, then applies the supplied fields. #### Content type Use `multipart/form-data` when uploading `header_image`. Use `application/json` for text-only updates. #### Request body See the request body table below for field requirements. | Field | Required | Description | | ---------------- | -------- | ------------------------------------------------------------------------------------ | | `tagline` | No | Short headline (max 255 chars) | | `description` | No | HTML description | | `support_email` | No | Valid email address (max 255 chars) | | `show_attendees` | No | Boolean | | `header_image` | No | Image file — `jpg`, `jpeg`, `png`, `gif`, `webp`, `avif`, or `apng`; min width 320px | All fields are optional (partial update). Omit a field to leave it unchanged. #### Example (multipart) ```http theme={null} POST /api/v1/{company}/event/{event-slug}/page Authorization: Bearer {token} Content-Type: multipart/form-data tagline=The future of HR tech description=

Join us for a full day of demos.

support_email=events@example.com show_attendees=true header_image=@header.jpg ``` #### Response `200` Returns the updated `EventPageResource`. #### Errors | Status | When | | ------ | --------------------------------------------------------- | | `422` | Validation failure (invalid email, image too small, etc.) | | `403` | Missing `events:update` scope | ## Related * [Authentication](../authentication.md) * [Conventions](../conventions.md) * [Events](events.md) * [Comms templates](comms.md) # Events Source: https://docs.animo.co/endpoints/events # Events ## Overview Events represent online or offline gatherings for a company. The API supports listing, creating, updating events, and managing cohosts. Organizing events also expose [event page](event-pages.md) and [comms template](comms.md) endpoints. Ticket types and orders are nested under organizing events — see [Ticket types](ticket-types.md) and [Orders](orders.md). **Subscription:** Pro plan required. Upgrade at [https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing). ## Public event URL ``` https://app.animo.co/{company-slug}/event/{event-slug} ``` Use `slug` from companies and `id` from the event resource. Not returned by the API — see [Conventions — Public URLs](../conventions.md#public-urls). ## Event ID and slug behavior On **create**, the event `id` is derived from `name` (e.g. `"AI Demo Days · HR Edition"` → `ai-demo-days-hr-edition`). A `slug` sent in the request body is **ignored**. On **update**, `slug` can be set explicitly via `PATCH`. This differs from ticket types, where the supplied `slug` is honored on create. See [Conventions — Slug behavior](../conventions.md#slug-behavior-events-vs-ticket-types). ## Endpoints ### List events * **Method:** `GET` * **Path:** `/api/v1/{company}/events` * **Scope:** `events:read` * **Subscription:** `USE_API` #### Path parameters | Name | Type | Description | | --------- | ------ | ------------ | | `company` | string | Company sqid | #### Query parameters | Name | Type | Description | | ------ | ------- | -------------------------------------- | | `q` | string | Filter by event name (substring match) | | `page` | integer | Page number | #### Response `200` ```json theme={null} { "data": [ { "id": "product-launch-2026", "name": "Product Launch 2026", "date_start": "2026-04-25T09:00:00.000+00:00", "date_end": "2026-04-26T18:00:00.000+00:00", "created_at": "2026-03-01T12:00:00.000+00:00", "attendance_mode": "offline", "company_role": "organizing", "timezone": "America/Montevideo", "privacy_policy": "https://example.com/privacy", "locale": "en", "requires_ticket_assignment": false, "city": { "name": "Montevideo", "latitude": -34.9011, "longitude": -56.1645, "google_place_id": "ChIJ..." }, "country": { "name": "Uruguay", "alpha2Code": "UY", "alpha3Code": "URY" }, "venue": { "name": "Convention Center", "google_place_id": "ChIJ...", "url": "https://example.com", "maps_url": "https://maps.google.com/...", "address": "Main St 1", "latitude": -34.9011, "longitude": -56.1645 }, "cohosts": [] } ], "links": { "...": "..." }, "meta": { "...": "..." } } ``` #### Response fields | Field | Type | Description | | ---------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------- | | `id` | string | Event slug | | `name` | string | Event name | | `date_start` | string | Start datetime (ISO 8601) | | `date_end` | string | End datetime (ISO 8601) | | `created_at` | string | Creation timestamp | | `attendance_mode` | string | `online` or `offline` | | `company_role` | string | `organizing` or `attending` | | `timezone` | string | IANA timezone identifier | | `privacy_policy` | string\|null | Privacy policy URL | | `locale` | string | Supported locale code | | `requires_ticket_assignment` | boolean | Whether tickets must be assigned to attendees | | `city` | object\|null | City details (offline events) | | `country` | object\|null | Country details | | `venue` | object\|null | Venue details | | `cohosts` | array | List of cohost objects | | `page` | object | Event page content — present on **show** and **update** for organizing events only. See [Event pages](event-pages.md) | #### Errors | Status | When | | ------ | ---------------------------------------------- | | `401` | Missing or invalid token | | `403` | Missing scope, subscription, or company access | *** ### Create event * **Method:** `POST` * **Path:** `/api/v1/{company}/events/create` * **Scope:** `events:create` (in addition to `events:read` on the route group) * **Subscription:** `USE_API` #### Request body See the request body table below for field requirements. ```json theme={null} { "date_start": "2026-04-25 09:00", "date_end": "2026-04-26 18:00", "city": "Montevideo", "province": "Montevideo", "country": "Uruguay", "venue": "Convention Center", "venue_address": "Main St 1", "name": "Product Launch 2026", "slug": "product-launch-2026", "attendance_mode": "offline", "timezone": "America/Montevideo", "locale": "en", "company_role": "organizing", "privacy_policy": "https://example.com/privacy", "tax_country": "UY", "requires_ticket_assignment": false } ``` | Field | Required | Description | | ---------------------------- | ------------- | ------------------------------------------------------------------------------------------------------- | | `date_start` | Yes | Format `Y-m-d H:i` | | `date_end` | Yes | Format `Y-m-d H:i`, must be >= `date_start` | | `name` | Yes | Event name (max 255 chars) | | `attendance_mode` | Yes | `online` or `offline` | | `locale` | Yes | Supported locale enum value | | `company_role` | Yes | `organizing` or `attending` | | `city` | If offline | City name | | `province` | If offline | Province/region | | `country` | If offline | Country name | | `venue` | No | Venue name | | `venue_address` | No | Venue street address | | `slug` | No | **Ignored on create** — event `id` is auto-derived from `name`. Use `PATCH` to set slug after creation. | | `timezone` | If online | IANA timezone | | `privacy_policy` | No | Valid URL | | `tax_country` | No | ISO 3166-1 alpha-2 country code | | `requires_ticket_assignment` | If organizing | Boolean | #### Response `201` Returns a single `EventResource` in the `data` envelope. #### Errors | Status | When | | ------ | ----------------------------- | | `422` | Validation failure | | `403` | Missing `events:create` scope | *** ### Show event * **Method:** `GET` * **Path:** `/api/v1/{company}/events/{event}` * **Scope:** `events:read` * **Subscription:** `USE_API` #### Path parameters | Name | Type | Description | | --------- | ------ | ------------ | | `company` | string | Company sqid | | `event` | string | Event slug | #### Response `200` Same shape as a single item from the list response. Organizing events include a nested `page` object. *** ### Update event * **Method:** `PATCH` * **Path:** `/api/v1/{company}/events/{event}` * **Scope:** `events:update` * **Subscription:** `USE_API` #### Request body All fields are optional (partial update). See the request body table below for field requirements. ```json theme={null} { "name": "Updated Event Name", "date_start": "2026-05-01 10:00", "date_end": "2026-05-01 18:00", "google_place_id": "ChIJ...", "venue_google_place_id": "ChIJ...", "venue": "New Venue", "venueAddress": "New Address", "slug": "updated-slug", "attendance_mode": "online", "timezone": "Europe/Amsterdam", "locale": "nl", "company_role": "organizing", "privacy_policy": "https://example.com/privacy", "tax_country": "NL", "requires_ticket_assignment": true } ``` #### Response `200` Returns the updated `EventResource`. *** ### Add cohost * **Method:** `POST` * **Path:** `/api/v1/{company}/events/{event}/cohosts` * **Scope:** `events:update` * **Subscription:** `USE_API` #### Request body ```json theme={null} { "name": "Partner Org", "privacy_policy": "https://partner.example.com/privacy", "position": 1 } ``` | Field | Required | Description | | ---------------- | -------- | ------------------------------ | | `name` | Yes | Cohost display name | | `privacy_policy` | No | Valid URL (max 512 chars) | | `position` | No | Sort position (integer, min 1) | #### Response `200` Returns the parent event with updated `cohosts` array. *** ### Update cohost * **Method:** `PATCH` * **Path:** `/api/v1/{company}/events/{event}/cohosts/{cohost}` * **Scope:** `events:update` * **Subscription:** `USE_API` #### Request body Same fields as add cohost; `name` is optional on update. #### Response `200` Returns the parent event with updated `cohosts` array. *** ### Delete cohost * **Method:** `DELETE` * **Path:** `/api/v1/{company}/events/{event}/cohosts/{cohost}` * **Scope:** `events:update` * **Subscription:** `USE_API` #### Response `200` Returns the parent event with the cohost removed. ## Not implemented * `DELETE /api/v1/{company}/events/{event}` — not exposed ## Related * [Authentication](../authentication.md) * [Conventions](../conventions.md) * [Event pages](event-pages.md) * [Comms templates](comms.md) * [Ticket types](ticket-types.md) * [Orders](orders.md) # Forms Source: https://docs.animo.co/endpoints/forms # Forms ## Overview Forms are reusable field collections belonging to a company. They are used by activations, ticket types, and other features. The API supports listing, creating, and updating forms and their fields. **Subscription:** Pro plan required. Upgrade at [https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing). ## Form field types Field `type` values fall into two categories: ### Custom fields General-purpose fields you can add freely to any form. Use the `type` value in the API when creating or updating fields. | Type | Label | Description | | ---------- | --------------- | -------------------------------------------------------- | | `text` | Text | Single-line text input. Supports `placeholder`. | | `textarea` | Multi-line text | Multi-line text input. Supports `placeholder`. | | `number` | Number | Numeric input. Supports `placeholder`, `min`, and `max`. | | `date` | Date | Date picker. | | `time` | Time | Time picker. | | `url` | URL | URL input. Supports `placeholder`. | | `file` | File | File upload. | | `option` | Options | Multiple-choice field. Requires an `options` array. | | `select` | Select box | Dropdown selection. Requires an `options` array. | | `toggle` | Toggle | On/off switch. Does not support `description`. | | `rating` | Rating | Star or scale rating input. | ### Common fields Pre-defined field types that map to standard lead attributes (birthdate, phone, job title, etc.). Each common field type can appear **at most once** per form — adding a second field with the same common `type` returns a validation error. | Type | Label | Description | | -------------- | ----------------- | ------------------------------------------------------------------------------------------------------------- | | `birthdate` | Birthdate | Date of birth. | | `company_name` | Company name | Attendee's company name. | | `company_url` | Company URL | Company website URL. | | `consent` | Marketing Consent | Consent checkbox. Does not support `description` (use `label` for consent text). Label max 10,000 characters. | | `country` | Country | Country selection. | | `gender` | Gender | Gender selection. | | `job_title` | Job title | Job title or role. | | `language` | Language | Preferred language. | | `phone` | Phone number | Phone number input. | | `social_url` | Social URL | Social profile URL. Supports `placeholder`. | ### Field attributes by type | Attribute | Supported on | | ------------- | ------------------------------------------------------------- | | `description` | All types except `consent` and `toggle` | | `placeholder` | `text`, `textarea`, `number`, `url`, `social_url` | | `required` | All types | | `min` / `max` | `number` only | | `options` | `option` and `select` — see [Options format](#options-format) | | `position` | All types (sort order, >= 0) | ### Options format For `option` and `select` fields, `options` accepts: * A **plain string array** (recommended): `["Option A", "Option B"]` * An array of `{label, value}` objects (also accepted) ## Endpoints ### List forms * **Method:** `GET` * **Path:** `/api/v1/{company}/forms` * **Scope:** `forms:read` * **Subscription:** Pro plan (`USE_API`) #### Path parameters | Name | Type | Description | | --------- | ------ | ------------ | | `company` | string | Company sqid | #### Query parameters | Name | Type | Description | | ------ | ------- | ------------------------------------- | | `q` | string | Filter by form name (substring match) | | `page` | integer | Page number | #### Response `200` ```json theme={null} { "data": [ { "id": "v7KAa", "name": "Registration form", "created_at": "2026-02-01T10:00:00.4040+00:00", "fields": [ { "id": "ff1Ab", "type": "text", "label": "Email address", "description": "We'll send your ticket here", "position": 0, "placeholder": "you@example.com", "required": true } ] } ], "links": { "...": "..." }, "meta": { "...": "..." } } ``` Use the form `id` (sqid) as `form_id` when attaching a form to a ticket type. #### Form field fields | Field | Type | Description | | ------------- | ------- | ------------------------------------------------------ | | `id` | string | Field sqid | | `type` | string | Field type — see [Form field types](#form-field-types) | | `label` | string | Display label | | `description` | string | Shown for types that allow descriptions | | `options` | array | Options for choice fields | | `position` | integer | Sort order | | `placeholder` | string | Placeholder for supported types | | `required` | boolean | Whether the field is required | | `min` | number | Min value for number fields | | `max` | number | Max value for number fields | *** ### Show form * **Method:** `GET` * **Path:** `/api/v1/{company}/forms/{form}` * **Scope:** `forms:read` * **Subscription:** Pro plan (`USE_API`) #### Response `200` Single `FormResource` with `fields` array. *** ### Create form * **Method:** `POST` * **Path:** `/api/v1/{company}/forms/create` * **Scope:** `forms:create` * **Subscription:** Pro plan (`USE_API`) #### Request body See the request body table below for top-level field requirements. Individual form fields are validated when included in the `fields` array. ```json theme={null} { "name": "Registration form", "fields": [ { "type": "text", "label": "Email address", "description": "We'll send your ticket here", "placeholder": "you@example.com", "required": true, "position": 0 }, { "type": "company_name", "label": "Company", "position": 1 } ] } ``` | Field | Required | Description | | ---------------------- | ------------------------------- | -------------------------------------------------------------------------------- | | `name` | Yes | Form name | | `fields` | Yes | Array of field objects | | `fields[].type` | Yes | Field type — see [Form field types](#form-field-types) | | `fields[].label` | Yes | Display label | | `fields[].description` | No | Field help text (not all types support it) | | `fields[].placeholder` | No | Input placeholder | | `fields[].required` | No | Mark field as required | | `fields[].position` | No | Sort order (>= 0) | | `fields[].options` | If type is `option` or `select` | String array or `{label, value}` objects — see [Options format](#options-format) | | `fields[].min` | No | Min for number fields | | `fields[].max` | No | Max for number fields | #### Response `201` Returns the created `FormResource`. *** ### Update form * **Method:** `PATCH` * **Path:** `/api/v1/{company}/forms/{form}` * **Scope:** `forms:update` * **Subscription:** Pro plan (`USE_API`) #### Request body ```json theme={null} { "name": "Updated form name", "fields": [ { "id": "ff1Ab", "label": "Updated label" }, { "type": "text", "label": "New field", "position": 2 } ] } ``` * Include `id` on a field to update an existing field. * Omit `id` to create a new field on the form. * Only `name` can be changed at the form level. #### Response `200` Returns the updated `FormResource`. ## Not implemented * `DELETE /api/v1/{company}/forms/{form}` — no route exists (`forms:delete` scope is registered but unused). Forms created via the API cannot be deleted through the API. ## Related * [Authentication](../authentication.md) * [Conventions](../conventions.md) * [Activations](activations.md) * [Webhooks](webhooks.md) — `submissions:subscribe` scope # Orders Source: https://docs.animo.co/endpoints/orders # Orders ## Overview Orders represent ticket registrations for organizing events. Use these endpoints to list orders, create orders on behalf of attendees, and approve or reject pending orders. **Subscription:** Pro plan required. Upgrade at [https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing). ## Order fields vs attached form `orders/create` has two layers of attendee data: | Layer | Fields | Purpose | | ------------------------- | ------------------------------------------------------------------ | ----------------------------------------------- | | **Order body** | `first_name`, `last_name`, `email`, `country`, `is_business`, etc. | Core registration and billing — always required | | **Form payload** (`form`) | Answers for fields on the ticket type's attached form | Additional custom questions only | Do **not** duplicate `first_name`, `last_name`, or `email` as form fields — those come from the order body. The attached form is for extra questions (job title, dietary requirements, etc.). ## Paid orders and checkout For paid ticket types, `orders/create` creates the order but does **not** charge the attendee. `OrderResource` does not currently include a payment URL field. Send the attendee to the Mollie checkout page: ``` https://app.animo.co/{company-slug}/event/{event-slug}/o/{order-id}/checkout ``` | Lifecycle step | API signal | | --------------------------------------- | ------------------- | | Order created | `paid_at` is `null` | | Mollie payment succeeds (async webhook) | `paid_at` is set | Requires a connected Mollie account on the company — see [Ticket types — Paid tickets and Mollie](ticket-types.md#paid-tickets-and-mollie). ## Endpoints ### List orders * **Method:** `GET` * **Path:** `/api/v1/{company}/event/{organizingEvent}/orders` * **Scope:** `orders:read` (route group also requires `events:read`) * **Subscription:** Pro plan (`USE_API`) #### Path parameters | Name | Type | Description | | ----------------- | ------ | --------------------- | | `company` | string | Company sqid | | `organizingEvent` | string | Organizing event slug | #### Query parameters | Name | Type | Description | | -------------------- | ------- | ----------------------------------------- | | `include_submission` | boolean | Embeds submission (with answers) and lead | | `page` | integer | Page number | #### Response `200` ```json theme={null} { "data": [ { "id": "o7Xm3", "first_name": "Jordan", "last_name": "Lee", "email": "jordan@example.com", "unit_amount": 2500, "currency": "EUR", "vat_percentage": "21", "exemption_status": "none", "created_at": "2026-04-10T14:30:00.4040+00:00", "paid_at": "2026-04-10T14:31:00.011+00:00", "authorized_at": null, "approved_at": null, "rejected_at": null, "cancelled_at": null, "country": { "name": "Netherlands", "alpha2Code": "NL", "alpha3Code": "NLD" } } ], "links": { "...": "..." }, "meta": { "...": "..." } } ``` #### Response fields | Field | Type | Description | | ------------------ | ------------ | ----------------------------------- | | `id` | string | Order sqid | | `first_name` | string | Attendee first name | | `last_name` | string | Attendee last name | | `email` | string | Attendee email | | `unit_amount` | integer | Total amount in minor units (cents) | | `currency` | string | Currency code | | `vat_percentage` | string | Applied VAT rate | | `exemption_status` | string | VAT exemption status | | `created_at` | string | Order creation timestamp | | `paid_at` | string\|null | Payment timestamp | | `authorized_at` | string\|null | Authorization timestamp | | `approved_at` | string\|null | Approval timestamp | | `rejected_at` | string\|null | Rejection timestamp | | `cancelled_at` | string\|null | Cancellation timestamp | | `submission` | object | Present with `include_submission` | | `lead` | object | Present with `include_submission` | | `country` | object | Buyer country | *** ### Show order * **Method:** `GET` * **Path:** `/api/v1/{company}/event/{organizingEvent}/orders/{order}` * **Scope:** `orders:read` * **Subscription:** Pro plan (`USE_API`) #### Query parameters | Name | Type | Description | | -------------------- | ------- | -------------------------- | | `include_submission` | boolean | Embeds submission and lead | #### Response `200` Single `OrderResource` in `data` envelope. *** ### Create order * **Method:** `POST` * **Path:** `/api/v1/{company}/event/{organizingEvent}/orders/create/{ticketType}` * **Scope:** `orders:create` * **Subscription:** Pro plan (`USE_API`) Registers tickets on behalf of an attendee. #### Path parameters | Name | Type | Description | | ------------ | ------ | ---------------- | | `ticketType` | string | Ticket type slug | #### Request body See the request body table below for field requirements. ```json theme={null} { "first_name": "Jordan", "last_name": "Lee", "email": "jordan@example.com", "country": "NL", "company_name": "Acme BV", "vat_number": "NL123456789B01", "city": "Amsterdam", "address": "Keizersgracht 1", "post_code": "1015", "is_business": false, "quantity": 1 } ``` | Field | Required | Description | | -------------- | -------- | --------------------------------------------------------------- | | `first_name` | Yes | Attendee first name | | `last_name` | Yes | Attendee last name | | `email` | Yes | Valid email address | | `country` | Yes | ISO 3166-1 alpha-2 country code | | `is_business` | Yes | Boolean | | `quantity` | Yes | Integer within ticket type min/max and availability | | `company_name` | No | Company name for business orders | | `vat_number` | No | VAT number | | `city` | No | City | | `address` | No | Street address | | `post_code` | No | Postal code | | `form` | No | Additional form answers — **not fully implemented** (see below) | #### Form answers (`form` field) > **Not yet fully implemented.** The `form` array is accepted by validation but order creation does not yet persist form submissions server-side. Shape is expected to be field sqid → value: ```json theme={null} { "form": { "ff1Ab": "Senior Engineer", "ff2Cd": "Vegetarian" } } ``` Keys are form field sqids from the attached ticket type form. Do not include order-level fields (`first_name`, `last_name`, `email`) here. #### Response `200` Returns the created `OrderResource`. For paid tickets, redirect the attendee to the [checkout URL](#paid-orders-and-checkout). `paid_at` remains `null` until payment completes. *** ### Approve order * **Method:** `PATCH` * **Path:** `/api/v1/{company}/event/{organizingEvent}/orders/{order}/approve` * **Scope:** `orders:curate` * **Subscription:** Pro plan (`USE_API`) Approves a pending order that requires curator approval. #### Response `200` Returns the updated `OrderResource` with `approved_at` set. *** ### Reject order * **Method:** `PATCH` * **Path:** `/api/v1/{company}/event/{organizingEvent}/orders/{order}/reject` * **Scope:** `orders:curate` * **Subscription:** Pro plan (`USE_API`) Rejects a pending order. #### Response `200` Returns the updated `OrderResource` with `rejected_at` set. ## Not implemented * `DELETE /api/v1/{company}/event/{organizingEvent}/orders/{order}/cancel` — route is commented out ## Related * [Authentication](../authentication.md) * [Conventions](../conventions.md) * [Ticket types](ticket-types.md) # Ticket types Source: https://docs.animo.co/endpoints/ticket-types # Ticket types ## Overview Ticket types define purchasable or free tickets for **organizing** events. These endpoints are nested under `/event/{organizingEvent}` — the event must be one your company organizes. **Subscription:** Pro plan required. Upgrade at [https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing). ## Public ticket page URL Each ticket type has a public signup page on the Animo app. Construct the URL from slugs returned by the API: ``` https://app.animo.co/{company-slug}/event/{event-slug}/{ticket-type-slug} ``` | Segment | API source | | -------------------- | ------------------------------------ | | `{company-slug}` | `slug` from `GET /api/v1/companies` | | `{event-slug}` | `id` from the event resource | | `{ticket-type-slug}` | `slug` from the ticket type resource | Append `?qty={n}` to pre-select the ticket quantity (e.g. `?qty=1`). **Example:** ``` https://app.animo.co/aidemodays/event/ai-demo-days-hr-edition/general-admission?qty=1 ``` See [Conventions — Public URLs](../conventions.md#public-urls) for full details. Unlike events, the `slug` you supply on ticket type **create** is honored and can be changed later via `PATCH`. ## Paid tickets and Mollie Paid ticket types (`price` > 0) require a **connected Mollie account** on the company before the public ticket page works. | State | Public page behavior | | -------------------- | -------------------------------------------------------------------------------------- | | Mollie connected | Ticket signup page loads normally | | Mollie not connected | Public page returns **404** — the ticket type exists in the API but is not purchasable | Connect Mollie in the Animo admin before sharing paid ticket URLs. Free tickets (`price: 0`) are not affected. ## Endpoints ### List ticket types * **Method:** `GET` * **Path:** `/api/v1/{company}/event/{organizingEvent}/ticket-types` * **Scope:** `ticket-types:read` (route group also requires `events:read`) * **Subscription:** Pro plan (`USE_API`) #### Path parameters | Name | Type | Description | | ----------------- | ------ | --------------------- | | `company` | string | Company sqid | | `organizingEvent` | string | Organizing event slug | #### Query parameters | Name | Type | Description | | -------------- | ------- | ------------------------------------------------ | | `include_form` | boolean | When present, embeds the related form and fields | #### Response `200` ```json theme={null} { "data": [ { "slug": "general-admission", "name": "General Admission", "description": "Standard entry ticket", "price": "25.00", "currency": "EUR", "vat_percentage": "21", "availability": 100, "approval_required": false, "sold_out": false, "active": true, "hidden_from_shop": false, "form_id": "f2Kd8", "position": 0, "min_quantity": 1, "max_quantity": 5 } ] } ``` #### Response fields | Field | Type | Description | | ------------------- | ------------ | -------------------------------------- | | `slug` | string | Ticket type slug (use in URLs) | | `name` | string | Display name | | `description` | string\|null | Description | | `price` | string | Price as decimal string (`0` for free) | | `currency` | string | Currency code | | `vat_percentage` | string | VAT rate percentage | | `availability` | integer | Max tickets (`-1` = unlimited) | | `approval_required` | boolean | Requires organizer approval | | `sold_out` | boolean | Marked as sold out | | `active` | boolean | Available for purchase | | `hidden_from_shop` | boolean | Hidden from public shop | | `form_id` | string\|null | Related form sqid | | `form` | object | Present when `include_form` is set | | `position` | integer | Sort order | | `min_quantity` | integer | Minimum per order | | `max_quantity` | integer | Maximum per order | *** ### Create ticket type * **Method:** `POST` * **Path:** `/api/v1/{company}/event/{organizingEvent}/ticket-types/create` * **Scope:** `ticket-types:create` * **Subscription:** Pro plan (`USE_API`) #### Request body See the request body table below for field requirements. ```json theme={null} { "name": "General Admission", "slug": "general-admission", "description": "Standard entry ticket", "form_id": "f2Kd8", "price": 25.00, "vat_percentage": 21, "availability": 100, "active": true, "hidden_from_shop": false, "approval_required": false, "sold_out": false, "position": 0, "min_quantity": 1, "max_quantity": 5 } ``` | Field | Required | Description | | ------------------- | -------- | -------------------------------------------------- | | `name` | Yes | Display name | | `price` | Yes | Numeric; `0` for free, minimum paid price enforced | | `vat_percentage` | Yes | Must be a valid rate for the event's tax country | | `slug` | No | URL slug | | `description` | No | Text description | | `form_id` | No | Form sqid belonging to the company | | `availability` | No | `0` or positive integer; `-1` for unlimited | | `active` | No | Boolean | | `hidden_from_shop` | No | Boolean | | `approval_required` | No | Boolean | | `sold_out` | No | Boolean | | `position` | No | Sort order (>= 0) | | `min_quantity` | No | Min per order (>= 1) | | `max_quantity` | No | Max per order (>= min\_quantity) | #### Response `201` Returns a single `TicketTypeResource`. *** ### Show ticket type * **Method:** `GET` * **Path:** `/api/v1/{company}/event/{organizingEvent}/ticket-types/{ticketType}` * **Scope:** `ticket-types:read` * **Subscription:** Pro plan (`USE_API`) #### Query parameters | Name | Type | Description | | -------------- | ------- | ---------------------- | | `include_form` | boolean | Embeds form and fields | #### Response `200` Single `TicketTypeResource` in `data` envelope. *** ### Update ticket type * **Method:** `PATCH` * **Path:** `/api/v1/{company}/event/{organizingEvent}/ticket-types/{ticketType}` * **Scope:** `ticket-types:update` * **Subscription:** Pro plan (`USE_API`) #### Request body Same fields as create; all optional on update. See the request body table below for field requirements.. #### Response `200` Returns the updated `TicketTypeResource`. ## Not implemented * `DELETE /api/v1/{company}/event/{organizingEvent}/ticket-types/{ticketType}` — route is commented out ## Related * [Authentication](../authentication.md) * [Conventions](../conventions.md) * [Events](events.md) * [Orders](orders.md) # User Source: https://docs.animo.co/endpoints/user # User ## Overview Returns the profile of the authenticated user associated with the bearer token. ## Endpoints ### Get authenticated user * **Method:** `GET` * **Path:** `/api/v1/user` * **Scope:** `user:read` * **Subscription:** None (global endpoint) #### Path parameters None. #### Query parameters None. #### Response `200` ```json theme={null} { "data": { "id": "u3Ab1", "avatar": "https://example.com/avatar.jpg", "first_name": "Alex", "job_title": "Product Manager", "last_name": "Rivera", "linkedin_url": "https://linkedin.com/in/alex", "email": "alex@example.com", "created_at": "2025-01-15T10:30:00.000000+00:00" } } ``` #### Response fields | Field | Type | Description | | -------------- | ------------ | -------------------------- | | `id` | string | User sqid | | `avatar` | string\|null | Avatar URL | | `first_name` | string | First name | | `job_title` | string\|null | Job title | | `last_name` | string | Last name | | `linkedin_url` | string\|null | LinkedIn profile URL | | `email` | string | Email address | | `created_at` | string | Account creation timestamp | #### Errors | Status | When | | ------ | ------------------------------- | | `401` | Missing or invalid token | | `403` | Token missing `user:read` scope | ## Related * [Authentication](../authentication.md) * [Conventions](../conventions.md) * [Companies](companies.md) # Webhooks Source: https://docs.animo.co/endpoints/webhooks # Webhooks ## Overview Webhooks let your application receive real-time HTTP notifications when events occur in Animo (new submissions, lead changes, meeting bookings, etc.). Use the API to register, update, and remove webhook endpoints. This documentation covers webhook **management** endpoints only (register, update, remove). Event delivery is handled by Animo when events occur. ## Prerequisites * Company subscription must include `USE_INTEGRATIONS`. * Token must include at least one `*:subscribe` scope matching the events you want to receive. ## Webhook events | Event value | Scope required | Description | | ---------------------- | ----------------------- | ------------------------- | | `activation-submitted` | `activations:subscribe` | New activation submission | | `form-submitted` | `submissions:subscribe` | New form submission | | `lead-created` | `leads:subscribe` | Lead created | | `lead-updated` | `leads:subscribe` | Lead updated | | `lead-deleted` | `leads:subscribe` | Lead deleted | | `meeting-booked` | `meetings:subscribe` | Meeting booked | | `meeting-updated` | `meetings:subscribe` | Meeting updated | | `meeting-cancelled` | `meetings:subscribe` | Meeting cancelled | You can only subscribe to events permitted by the subscribe scopes on your token. ## Endpoints ### Attach webhook * **Method:** `POST` * **Path:** `/api/v1/{company}/webhooks/attach` * **Scope:** At least one of `activations:subscribe`, `submissions:subscribe`, `leads:subscribe`, `meetings:subscribe` * **Subscription:** `USE_INTEGRATIONS` #### Request body ```json theme={null} { "name": "My integration", "url": "https://example.com/webhooks/animo", "events": [ "activation-submitted", "lead-created", "form-submitted" ] } ``` | Field | Required | Description | | -------- | -------- | ------------------------------------------------------------ | | `name` | Yes | Unique name per user (max 255 chars) | | `url` | Yes | HTTPS callback URL (max 255 chars) | | `events` | Yes | Array of webhook event values (must match your token scopes) | #### Response `201` ```json theme={null} { "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "My integration", "token": "random32charSecretToken...", "url": "https://example.com/webhooks/animo", "events": [ "activation-submitted", "lead-created", "form-submitted" ], "created_at": "2026-06-01T12:00:00.000+00:00" } } ``` Store the `token` securely — it is required to update or detach the webhook. #### Response fields | Field | Type | Description | | ------------ | ------ | ------------------------------ | | `id` | string | Webhook UUID | | `name` | string | Webhook name | | `token` | string | Secret token for update/detach | | `url` | string | Callback URL | | `events` | array | Subscribed event values | | `created_at` | string | ISO 8601 timestamp | *** ### Update webhook * **Method:** `PATCH` * **Path:** `/api/v1/{company}/webhooks/update` * **Scope:** Matching subscribe scope(s) for any events being set * **Subscription:** `USE_INTEGRATIONS` #### Request body At least one of `name`, `url`, or `events` must be provided (along with `id` and `token`). ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440000", "token": "random32charSecretToken...", "url": "https://example.com/webhooks/animo-v2" } ``` | Field | Required | Description | | -------- | -------- | --------------------------------- | | `id` | Yes | Webhook UUID | | `token` | Yes | Secret token from attach response | | `name` | No | New name | | `url` | No | New HTTPS URL | | `events` | No | New event array | #### Response `200` Returns the updated `WebhookResource`. #### Errors | Status | When | | ------ | ----------------------------------- | | `422` | Invalid token or validation failure | *** ### Detach webhook * **Method:** `DELETE` * **Path:** `/api/v1/{company}/webhooks/detach` * **Scope:** Any subscribe scope (token must be valid) * **Subscription:** `USE_INTEGRATIONS` #### Request body ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440000", "token": "random32charSecretToken..." } ``` #### Response `204` Empty body on success. ## Related * [Authentication](../authentication.md) * [Conventions](../conventions.md) * [Activations](activations.md) * [Forms](forms.md) # For ai agents Source: https://docs.animo.co/for-ai-agents # Animo for AI agents **Read this first — before writing any code.** This guide is for AI coding agents (Claude and others) asked to build, set up, or operate events with Animo. Its purpose is to keep you from rebuilding things Animo already does. Animo is a complete events platform: ticketing, payments (Mollie), VAT, invoicing, attendee CRM, lifecycle emails, approval workflows, QR check-in, and hosted event & registration pages all exist and are battle-tested. **Your job is to orchestrate and present — not to reimplement the platform.** If you read nothing else, read the contract below. *** ## The contract > **You are integrating with Animo, an events platform that already handles ticketing, payments (Mollie), VAT, invoicing, attendee CRM, confirmation/lifecycle emails, approval workflows, QR check-in, and hosted event & form pages. Orchestrate and present — do not rebuild these.** > > ### Never build > > Payment or checkout flows · VAT/tax calculation · invoicing · ticket issuance, QR codes, or door scanning · attendee or lead storage · confirmation/lifecycle emails · custom forms that submit into Animo. > > ### Decide with one question — *what is this thing?* > > | The thing is… | Rule | Who builds it | > | --------------------------------------------------------------------------------------- | ------------------------------------------------------- | ---------------------- | > | **Money** — tickets, payment, refunds, VAT, invoices | Always Animo-hosted. Link or embed. | Animo | > | **Data of record** — registrations, leads, sponsorship / waitlist / contact submissions | Animo-hosted form or activation, **embedded or linked** | Animo hosts, you frame | > | **Presentation** — marketing pages, schedules, speaker bios, calendars | Build freely; pull content via `GET` | You | > > ### Two supported modes > > 1. **Fully Managed** — you call the API to set up and operate; Animo hosts everything the attendee sees. **This is the default. Start here.** > 2. **Custom Site + Animo Checkout** — you build the marketing site; every money and data-of-record path links or embeds to Animo. > > ### Not supported — do not attempt > > Your own payment UI or Mollie integration · completing payment outside Animo's hosted checkout ("headless paid") · custom-built forms that POST submissions or leads into Animo · polling for leads/submissions. There is no submission/lead ingest endpoint and no checkout URL for completing payment yourself. **If a requirement seems to need one of these, stop and surface the limitation — do not code around it.** ### Supported patterns at a glance | Pattern | OK? | | ------------------------------------------------------------------------------------- | --- | | API setup + link to hosted ticket/checkout pages | ✅ | | API `orders/create` (paid ticket) **+ redirect attendee to** `/o/{order-id}/checkout` | ✅ | | API form definitions (`POST /forms/create`) + attach `form_id` to ticket types | ✅ | | Custom site + `GET` for display + links to Animo | ✅ | | Webhook receiver that persists events | ✅ | | Your own payment form or direct Mollie integration | ❌ | | Custom form POSTing submissions/leads into Animo | ❌ | | Polling the API for leads/submissions (no `GET` — use webhooks) | ❌ | **On paid registration:** creating an order via the API and sending the attendee to Animo's hosted checkout is *supported orchestration*, not "building checkout." Don't avoid `orders/create` for paid tickets — use it, then redirect to `/o/{order-id}/checkout`. What's forbidden is collecting payment yourself. "Headless paid orders" means *completing payment outside Animo's hosted checkout* — that path has no checkout URL, not API-driven paid registration. **On forms:** *defining* a form via the Forms API (`POST /forms/create`) and attaching it to a ticket type is supported and encouraged. *Collecting answers* on your own frontend and posting them into Animo is not — answers are captured on the hosted ticket/activation flow. ### Why the money path is off-limits Animo's price is a function of `ticket count × customer type (business vs. consumer) × country × online-vs-in-person`. That is tax law encoded in software. Reimplementing it produces invoices that are confidently wrong in ways nobody catches until one is disputed. The same logic extends to data of record: there is no API to write a submission or lead into Animo, so any "custom form that posts to Animo" is a fragile bridge across a gap that does not exist. **Hosted-and-linked is not a limitation to work around — it is the correct architecture.** *** ## Before you call the API First-contact gotchas documented elsewhere that bite agents who only read this file: * **Pro plan required.** Without it, `GET /api/v1/companies` returns `200` with an **empty** `data` array — not a `403`. An empty list means "no Pro plan," not "bad token." See [API index — Plan requirement](index.md#plan-requirement). * **`{company}` in API paths is a sqid; public URLs use slugs.** Don't interchange them — get the sqid from `GET /api/v1/companies`, build public URLs from the `slug` fields. See [Conventions — Public URLs](conventions.md#public-urls). * **Paid ticket public pages 404 until Mollie is connected.** A paid ticket type with no connected payment provider has no working signup page. * **Event `slug` is ignored on create** — it's derived from `name`. Use the `id` from the create response; `PATCH` the slug afterward if you need a specific one. *** ## The two supported modes ### Mode 1 — Fully Managed (default) You call the API; Animo hosts the entire attendee experience. * **You build:** no separate site. You orchestrate API calls to set up and run the event. You still shape what attendees see *through the API* — event page copy, ticket types, pricing, email templates — you just don't build a frontend. * **Animo owns:** event page, ticket shop, checkout, payments, forms, emails, approvals, scanning — everything. * **Best for:** private or marketing events, individuals, fast turnaround, anyone without a hard branding requirement. * **Attendees go to:** the hosted event page — `https://app.animo.co/{company-slug}/event/{event-slug}`. Start here. Only climb to Mode 2 when there is a **hard branding or domain requirement** the hosted page cannot meet. ### Mode 2 — Custom Site + Animo Checkout You build the marketing site on your own design and domain; every money and data-of-record path links or embeds to Animo. * **You build:** marketing/landing pages, schedules, speaker bios, a calendar — all *presentation*, populated via `GET`. * **Animo owns:** the entire money path (ticket pages, checkout, payments, VAT, invoices) and the entire data-of-record path (hosted forms/activations for sponsorship, waitlist, contact). * **Best for:** professional or paid public events that need a bespoke brand. * **How you connect to Animo:** * **Tickets** → link to the hosted ticket signup page: `https://app.animo.co/{company-slug}/event/{event-slug}/{ticket-type-slug}` (append `?qty=1` to pre-select quantity). See [Conventions — Public URLs](conventions.md#public-urls). * **Forms** (sponsorship, waitlist, contact) → embed or link the **Animo-hosted activation**. Get the activation `slug` from `GET /api/v1/{company}/activations` and link to the company-scoped URL `https://app.animo.co/{company-slug}/{activation-slug}` (the default — always works for published activations). Link to that **base URL, not `/form`** — `/form` is where attendees land *after* sign-up, not a cold-traffic entry point. See [Conventions — Activation form page](conventions.md#activation-form-page) for the event-scoped variant. Do **not** build a custom form that posts into Animo — see [Known API gaps](index.md#known-api-gaps). The API does not return public URLs in responses; build them from slugs per [Conventions — Public URLs](conventions.md#public-urls). *** ## What the AI's job actually is Setup is only one of several jobs. These are orthogonal to the two modes above — any of them applies on top of either mode. Most real engagements are one or two of these, not all five. ### Operate — ongoing, no building (highest value, lowest cost) The AI as an **ops console**, not a builder. Approve or reject pending registrations, monitor sign-ups, pull attendee lists, shift event dates, swap copy on pages and emails. This is the cheapest and most common mode — reach for it before assuming a setup or build task. * Read events, ticket types, orders. * `PATCH` events (dates, details), ticket types, orders (approve/reject). * `PATCH` comms templates to update email copy; `PATCH`/`POST` event pages to update content. Customize Animo's templates — don't send parallel lifecycle emails from your own app; Animo already sends them. ### Provision — one-time setup Create events, ticket types, forms, and pages from a spec. Build the structure, attach forms to ticket types, set up comms templates, then hand off to Operate. ### Integrate — webhook-driven On `form-submitted`, `activation-submitted`, `lead-created`, `meeting-booked`, etc., route the event to Slack / Sheets / CRM / email. The AI builds a thin **receiver**, not a site. > **Important:** there is no `GET` backfill for leads, submissions, or meetings. Webhooks are the *only* way to obtain this data — you **must persist** every webhook event when it arrives. See [Webhooks](endpoints/webhooks.md). ### Sync / pull — Animo as source of truth → your site The inverse of Provision: read events, ticket types, and pages, then generate or refresh a static site, calendar, or directory from them. Great for multi-event calendars. ### Bulk / templated — one spec → many events Recurring series, multi-vertical programs, franchise/decentralized editions. One spec fans out to many events via repeated Provision calls. *** ## Prompt recipes Copy-paste starting prompts for each job. Adjust names, dates, and the company sqid. ### Operate — approve pending registrations ``` Using the Animo API for company {sqid}, list pending orders for the organizing event "{event}". Show me each registrant's name and ticket type, and approve the ones from a company domain. Don't reimplement any email — Animo sends the approval email. ``` ### Provision — set up a new event ``` Create an event "{name}" on {date} in Animo for company {sqid}. Add ticket types: {list}. For any paid ticket, the attendee pays through Animo's hosted checkout — do not build a payment flow. Give me the hosted event URL and the ticket signup URLs when done. ``` ### Custom site — link out to Animo ``` Build a marketing landing page for "{event}" with our branding. Pull the schedule and ticket types from the Animo API. For tickets, link to the hosted Animo ticket pages — do not build a cart or checkout. For the sponsorship enquiry, embed/link our Animo activation form. ``` ### Integrate — webhook receiver ``` Register an Animo webhook for {events} pointing at {url}. Then build a receiver that persists every event to {store} — there is no GET backfill, so nothing should depend on re-fetching this data from the API. ``` ### Sync — refresh a calendar ``` Read all events and ticket types for company {sqid} from the Animo API and regenerate our events calendar page. Animo is the source of truth; the page is read-only presentation. ``` *** ## Capability map & known gaps What the API supports today, and the edges not to wander into. Always treat the [endpoint docs](index.md#endpoints) and [Conventions](conventions.md) as the source of truth. | Capability | Status | | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | | Read user, companies, events, activations, ticket types, orders, forms | ✅ `GET` | | Create/update events, ticket types, forms, event pages, comms templates | ✅ | | Create/update orders (free tickets) | ✅ | | Create paid orders, then redirect attendee to hosted checkout (`/o/{order-id}/checkout`) | ✅ | | Approve/reject orders, shift dates, edit copy (Operate) | ✅ `PATCH` | | Register webhooks for submissions, leads, meetings | ✅ | | **Payment collection / VAT / invoicing** | 🚫 Animo-hosted only — never build | | **Completing payment outside hosted checkout** ("headless paid") | 🚫 No checkout URL for this — always redirect to `/o/{order-id}/checkout` | | **Custom form that posts into Animo** | 🚫 No submission/lead ingest endpoint — embed/link the hosted activation instead | | **`GET` leads / submissions / meetings** | 🚫 Not exposed — use webhooks and persist | | `orders/create` `form` payload | ⚠️ Not yet persisted server-side; send order-level fields only | | Delete events / ticket types / orders / forms | ⚠️ Routes missing — cannot delete via API | For the full, current list see [API index — Known API gaps](index.md#known-api-gaps) and the [Changelog](changelog.md). *** ## Related * [API index](index.md) — API overview and endpoint index * [Authentication](authentication.md) — tokens and scopes * [Conventions](conventions.md) — IDs, dates, errors, public URLs * [Webhooks](endpoints/webhooks.md) — outbound events (the only path to lead/submission data) * [Changelog](changelog.md) — version history and known limitations # Animo API (v1) Source: https://docs.animo.co/index REST API for events, forms, orders, activations, and webhooks on Animo. The Animo REST API lets external applications read and manage events, forms, orders, activations, and webhooks on behalf of Animo users. **Base URL:** `https://app.animo.co/api/v1` ## For AI assistants * [llms.txt](/llms.txt) — documentation index (start here) * [llms-full.txt](/llms-full.txt) — full corpus in one file * [For AI agents](for-ai-agents.md) — integration contract (what to orchestrate vs. never rebuild) ## Plan requirement API access requires an active **Pro** plan on the company you want to integrate with. Upgrade or manage your subscription at: **[https://app.animo.co/admin/settings/billing](https://app.animo.co/admin/settings/billing)** Without a Pro plan, `GET /api/v1/companies` returns `200` with an **empty** `data` array (not `403`), and direct calls to company-scoped endpoints return `403`. Webhooks additionally require the Integrations feature (included on Pro and above). All endpoints require a valid OAuth2 bearer token unless noted otherwise. See [Authentication](authentication.md) for how to obtain and use tokens. ## Quick start 1. **Create a personal access token** in the Animo admin panel (User settings → API tokens). Select the scopes your integration needs. 2. **List your companies** to get a company sqid: ```http theme={null} GET /api/v1/companies Authorization: Bearer {token} ``` 3. **Call a company-scoped endpoint** using the company sqid from step 2: ```http theme={null} GET /api/v1/{company}/events Authorization: Bearer {token} ``` ## Documentation | Topic | Description | | --------------------------------------- | ------------------------------------------------------------------------------ | | [Business case](business-case.md) | Cost comparison — fully managed vs. custom site vs. building your own | | [Animo for AI agents](for-ai-agents.md) | Integration guide for AI coding agents — what to orchestrate vs. never rebuild | | [Authentication](authentication.md) | OAuth2, bearer tokens, scopes | | [Conventions](conventions.md) | IDs, dates, pagination, errors, subscriptions | | [Changelog](changelog.md) | API version history | ### Endpoints | Resource | Description | | ----------------------------------------- | -------------------------------------------------------------- | | [User](endpoints/user.md) | Authenticated user profile | | [Companies](endpoints/companies.md) | Companies available to the user | | [Activations](endpoints/activations.md) | Lead-generation activations | | [Events](endpoints/events.md) | Events and cohosts | | [Event pages](endpoints/event-pages.md) | Public event page content (tagline, description, header image) | | [Comms templates](endpoints/comms.md) | Registration email templates | | [Ticket types](endpoints/ticket-types.md) | Ticket types for organizing events | | [Orders](endpoints/orders.md) | Event orders (tickets) | | [Forms](endpoints/forms.md) | Company forms and fields | | [Webhooks](endpoints/webhooks.md) | Outbound webhook subscriptions | ## Not yet available The following are defined in code but **not exposed** via the API today: * `GET` endpoints for leads, submissions, and meetings (scopes exist: `leads:read`, `submissions:read`, `meetings:read`) * `DELETE /api/v1/{company}/events/{event}` (event delete) * `DELETE` ticket types * `DELETE /api/v1/{company}/event/{organizingEvent}/orders/{order}/cancel` (order cancel) * `DELETE /api/v1/{company}/forms/{form}` (form delete — `forms:delete` scope exists but no route) ## Known API gaps Issues discovered during live integration testing. These are API/code fixes, not doc typos: | Issue | Impact | Workaround | | -------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | Event `slug` ignored on create | Event `id` is derived from `name`, not your supplied slug | Use the `id` in the create response; set slug via `PATCH` if needed | | No `public_url` in resources | Must string-build ticket/event URLs | See [Conventions — Public URLs](conventions.md#public-urls) | | `OrderResource` omits payment URL | Paid order checkout link not in JSON | Build checkout URL from order `id` — see [Orders](endpoints/orders.md) | | `orders/create` `form` payload | Form answers not yet processed server-side | Only send order-level fields for now | | No submission/lead ingest endpoint | Custom-built forms cannot `POST` into Animo; activations are read-only | Embed/link the Animo-hosted activation form; receive data via webhooks | | `ActivationResource` omits linked event slug | Can't build the event-scoped activation URL from `GET /activations` alone | Use the company-scoped URL (always works), or fetch the event slug from `GET /events` — see [Conventions](conventions.md#activation-form-page) |