# Guide

The Ingest API imports contacts and events into an Instasent project so every downstream surface — audiences, campaigns, analytics — sees the same source of truth.

**Language:** en
**Audience:** developer

The Ingest API is the door that customer data walks through on its way into Instasent. Every contact you create, every event you record, lands in a project as a **datasource** and feeds the audience that your campaigns, segments and analytics read from.

It is a focused subset of the [Product API](/developers/product-api/guide): three write endpoints for contacts and events, plus a handful of read endpoints for validating your payloads against the datasource specs. If you already run Product API calls, the same tokens work here — see [Authentication](/developers/ingest-api/authentication).

## When to reach for it

Pick the Ingest API when an **external system of record** owns your customer data and Instasent needs a steady feed of it:

- **CRM or e-commerce syncs** — push new and updated contacts as your source platform mutates them.
- **Product event streams** — record purchases, sign-ups, page views or any custom interaction you want to segment on.
- **Backfills** — load historical data into a fresh project before flipping campaigns on.

If instead you need to **read back** an audience, trigger a campaign, or orchestrate messaging, that is the Product API's job.

## Mental model

Three nouns and one rule of thumb:

- **Contacts** — the people in your audience. Identified by a `_user_id` that is unique within your datasource. Sending the same `_user_id` again updates the existing record.
- **Events** — time-stamped interactions tied to a contact (`purchase`, `viewed_product`, `reservation_made`, anything you define). Events are **immutable** once accepted.
- **Datasource** — the logical stream your API token writes to. A project can have several datasources, and when two disagree about the same contact, the Ingest datasource wins.

The rule of thumb: **treat Ingest writes as idempotent for contacts and append-only for events.** Your client should be safe to retry contact uploads; event uploads must use a stable `_event_id` so duplicates are discarded on our side.

## A day in the life of a contact

#### 1. Your CRM emits a change

A new customer signs up, or an existing one updates their phone number. Your integration catches the change event.

#### 2. POST /stream/contacts

Send the contact — `_user_id`, identity fields, any custom attributes — as a single-item array. Omit attributes you do not own; nothing you leave out gets overwritten.

#### 3. Instasent merges the record

The datasource is updated synchronously; the audience view is refreshed within seconds.

#### 4. Later: POST /stream/events

The same customer places an order. You POST a `purchase` event with `_user_id` and `_event_parameters`. The event lands on the contact's timeline and becomes queryable for segmentation.

## Shape of the API

All endpoints live under `https://api.instasent.com/v1/project/{project}/datasource/{datasource}/`:

| Endpoint                           | Purpose                                                                               |
| ---------------------------------- | ------------------------------------------------------------------------------------- |
| `POST /stream/contacts`            | Create or update up to 100 contacts per call.                                         |
| `DELETE /stream/contacts/{userId}` | Permanently remove a contact.                                                         |
| `POST /stream/events`              | Record up to 100 events per call.                                                     |
| `GET /stream`                      | Stream metadata and quota — also the simplest auth probe.                             |
| `GET /stream/specs/*`              | Discover the attributes, event types and per-event parameters the datasource accepts. |

Per-parameter detail lives in the [API Reference](/developers/ingest-api/reference).

## How contacts are merged

Attributes in a contact payload follow a few simple rules:

- **Omitted attribute** → existing value kept. Send partial updates without fear.
- **Explicit `null`** → attribute cleared.
- **Same `_user_id` across datasources** → contacts merge automatically into one audience record, and Ingest values take precedence when they collide.

> **Tip**: Custom attributes are first-class citizens — just send them alongside the reserved `_`-prefixed ones. Validate the names against [`/stream/specs/attributes`](/developers/ingest-api/reference) during integration to catch typos before they create new columns.

## How events flow

Two shapes for `POST /stream/events`:

- **Event-only** — just `_user_id`, `_event_id`, `_event_type` and `_event_parameters`. Skipped silently if the contact does not exist.
- **Event with embedded contact** — add `_user_data` to create or update the contact in the same call. Saves a round-trip when your source system can produce both at once.

`_event_id` must be stable and unique per event. Resend the same id and we drop the duplicate — that is what makes safe retries possible.

## Automations and the event time window

Every event you send is stored on the contact's timeline regardless of its date, but **only events whose `_event_date` is close to reception time trigger automations**. The Ingest API applies a symmetric window of **±1 hour** around the moment we receive the request:

```
abs(_event_date − reception_time) ≤ 1 hour
```

Events outside that window are accepted, persisted and queryable for segmentation — they just do not fire any automation. This is a safeguard: without it, a single backfill of historical orders, a producer with clock drift, or a queue replayed after a long stall could enqueue thousands of SMS, emails and other messages for things that already happened.

In practice:

- **Omit `_event_date`** when the event is happening "now" — it defaults to the current time and is guaranteed to be inside the window.
- **Send `_event_date`** only when you genuinely need a specific timestamp, and make sure it is within ±1h of reception.
- **Backfills** of historical events for segmentation or analytics should be sent with their real dates. Automations will not fire for them — by design.

> **Warning**: A future-dated `_event_date` (for example, a producer whose clock is two hours ahead) silently skips automations. If your automations stop firing but events still land on the timeline, check the clock of whatever system is generating `_event_date`.

## What to read next

- [Quickstart](/developers/ingest-api/quickstart) - Load your first contact and event in under five minutes.
- [Authentication](/developers/ingest-api/authentication) - Datasource tokens, Product API tokens, rotation.
- [Errors](/developers/ingest-api/errors) - Status codes and the partial-success response shape.
- [API Reference](/developers/ingest-api/reference) - Every endpoint, every parameter.
