> ## Documentation Index
> Fetch the complete documentation index at: https://swapnice.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Events and intent ontology

> How to shape partner activity into Swapnice events, claims, and intent categories.

Events are the main ingestion surface. Swapnice stores them as **claims**, not as destructive overwrites of a profile field. Resolved [outputs](/partners/outputs) are computed from those claims.

## What every event must carry

| Field                                    | Role                                                             |
| ---------------------------------------- | ---------------------------------------------------------------- |
| `customer_id`                            | Partner customer the event belongs to                            |
| `event_type`                             | What happened (`purchase.completed`, `collection.interacted`, …) |
| `ontology` / `ontology_category`         | Which intent family this is                                      |
| `object`                                 | What the user showed intent toward (SKU, series, event, creator) |
| `consent_purpose` + `consent_receipt_id` | Why you are allowed to send it                                   |
| `occurred_at`                            | When it happened                                                 |
| `payload`                                | Partner attributes (amount, sku, screen)                         |
| `context`                                | Surface, storefront, platform                                    |
| `confidence`                             | 1.0 for a completed purchase; lower for inferred views           |
| `provenance`                             | Who collected it, in which system, when                          |
| `Idempotency-Key`                        | Header so retries do not duplicate                               |

Omit secrets, emails, and payment tokens from `payload` and `context`.

## Intent categories

These five categories are the ontology profile and intent reads speak.

| Category     | Meaning                                                   | Strong signals                                      | Weaker signals                |
| ------------ | --------------------------------------------------------- | --------------------------------------------------- | ----------------------------- |
| `outcome`    | Intent to purchase, participate, or complete a collection | `purchase.completed`, ticket scanned, set completed | Add-to-cart, checkout started |
| `sentiment`  | Preference or satisfaction                                | Explicit thumbs-up / rating                         | Dwell time                    |
| `affiliate`  | Self-label or group association                           | "I collect Yu-Gi-Oh", team follow                   | Repeated category browse      |
| `habit`      | Repeated interest or activity                             | Nth view of the same series                         | First impression              |
| `activation` | Ready for the next journey step                           | Finished onboarding step, claimed offer             | Saw the prompt                |

Designed finer types you can map into those categories:

* `outcome.participate`
* `outcome.purchase`
* `outcome.complete_collection`
* `sentiment.positive` / `sentiment.negative`
* `affiliate.self_label` / `affiliate.group_association`
* `habit.repeated_interest` / `habit.repeated_activity`
* `activation.ready_for_next_step`

V1 accepts `ontology_category` as one of `outcome`, `sentiment`, `affiliate`, `habit`, `activation`. Put the finer type in `event_type` or `payload` if you need it.

## Suggested event catalog

Use this as the first mapping workshop with Swapnice. You do not need all of them for a pilot.

| Partner action               | `event_type`            | Category     | Typical `object` | Typical purpose   |
| ---------------------------- | ----------------------- | ------------ | ---------------- | ----------------- |
| User picks a favorite series | `profile.selected`      | `affiliate`  | series id        | `personalization` |
| Views a collection or SKU    | `collection.interacted` | `habit`      | series or SKU    | `personalization` |
| Completes a purchase         | `purchase.completed`    | `outcome`    | SKU              | `personalization` |
| Attends or checks in         | `event.participated`    | `outcome`    | event id         | `personalization` |
| Completes a set              | `collection.completed`  | `outcome`    | set id           | `personalization` |
| Rates or hearts an item      | `sentiment.recorded`    | `sentiment`  | SKU              | `personalization` |
| Finishes a journey step      | `activation.ready`      | `activation` | step id          | `personalization` |

Pilot minimum: one explicit claim (`profile.selected`) and one behavioral conversion (`purchase.completed`).

## Example events

