Four calls, in order: create a student, enroll them in a teaching group, mark them present at a session, and issue an invoice for the enrollment. Every response id below feeds the next call — copy them as you go, or read the ids back off each response instead of hardcoding these placeholders.

> **Before you start**
>
> You'll need a `branchId` and a `groupId` that already exist in your tenant (`GET /api/v1/branches`, `GET /api/v1/groups`) and an API key with `students.write`, `enrollments.write`, `attendance.mark`, and `invoices.write`. See [Authentication](https://docs.institflow.com/en/guides/authentication#api-keys).

## 1. Create a student

`POST /api/v1/students` — only `branchId`, `firstName`, and `lastName` are required; `studentNumber` auto-allocates if omitted.

```bash
curl -X POST "https://api.yourdomain.com/api/v1/students" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "branchId": "0190a1b2-0000-7000-8000-000000000001",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "email": "ada.lovelace@example.com",
    "phone": "+15551234567"
  }'
```

```javascript
const res = await fetch('https://api.yourdomain.com/api/v1/students', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${process.env.INSTITFLOW_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() },
  body: JSON.stringify({
    "branchId": "0190a1b2-0000-7000-8000-000000000001",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "email": "ada.lovelace@example.com",
    "phone": "+15551234567"
  }),
});
const data = await res.json();
```

The response is the created student, id `0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b` below.

## 2. Enroll them in a group

`POST /api/v1/enrollments` — `priceMinor`/`currency` set what this student is charged for this group (a fee schedule can compute this for you; see [Billing](https://docs.institflow.com/en/reference/billing)).

```bash
curl -X POST "https://api.yourdomain.com/api/v1/enrollments" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "studentId": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
    "groupId": "0190a1b2-0000-7000-8000-000000000002",
    "priceMinor": 12000,
    "currency": "ILS"
  }'
```

```javascript
const res = await fetch('https://api.yourdomain.com/api/v1/enrollments', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${process.env.INSTITFLOW_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() },
  body: JSON.stringify({
    "studentId": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
    "groupId": "0190a1b2-0000-7000-8000-000000000002",
    "priceMinor": 12000,
    "currency": "ILS"
  }),
});
const data = await res.json();
```

## 3. Mark attendance

Attendance is per-session, bulk-upserted: `PUT /api/v1/sessions/:id/attendance` with one entry per student on the roster. Re-sending the same session's attendance later updates it in place — it's idempotent by design, not just by the `Idempotency-Key` header.

```bash
curl -X PUT "https://api.yourdomain.com/api/v1/sessions/0190a1b2-0000-7000-8000-000000000003/attendance" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "entries": [
      {
        "studentId": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
        "status": "present"
      }
    ]
  }'
```

```javascript
const res = await fetch('https://api.yourdomain.com/api/v1/sessions/0190a1b2-0000-7000-8000-000000000003/attendance', {
  method: 'PUT',
  headers: { 'Authorization': `Bearer ${process.env.INSTITFLOW_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() },
  body: JSON.stringify({
    "entries": [
      {
        "studentId": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
        "status": "present"
      }
    ]
  }),
});
const data = await res.json();
```

## 4. Issue an invoice

Invoices are created as drafts, then issued — issuing is a separate call because a draft can be edited (or discarded) freely, while an issued invoice is a real, numbered financial document. Totals are always recomputed server-side from `lines`; never send a total yourself.

```bash
curl -X POST "https://api.yourdomain.com/api/v1/invoices" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "branchId": "0190a1b2-0000-7000-8000-000000000001",
    "studentId": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
    "issueDate": "2026-03-02",
    "dueDate": "2026-03-16",
    "currency": "ILS",
    "lines": [
      {
        "description": "March tuition — Algebra I",
        "quantity": 1,
        "unitMinor": 12000,
        "enrollmentId": "0190a1b2-0000-7000-8000-000000000004"
      }
    ]
  }'
```

```javascript
const res = await fetch('https://api.yourdomain.com/api/v1/invoices', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${process.env.INSTITFLOW_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() },
  body: JSON.stringify({
    "branchId": "0190a1b2-0000-7000-8000-000000000001",
    "studentId": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
    "issueDate": "2026-03-02",
    "dueDate": "2026-03-16",
    "currency": "ILS",
    "lines": [
      {
        "description": "March tuition — Algebra I",
        "quantity": 1,
        "unitMinor": 12000,
        "enrollmentId": "0190a1b2-0000-7000-8000-000000000004"
      }
    ]
  }),
});
const data = await res.json();
```

Then issue it (draft id from the previous response):

```bash
curl -X POST "https://api.yourdomain.com/api/v1/invoices/0190a1b2-0000-7000-8000-000000000005/issue" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

```javascript
const res = await fetch('https://api.yourdomain.com/api/v1/invoices/0190a1b2-0000-7000-8000-000000000005/issue', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${process.env.INSTITFLOW_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() },
});
const data = await res.json();
```

That's the core loop. From here: [subscribe to a webhook](https://docs.institflow.com/en/webhooks) so your system hears about `invoice.issued`/`payment.recorded` without polling, or browse the full [Billing reference](https://docs.institflow.com/en/reference/billing) for payments, statements, and fee schedules.
