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

# Missions API

> Read your team's SalesOS mission progress from your own backend — daily objectives, completion counters and the next mission to surface, built for super-app home screens and partner dashboards.

# Missions API

<Note>
  Examples below show the wire-format `Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE` header. To compute the signature in your code, use the `signedRequest` helper in [Authentication](/api/authentication).
</Note>

The Missions API lets your backend read the **gamification mission state** of your salespeople in SalesOS — which objectives are open today, how far along each one is, what was already completed, and which mission to surface next. It was built for **composing your own app screens** (for example, a home widget showing "3 of 6 missions done") before the user ever opens the SalesOS module.

It is a **read-only, server-to-server** API: you look collaborators up by CPF or email, in batch, and SalesOS answers with the missions whose period window contains today, computed in your company's timezone.

## How It Works

1. **You get an API Key** with the `missions:read` scope (Admin > Integrations > API Keys)
2. **Your backend asks** for the mission state of one or many collaborators (by CPF or email)
3. **SalesOS answers** with today's missions per collaborator — progress counters, the full list, and the next active mission

<Note>
  Missions are **advanced** by activity inside SalesOS: the engine listens to events (a visit scheduled, a sale closed) and increments the matching mission. This API is for **reading** that state — pair it with a deep link into the SalesOS module for the action itself.
</Note>

<Warning>
  This endpoint never creates missions. If a collaborator's daily missions have not been provisioned yet, the answer is an empty list with `progress.total = 0` — not an error, and not a silent write.
</Warning>

***

## Authentication

All requests require an API Key in the `Authorization` header:

```
Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE
```

See the [Authentication page](/api/authentication) for details on creating and managing API Keys.

| Property           | Details                                                  |
| ------------------ | -------------------------------------------------------- |
| **Header**         | `Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE` |
| **Scope required** | `missions:read`                                          |
| **Rate limit**     | Configurable per key (default: 1000 requests/hour)       |
| **Key format**     | `sk_live_` (production) or `sk_test_` (testing)          |

***

## Environments

<Tabs>
  <Tab title="Production">
    **Base URL:** `https://api.play2sell.com`
  </Tab>

  <Tab title="Staging">
    **Base URL:** `https://api-staging.play2sell.com`
  </Tab>
</Tabs>

***

## Endpoint Reference

```
POST https://api.play2sell.com/functions/v1/missions-partner-api
```

The endpoint accepts two actions via the `action` field: `missions_status` and `missions_summary`.

Each collaborator is identified by **CPF** (any format — digits are normalized) or **email**. When both are sent, CPF wins.

***

### Action: missions\_status

The missions whose period window contains today, per collaborator. "Today" is computed in your company's timezone (returned as `timezone` / `reference_date` in the response) — never UTC.

#### Request Schema

<ParamField body="action" type="string" required>
  Must be `"missions_status"`
</ParamField>

<ParamField body="collaborators" type="array" required>
  Array of collaborator references (max 500 — 100 when `include_missions` is on)
</ParamField>

<ParamField body="include_missions" type="boolean" default="false">
  Return the full `missions[]` array. Off by default: missions fan out per collaborator, so a large batch with the list on becomes a multi-megabyte response. `progress` and `next_mission` always come back.
</ParamField>

<ParamField body="collaborators[].cpf" type="string">
  CPF, any format (`52998224725` or `529.982.247-25`). Must contain exactly 11 digits.
</ParamField>

<ParamField body="collaborators[].email" type="string">
  Email — used only when `cpf` is absent
</ParamField>

#### Example

```bash theme={null}
curl -X POST https://api.play2sell.com/functions/v1/missions-partner-api \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "missions_status",
    "collaborators": [
      { "cpf": "529.982.247-25" },
      { "email": "joao@yourcompany.com" }
    ]
  }'
```

**Response (200):**

