# Product API quickstart

Find a contact in the unified audience, read their recent events and send a direct SMS — end to end in under five minutes.

**Language:** en
**Audience:** developer
**TLDR:** Authenticate with a Product API token as a Bearer header against https://api.instasent.com/v1/project/{project}, scoped to PROJECT_AUDIENCE_READ, PROJECT_AUDIENCE_DATA_BASIC/EVENTS and PROJECT_DIRECT_WRITE. Verify it with GET /v1/project/{project} (200 = live token), then resolve a contact via GET /audience/search/phone/{phone} (or /search/email/{email}, /user/{userId}) to get the audience id used for events and direct-send calls.
**Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt
**This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/platform-api/product-api/llms-full.txt
**This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/platform-api/product-api/quickstart/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/platform-api/product-api/quickstart.md (Markdown)

This walkthrough exercises the three things every Product API integration eventually does: locate a contact, inspect their timeline, and trigger a direct message. Budget five minutes.

## Before you start

#### 1. Have a project with data

Sign in to the [dashboard](https://dashboard.instasent.com) and pick a project that already has at least one audience contact (either pushed via Ingest or imported via a data source). If your project is empty, walk through the [Ingest Quickstart](/platform-api/ingest-api/quickstart) first.

#### 2. Create a Product API token

Open **Project settings** → **API tokens** and create a token with at least these scopes:

- `PROJECT_AUDIENCE_READ`
- `PROJECT_AUDIENCE_DATA_BASIC` (to see phone and email on responses)
- `PROJECT_AUDIENCE_DATA_EVENTS` (to read events)
- `PROJECT_DIRECT_WRITE` (to send direct SMS)

#### 3. Export credentials

```bash
export INSTASENT_PROJECT="proj_xxx"
export INSTASENT_TOKEN="eyJhbGciOi..."
export BASE="https://api.instasent.com/v1/project/$INSTASENT_PROJECT"
```

## 1. Verify the token

`GET /v1/project/{project}` returns the project's metadata and is the cheapest probe for credentials.

```bash
curl "$BASE" \
  -H "Authorization: Bearer $INSTASENT_TOKEN"
```

A `200` confirms the token is live and the project UID resolves. A `401` means the token is wrong; a `404` means the project UID is wrong or not visible to the token.

## 2. Find an audience contact

The audience exposes three search helpers for the common identifier types — user id, phone and email. They return the unified audience contact so you can then read events or send messages.

```bash
curl "$BASE/audience/search/phone/%2B34600000000" \
  -H "Authorization: Bearer $INSTASENT_TOKEN"
```

Response (trimmed):

```json
{
  "entity": {
    "id": "uQTuHNBKLdwTxzGldW5pocUNqzyz-066",
    "_user_id": "USER-123",
    "_first_name": "Ada",
    "_email": "ada@example.com",
    "_phone_mobile": "+34600000000"
  }
}
```

Copy the returned `id` — that is the **audience contact id** you need for the next two calls.

```bash
export AUDIENCE_ID="uQTuHNBKLdwTxzGldW5pocUNqzyz-066"
```

> **Tip**: Phone numbers in the path must be URL-encoded (`+` → `%2B`). If you store user ids instead, use `/audience/user/{userId}`; for email use `/audience/search/email/{userEmail}`.

## 3. Read the contact's recent events

```bash
curl "$BASE/audience/$AUDIENCE_ID/events" \
  -H "Authorization: Bearer $INSTASENT_TOKEN"
```

You get the last events for that contact — purchases, message deliveries, clicks, custom events. Use this to confirm your Ingest pipeline is landing and to drive application logic that depends on the customer timeline.

## 4. Send a direct SMS

The direct SMS endpoint takes the sender and audience id in the path and the text in the body. Use `"default"` as the sender id to fall back to the project's default sender.

```bash
curl -X POST "$BASE/channel/sms/sms/direct/default/$AUDIENCE_ID" \
  -H "Authorization: Bearer $INSTASENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Hi Ada — your order is on its way. Track it at {{short:https://track.example.com/O-987}}"
  }'
```

A `201 Created` returns the SMS entity with its `id`, `status` (`enqueued` initially), `encoding`, `messagesCount`, `pricePerSms` and the `audienceId` the message was dispatched to. Status transitions are pushed to your DLR webhook the same way transactional traffic is — see [Transactional DLRs](/a2p-messaging-api/http/dlrs).

> **Warning**: The direct SMS endpoint supports `{{short:URL}}` for automatic link-shortening and `{{unsubscribe}}` for an opt-out link. It is designed for individual triggered messages, not bulk campaigns — those belong in a campaign or automation.

## 5. (Optional) send by phone instead of audience id

If the contact does not yet exist and your project has **outbound auto-creation** enabled, you can send straight to a phone number and let the API create the audience contact for you:

```bash
curl -X POST "$BASE/channel/sms/sms/direct/default/%2B34600000000" \
  -H "Authorization: Bearer $INSTASENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "text": "Welcome to Example Co." }'
```

The response's `metadata.autoCreated` is `true` when a new audience contact was created for the message. If auto-creation is disabled and the phone number is not found, the call returns `404`.

## 6. Check what's left to configure

Two read-only endpoints answer "is this project ready to send?" — handy for onboarding screens or a pre-flight check before you start sending. Both are recomputed on every call: there is no cached "done" flag, so a completed step re-opens if its condition regresses. The separate question — what has actually broken — is [attention](/platform-api/product-api/project-setup/attention).

`GET /v1/project/{project}/readiness` returns the project-wide setup report: an `operational` flag, a `percent` complete, and a list of `steps` (import contacts, set up channels, complete the legal profile, cover the balance). Each step carries a stable `key` — the API ships no copy — plus `completed`, `isBlocker`, `disabled`, `skipped` and `skippable`.

```bash
curl "$BASE/readiness" \
  -H "Authorization: Bearer $INSTASENT_TOKEN"
```

Response (trimmed):

```json
{
  "entity": {
    "scope": "project",
    "channel": null,
    "percent": 75,
    "operational": false,
    "steps": [
      { "key": "import", "completed": true, "isBlocker": true, "disabled": false, "skipped": false, "skippable": true },
      { "key": "channels", "completed": true, "isBlocker": false, "disabled": false, "skipped": false, "skippable": true },
      { "key": "legal-profile", "completed": false, "isBlocker": false, "disabled": false, "skipped": false, "skippable": true },
      { "key": "funds", "completed": false, "isBlocker": true, "disabled": false, "skipped": false, "skippable": true }
    ],
    "warnings": [],
    "reach": null
  }
}
```

`GET /v1/project/{project}/readiness/channel/{channel}` narrows the report to a single channel (`sms` or `rcs`; any other value returns `404`). On top of channel-specific steps (`add-sender` for SMS, `add-agent` for RCS) it surfaces `warnings` — today just `recommended-registrations`, the countries a sender ought to be registered for and is not — and `reach`, the countries the channel can send to right now.

```bash
curl "$BASE/readiness/channel/sms" \
  -H "Authorization: Bearer $INSTASENT_TOKEN"
```

Response (trimmed):

```json
{
  "entity": {
    "scope": "channel",
    "channel": "sms",
    "steps": [
      { "key": "add-sender", "completed": true, "isBlocker": true, "disabled": false, "skipped": false, "skippable": true }
    ],
    "warnings": [],
    "reach": {
      "reachableCountries": ["ES", "FR", "PT"],
      "senders": { "ACME": ["ES", "PT"], "INFO": ["FR"] },
      "senderCount": 2,
      "defaultSender": "ACME"
    }
  }
}
```

> **Tip**: On a channel, `operational` also requires a non-empty `reach`: holding a sender is not the same as being able to deliver with it. The full field-by-field reference is in [Project readiness](/platform-api/product-api/project-setup/readiness) and [Channel readiness](/platform-api/product-api/project-setup/channel-readiness).

## 7. Count your reachable audience

Readiness tells you the channel is set up; a count tells you whether there is anyone to send to. `POST /v1/project/{project}/audience/count` returns only a total, and `filterCompliance` makes that total mean *contacts an SMS campaign under this consent policy would reach* instead of *contacts in the project*.

```bash
curl -X POST "$BASE/audience/count" \
  -H "Authorization: Bearer $INSTASENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "filterCompliance": { "sms": "no-opt-out" } }'
```

Response:

```json
{
  "metadata": {
    "totalHits": 452
  }
}
```

Swap the policy for `basic` or `opt-in` to compare what each one reaches, add a `root` to count a segment or a set of conditions, and read the project's own policy from `generalConfig.channelSms.defaultCompliancePolicy` in step 1's response. The full key is in [Counting who can receive](/platform-api/product-api/audience/query-filter#counting-who-can-receive-filtercompliance).

## What to read next

- [Guide](/platform-api/product-api/guide) - Mental model, entities and how the unified audience is built.
- [Audience query filter](/platform-api/product-api/audience/query-filter) - Search and segment the unified audience.
- [Authentication](/platform-api/product-api/authentication) - Token types, scopes and rotation.
- [Full API Reference](/platform-api/product-api/reference) - Every endpoint, every parameter.

---

This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context.
