# Auth, Headers, And Errors

Read this when a task involves credentials, tenant scope, writes, ETags, idempotency, list counts, or Problem handling.

## Base URL

Use the canonical implementation URL unless the caller supplies another approved base:

```bash
export ED_FI_BASE_URL="${ED_FI_BASE_URL:-https://platform3-andymontgomery-9773s-projects.vercel.app/ed_fi/1edtech/implementation/api}"
```

Strip only a trailing slash. Do not rewrite the module path.

## Demo token

The public mint endpoint is demo-only:

```bash
TOKEN_JSON="$(curl -fsS -X POST "$ED_FI_BASE_URL/dev/mint?tenantId=demo")"
export ED_FI_TOKEN="$(printf '%s' "$TOKEN_JSON" | jq -r '.token')"
```

If `jq` is unavailable, use the host's JSON tool to extract the `token` field. Token extraction is a shell convenience, not Ed-Fi business logic.

Never call `/dev/mint` for a non-demo tenant. The live API returns a typed Problem when `tenantId` is anything other than `demo`.

## Real tenant token

For reviewer or customer work:

```bash
export ED_FI_TOKEN="$ED_FI_REVIEWER_JWT"
```

The token is operator-minted out of band. Never print it, commit it, include it in screenshots, place it in a public static file, or send it to any origin other than the approved Ed-Fi base URL or a same-origin proxy owned by the generated app.

## Required headers

Every protected request:

```bash
-H "Authorization: Bearer $ED_FI_TOKEN"
```

JSON writes:

```bash
-H "Content-Type: application/json"
-H "Idempotency-Key: <stable-key-for-one-logical-operation>"
```

Attendance teacher-mark POST responses include the policy headers clients should obey instead of discovering throughput by trial and error:

- `X-Platform3-Attendance-Policy: edfi.attendance.teacher_mark.v2026-06-22`
- `X-Platform3-Write-Path: teacher-mark-create`
- `X-Platform3-Max-Client-Concurrency: 8`
- `X-Platform3-Classroom-Batch-Size: 32`
- `X-Platform3-Confirmation-Target-Seconds: 30`

For daily or section attendance marks, send append-only POSTs and treat `ack_id` plus ETag as the confirmation. Use detail `PUT`, `PATCH`, or `DELETE` only for corrections after a row has already been acknowledged.

Overwrite-capable writes:

```bash
-H "If-Match: <ETag from detail read or prior write response>"
```

Use a new `Idempotency-Key` for a different logical operation. Reusing a key with changed method, path, or body should return `edfi:idempotency_conflict`; do not hide that behind a local retry.

## ETags

Before `PUT`, `PATCH`, `DELETE`, or draft promotion:

1. Read the detail resource.
2. Copy the returned `ETag`.
3. Send it as `If-Match`.
4. If the surface returns `edfi:etag_mismatch`, refetch and merge intentionally.

Do not compute ETags locally. Do not strip weak validator prefixes unless the docs for the specific response tell you to compare normalized validators.

## List counts

For list responses, prefer these surface-owned values:

- HTTP `Total-Count`
- JSON `totalCount`
- JSON `page.totalCount`
- JSON `page.nextOffset`
- JSON `page.nextCursor`
- JSON `links.next`

Do not count pages locally to infer totals. Do not decode cursor tokens.

## Safe browser proxy pattern

For a browser app, a same-origin proxy is acceptable only when it:

- allowlists the Ed-Fi base URL;
- strips credentials from logs and visible traces;
- forwards only the documented method, path, headers, and JSON body;
- does not store Ed-Fi domain records outside the surface;
- exposes response headers needed by the UI, such as `Total-Count`, `ETag`, `X-Ed-Fi-Current-ETag`, and `Idempotency-Replayed`.

If the proxy starts validating Ed-Fi schema, reconciling roster ids, replaying idempotency, or redacting Problem details, it has crossed the skill-pack boundary.

## Problem response rule

Preserve Problem fields exactly. A good generated report includes status, code, type, request id, trace id, field errors, and the request path. It does not rewrite support evidence into a friendlier local error taxonomy.