```json theme={null}
{
  "data": {
    "timezone": "America/Sao_Paulo",
    "reference_date": "2026-08-11",
    "collaborators": [
      {
        "cpf": "52998224725",
        "found": true,
        "user_id": "8f14e45f-0000-0000-0000-000000000001",
        "name": "Maria Santos",
        "membership_status": "active",
        "progress": {
          "total": 6,
          "completed": 3,
          "pending_approval": 1,
          "active": 2,
          "expired": 0,
          "points_earned": 90,
          "points_available": 235,
          "points_pending_approval": 0
        },
        "next_mission": {
          "id": "b1a2c3d4-0000-0000-0000-000000000010",
          "key": "schedule_visit",
          "name": "Schedule 1 visit",
          "description": "Schedule a visit with a lead",
          "category": "sales",
          "icon": "calendar",
          "current_count": 0,
          "target_count": 1,
          "points_reward": 35,
          "nominal_reward": 70,
          "action": { "route": "/leads", "label": "See leads", "params": null }
        },
        "missions": [
          {
            "id": "b1a2c3d4-0000-0000-0000-000000000009",
            "key": "call_3_leads",
            "name": "Call 3 leads",
            "category": "sales",
            "period_type": "daily",
            "period_start": "2026-08-11",
            "period_end": "2026-08-11",
            "icon": "phone",
            "status": "completed",
            "current_count": 3,
            "target_count": 3,
            "points_reward": 30,
            "nominal_reward": 30,
            "points_credited_so_far": 30,
            "points_awarded": 30,
            "completed_at": "2026-08-11T13:20:04Z",
            "action": null
          }
        ]
      },
      { "email": "joao@yourcompany.com", "found": false }
    ],
    "total": 2,
    "found": 1
  },
  "meta": {
    "request_id": "1f6c1b2e-0000-4a1b-8c2d-000000000001",
    "timestamp": "2026-08-11T13:45:00.000Z"
  }
}
```

#### Response fields explained

| Field                               | Meaning                                                                                                                                                                                                    |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `timezone` / `reference_date`       | Your company's timezone and the local day the answer refers to                                                                                                                                             |
| `found`                             | Whether the CPF/email resolved to a collaborator in your company                                                                                                                                           |
| `membership_status`                 | The collaborator's membership state in your company                                                                                                                                                        |
| `progress.total`                    | Missions whose period window contains today                                                                                                                                                                |
| `progress.completed`                | Finished **and** rewarded                                                                                                                                                                                  |
| `progress.pending_approval`         | Finished by the collaborator, waiting for a manager — points **not yet** credited                                                                                                                          |
| `progress.active`                   | Still in progress                                                                                                                                                                                          |
| `progress.expired`                  | Period closed without completion                                                                                                                                                                           |
| `progress.points_earned`            | Points **actually credited** so far: the full reward of completed missions plus the installments already paid on missions in progress                                                                      |
| `progress.points_available`         | What is **still to be won** on active missions — the effective reward minus the installments already credited. Penalty missions (negative reward) are excluded: a penalty is a risk, not something to earn |
| `progress.points_pending_approval`  | Rewards locked awaiting a manager's approval. Nothing has been credited for these yet                                                                                                                      |
| `next_mission`                      | The active mission with the lowest display order — the one to surface first. Absent when nothing is active                                                                                                 |
| `missions[]`                        | The full list, in the company's configured display order                                                                                                                                                   |
| `missions[].points_reward`          | The **effective** reward — what this collaborator will actually be credited. Scaled by their own target (see below)                                                                                        |
| `missions[].nominal_reward`         | The reward configured on the mission definition, before scaling. Same field names the `mission.completed` event uses                                                                                       |
| `missions[].points_credited_so_far` | How much of this mission's reward has already been paid out through progress                                                                                                                               |
| `missions[].action`                 | Optional deep link configured for that mission (`route`, `label`, `params`). `null` when none                                                                                                              |

<Warning>
  **The full list is opt-in.** By default the answer carries `progress` and `next_mission` only — enough to render a home screen. Ask for `include_missions: true` when you need every mission, and expect a tighter batch limit (100 instead of 500), because each collaborator carries several missions.
</Warning>

<Warning>
  **Rewards are personalized — always render `points_reward`, never `nominal_reward`.** A mission's target can be adapted per collaborator, and the reward scales with it: someone whose target was halved earns half the points. `points_reward` is what will actually be credited; `nominal_reward` is the number configured on the definition, sent only so you can show a "reduced goal" hint if you want. These are the same field names the `mission.completed` webhook uses, so both channels agree.
</Warning>

<Note>
  **Rewards are paid in installments, so "earned" and "still to win" are different numbers.** A mission worth 100 points with a target of 5 credits 20 on every step. After one step the collaborator has **earned 20** and still has **80 to win** — `points_earned` counts the 20, `points_available` counts the 80. Adding the full 100 to both would count the same points twice.
</Note>

<Tip>
  **`pending_approval` is not `completed`.** Some missions require a manager's approval before the points are credited, and the credit happens on the approval date. Reporting them apart lets you render "waiting for approval" without inventing the distinction.
