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

# Sync your roster

> A worked example: pull every member into your own system, then keep it current.

This is the first thing most integrations do. It uses one endpoint,
`GET /v1/members`, and the two mechanics that make a sync resumable: cursors and
`updatedSince`.

## 1. Create a token

Use a **service token** (Settings → Developers) rather than a personal one. A
service token belongs to the organization, so the sync keeps running when the
officer who set it up graduates.

Scopes: `members:read` — each member already carries the groups they belong to.
Role: `viewer`, since a sync that only reads should not be able to write.

The secret is shown once. Store it as a secret in your own system; if you lose
it, revoke and reissue.

## 2. Read the first page

```bash theme={"system"}
curl "https://api.dueflow.co/v1/members?limit=100" \
  -H "Authorization: Bearer $DUEFLOW_TOKEN"
```

```json theme={"system"}
{
  "data": [
    {
      "id": "S_QZS1bcVIsrxWJHTKbIM",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "email": "ada@example.com",
      "isActive": true,
      "joinDate": "2026-07-25",
      "groups": [],
      "createdAt": "2026-07-25T17:40:23.352Z",
      "updatedAt": "2026-07-25T17:40:23.352Z"
    }
  ],
  "hasMore": true,
  "nextCursor": "MjAyNi0wNy0yNVQxNzo0MDoyMy4zNTJafFNfUVpTMWJjVklzcnhXSkhUS2JJTQ"
}
```

Members are returned oldest-change-first. `isActive: false` means archived —
Dueflow never deletes a member, so archived rows keep appearing and your system
should mirror the flag rather than dropping the record.

<Note>
  Phone numbers, guardian and emergency contacts, custom fields and internal
  notes are deliberately absent from every API response in v1. If your
  integration needs one of those, tell us which and why — it's a decision about
  what leaves the platform, not an oversight.
</Note>

## 3. Walk the pages

Pass `nextCursor` back verbatim while `hasMore` is true:

```python theme={"system"}
def fetch_all(token, params=None):
    cursor, out = None, []
    while True:
        r = requests.get(
            "https://api.dueflow.co/v1/members",
            headers={"Authorization": f"Bearer {token}"},
            params={"limit": 100, "cursor": cursor, **(params or {})},
        )
        r.raise_for_status()
        page = r.json()
        out += page["data"]
        if not page["hasMore"]:
            return out
        cursor = page["nextCursor"]
```

Cursors are keyset-based on `(updatedAt, id)`, so a member edited while you're
mid-walk is never silently skipped or duplicated — which is exactly what offset
pagination does.

## 4. Keep it current

Store the highest `updatedAt` you saw. Next run, ask only for what changed:

```bash theme={"system"}
curl "https://api.dueflow.co/v1/members?updatedSince=2026-07-25T17:40:23Z&limit=100" \
  -H "Authorization: Bearer $DUEFLOW_TOKEN"
```

Use the watermark from the *previous* run rather than "now minus an hour": a
watermark can't lose a record to clock skew or to a run that took longer than
its interval. Overlapping slightly is harmless — you'll re-apply an update you
already have.

A full nightly re-read is fine too. At 100 rows a page, a 500-member roster is
5 requests out of 600 per minute.

## Nationals: reading your chapters

With `chapters:read` a national can enumerate its chapters and read each roster:

```bash theme={"system"}
curl "https://api.dueflow.co/v1/chapters?limit=100" \
  -H "Authorization: Bearer $DUEFLOW_TOKEN"

curl "https://api.dueflow.co/v1/chapters/$CHAPTER_ID/members?limit=100" \
  -H "Authorization: Bearer $DUEFLOW_TOKEN"
```

Only *direct* child chapters are reachable; anything else is a `404`.

Two things to plan for:

* **Roster reads are priced by page size** — `ceil(limit / 25)` units, so 100
  rows costs 4. A 200-chapter sync at 100 rows a page spends 800 of your 600
  units per minute, so it will need to pace itself. See
  [Rate limits](/rate-limits).
* **The chapter can see you read it.** Each cross-organization read appears in
  that chapter's own activity log, naming your organization and how many members
  were returned. That transparency is the reason the access exists; treat it as a
  feature to mention to your chapters rather than a surprise for them to find.

## Writing back

Creating members is `POST /v1/members` with `members:write` and an
`Idempotency-Key`:

```bash theme={"system"}
curl -X POST https://api.dueflow.co/v1/members \
  -H "Authorization: Bearer $DUEFLOW_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"Ada","lastName":"Lovelace","email":"ada@example.com"}'
```

A duplicate email returns `409 conflict`. To archive rather than delete, use
`PATCH /v1/members/:id` with `{"isActive": false}` — there is no delete.