<Tabs>
  <Tab title="Explicit preference">
    ```json theme={null}
    {
      "customer_id": "cust_8f2a1c",
      "client_event_id": "evt_pref_1",
      "consent_receipt_id": "cr_44aa01",
      "consent_purpose": "personalization",
      "event_type": "profile.selected",
      "ontology": "intent",
      "ontology_category": "affiliate",
      "object": "yugioh",
      "occurred_at": "2026-08-26T16:03:00.000Z",
      "payload": { "attribute": "favorite_series", "value": "yugioh" },
      "context": { "surface": "onboarding" },
      "confidence": 0.92,
      "provenance": {
        "source_partner": "partner_app",
        "source_system": "ios",
        "collected_at": "2026-08-26T16:03:00.000Z"
      }
    }
    ```
  </Tab>

  <Tab title="Purchase">
    ```json theme={null}
    {
      "customer_id": "cust_8f2a1c",
      "client_event_id": "evt_checkout_8891",
      "consent_receipt_id": "cr_44aa01",
      "consent_purpose": "personalization",
      "event_type": "purchase.completed",
      "ontology": "intent",
      "ontology_category": "outcome",
      "object": "sku_blue_eyes_tin",
      "occurred_at": "2026-08-26T16:04:00.000Z",
      "payload": { "sku": "sku_blue_eyes_tin", "currency": "USD", "amount": 24.99 },
      "context": { "surface": "checkout", "storefront": "ios" },
      "confidence": 1,
      "provenance": {
        "source_partner": "partner_app",
        "source_system": "ios",
        "collected_at": "2026-08-26T16:04:00.000Z"
      }
    }
    ```
  </Tab>

  <Tab title="Repeated browse">
    ```json theme={null}
    {
      "customer_id": "cust_8f2a1c",
      "client_event_id": "evt_view_441",
      "consent_receipt_id": "cr_44aa01",
      "consent_purpose": "personalization",
      "event_type": "collection.interacted",
      "ontology": "intent",
      "ontology_category": "habit",
      "object": "ygo_series_001",
      "occurred_at": "2026-08-26T16:05:00.000Z",
      "payload": { "screen": "series_detail" },
      "confidence": 0.72,
      "provenance": {
        "source_partner": "partner_app",
        "source_system": "ios",
        "collected_at": "2026-08-26T16:05:00.000Z"
      }
    }
    ```
  </Tab>
</Tabs>

## Claims, not overwrites

If two sources disagree, both claims stay. The profile picker chooses a winner and keeps alternatives.

Resolution today: **highest confidence, then most recent**. Designed rules that later resolution will also honor:

* Explicit user-declared signals outrank inferred behavior
* Recency matters unless an older explicit signal is still active
* Higher-trust sources outrank lower-trust sources
* Truth can be context-specific ("favorite for apparel" ≠ "favorite for collectibles")
* A claim is only usable for purposes still granted
* Revoke, delete, and correction requests remove that claim from purpose-scoped reads

That is why the API returns `resolution_method`, `evidence`, and `alternatives`. Partners can see *why* a fact won.

## Ordering, idempotency, and async

* Events for one `app_id` + `customer_id` are FIFO through a single coordination object
* Replaying the same `Idempotency-Key` returns the original `event_id`
* A reused key with a different body returns `409 idempotency_conflict`
* `POST /v1/events` returns `202` with `processing_status: queued`
* Profile reads see the event after archive projection. If a profile is empty, wait and retry `GET /v1/events/:id` until `processed`

Batch: `POST /v1/events/batch` accepts 1–100 of the same shape.

## Confidence guidance

| Signal                                                 | Suggested `confidence`                                       |
| ------------------------------------------------------ | ------------------------------------------------------------ |
| Completed purchase, ticket scan, explicit profile pick | `1.0` or `0.9+`                                              |
| Repeated, same-object engagement                       | `0.7`–`0.85`                                                 |
| Single view or weak inference                          | `0.4`–`0.6`                                                  |
| Do not send                                            | Guesswork, scraped third-party data, another partner's users |

Garbage confidence makes profile reads harder to trust. Prefer fewer, cleaner events in the pilot.

## Catalog objects

When the object is a card, SKU, or collectible, upsert it as an [entry](/partners/catalog) and use that id in `object`. Intents then attach to a shared catalog instead of free-text names.