</Tip>

<Warning>
  Mission names, categories, icons, rewards, targets and display order are **configured per company**. Never hardcode them in your client — render whatever the response carries, so a change in SalesOS does not require an app release.
</Warning>

***

### Action: missions\_summary

Everything `missions_status` returns, plus week/month completion counters. Useful for a "your month so far" strip.

Missions are counted in the period they **belong to** (their own window), not the date they were approved — so a mission approved late still counts in the week it was earned.

#### Example

```bash theme={null}
curl -X POST https://api.play2sell.com/functions/v1/missions-partner-api \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "missions_summary",
    "collaborators": [{ "cpf": "529.982.247-25" }]
  }'
```

**Response (200) — additional block per collaborator:**

```json theme={null}
{
  "summary": {
    "week":  { "completed": 8,  "points": 240 },
    "month": { "completed": 31, "points": 980 }
  }
}
```

<Note>
  `missions_summary` scans a month of mission rows per collaborator, so its batch limit is **100** instead of 500.
</Note>

***

## Self mode (federated session)

When your app already holds a federated SalesOS user token, send it as `Authorization: Bearer <jwt>` and **omit** `collaborators`. The answer covers only the token's own user.

```bash theme={null}
curl -X POST https://api.play2sell.com/functions/v1/missions-partner-api \
  -H "Authorization: Bearer <federated-jwt>" \
  -H "Content-Type: application/json" \
  -d '{ "action": "missions_status" }'
```

<Warning>
  Sending `collaborators` together with a user token is rejected with `SELF_MODE_NO_COLLABORATORS`. Batch lookups require a partner API key — otherwise any signed-in user could read other people's progress by CPF.
</Warning>

***

## Error Handling

All errors follow the shared structure:

```json theme={null}
{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description",
    "details": []
  }
}
```

<Warning>
  **Two shapes, not one.** The block above is the *endpoint* error. Failures in the **authentication layer** answer before the endpoint runs, with `error` as a **string**:

  ```json theme={null}
  { "error": "Missing Authorization header", "code": "header_missing" }
  ```

  Code that assumes `error.code` reads `undefined` on every auth failure. Branch on the type — see [API Conventions](/api/conventions#error-shapes).
</Warning>

<AccordionGroup>
  <Accordion title="400 — VALIDATION_ERROR">
    Invalid body, batch over the limit, CPF without 11 digits, or an item with neither `cpf` nor `email`. The `details` array points to the item `index`.
  </Accordion>

  <Accordion title="400 — SELF_MODE_NO_COLLABORATORS">
    A `collaborators` list was sent with a user token. Omit it, or use a partner API key.
  </Accordion>

  <Accordion title="401 — UNAUTHORIZED">
    API key is missing, invalid, or expired, or the signature does not match.
  </Accordion>

  <Accordion title="403 — FORBIDDEN">
    API key is valid but lacks the `missions:read` scope.
  </Accordion>

  <Accordion title="405 — METHOD_NOT_ALLOWED">
    Only `POST` is accepted.
  </Accordion>

  <Accordion title="429 — RATE_LIMITED">
    Too many requests this hour. Wait `retry_after` seconds, then retry.
  </Accordion>

  <Accordion title="500 — SERVER_ERROR">
    Internal server error. Retry with exponential backoff (2s, 4s, 8s).
  </Accordion>
</AccordionGroup>

<Tip>
  **An unknown collaborator is not an error.** The request succeeds with `found: false` for that item, so one bad CPF never breaks a whole home-screen render.
</Tip>

***

## Rate Limits

| Limit                                    | Value                             |
| ---------------------------------------- | --------------------------------- |
| Default requests per hour                | 1000                              |
| Max collaborators per `missions_status`  | 500 (100 with `include_missions`) |
| Max collaborators per `missions_summary` | 100 (50 with `include_missions`)  |

***

## Security

* Read-only: this API never creates, advances or provisions missions
* Each key is scoped to a single company — a CPF from another company answers `found: false`
* Requests are HMAC-signed (P2S-SIGN-V1) and logged for audit; documents are never written to logs

<Warning>
  Never expose your API key in client-side code. This API must only be called from your backend server.
</Warning>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Check-in API" icon="location-dot" href="/api/integrations/checkin">
    Read duty presence to complete the same home screen
  </Card>

  <Card title="Authentication" icon="key" href="/api/authentication">
    Learn how to create and manage API Keys
  </Card>
</CardGroup>
