## Edupaid API Reference

### General

- All endpoints require `x-api-key` header (provider API key).
- Request/response content type: `application/json`.

---

## Learning Track Authorization

### POST /get-app-authorization

Get learning track authorization status for a student.

#### Description

Verifies the API key, validates input, fetches the learning track and student, and returns whether the track is locked, unlocked, scheduled, or defaulted along with payment modes and scheduling information.

#### Headers

| Name        | Type   | Required | Description                                       |
| ----------- | ------ | -------- | ------------------------------------------------- |
| `x-api-key` | string | Yes      | Provider API key used to authenticate the request |

#### Request Body

```json
{
  "studentId": "string",
  "learningTrackId": "string"
}
```

#### Responses

- 200 OK – Successful response with authorization status

```json
{
  "track": {
    "learningTrackId": "string",
    "learningTrackName": "string",
    "appId": "string",
    "isLocked": true,
    "paymentModes": [
      {
        "payee": "string",
        "amount": 0,
        "currency": "string",
        "frequency": "string"
      }
    ],
    "scheduledChargeDate": "2025-09-29T07:22:53.264Z",
    "frequency": "Monthly"
  },
  "defaulted": true,
  "status": "unlocked"
}
```

- 400 Bad Request – invalid input

```json
{ "error": "string" }
```

- 401 Unauthorized – invalid or missing API key

- 404 Not Found – learning track not found or unavailable

- 500 Internal Server Error

```json
{ "error": "string" }
```

---

### POST /list-app-authorizations

List learning track authorizations for a student.

#### Description

Verifies the API key, looks up a student by `timebackStudentId`, fetches approved learning tracks, and returns locked, unlocked, scheduled, and defaulted track authorizations. If the student does not exist, the endpoint returns 200 with all approved tracks treated as locked (with payment modes) and the unlocked, scheduled, and defaulted sections empty.

#### Headers

| Name        | Type   | Required | Description                                       |
| ----------- | ------ | -------- | ------------------------------------------------- |
| `x-api-key` | string | Yes      | Provider API key used to authenticate the request |

#### Request Body

```json
{
  "timebackStudentId": "string",
  "providerId": "string"
}
```

> Note: In testing, you don't get an error response if the student does not exist. It simply returns a canned response for the provider id.

Example:

Request:

```json
{
  "timebackStudentId": "this-user-does-not-exist",
  "providerId": "d22f8859-176f-4cca-920c-bfdef62a04ed"
}
```

Response:

```json
{
  "unlockedTracks": [],
  "lockedTracks": [
    {
      "learningTrackId": "34a6bd30-a8f0-4c8f-9348-dc736d85c242",
      "learningTrackName": "Alpha Anywhere",
      "appId": "f8600af3-4131-4c30-a09a-b208c226381d",
      "isLocked": true,
      "paymentModes": [
        {
          "payee": "Student",
          "amount": 833.33,
          "currency": "USD",
          "frequency": "Monthly"
        }
      ]
    }
  ],
  "scheduledTracks": [],
  "defaultedTracks": []
}
```

#### Responses

- 200 OK – Successful response with locked, unlocked, scheduled, and defaulted learning tracks

```json
{
  "lockedTracks": [
    {
      "learningTrackId": "string",
      "learningTrackName": "string",
      "appId": "string",
      "isLocked": true,
      "paymentModes": [
        {
          "payee": "string",
          "amount": 0,
          "currency": "string",
          "frequency": "string"
        }
      ],
      "scheduledChargeDate": "2025-09-29T07:22:53.267Z",
      "frequency": "Monthly"
    }
  ],
  "unlockedTracks": [
    {
      "learningTrackId": "string",
      "learningTrackName": "string",
      "appId": "string",
      "isLocked": true,
      "paymentModes": [
        {
          "payee": "string",
          "amount": 0,
          "currency": "string",
          "frequency": "string"
        }
      ],
      "scheduledChargeDate": "2025-09-29T07:22:53.267Z",
      "frequency": "Monthly"
    }
  ],
  "scheduledTracks": [
    {
      "learningTrackId": "string",
      "learningTrackName": "string",
      "appId": "string",
      "isLocked": true,
      "paymentModes": [
        {
          "payee": "string",
          "amount": 0,
          "currency": "string",
          "frequency": "string"
        }
      ],
      "scheduledChargeDate": "2025-09-29T07:22:53.267Z",
      "frequency": "Monthly"
    }
  ],
  "defaultedTracks": [
    {
      "learningTrackId": "string",
      "learningTrackName": "string",
      "appId": "string",
      "isLocked": true,
      "paymentModes": [
        {
          "payee": "string",
          "amount": 0,
          "currency": "string",
          "frequency": "string"
        }
      ],
      "scheduledChargeDate": "2025-09-29T07:22:53.267Z",
      "frequency": "Monthly"
    }
  ]
}
```

- 400 Bad Request – invalid input

```json
{ "error": "string" }
```

- 401 Unauthorized – invalid or missing API key

- 500 Internal Server Error

```json
{ "error": "string" }
```

---

### POST /batch-get-app-authorization

Get learning track authorization status for multiple students.

#### Description

Verifies the API key, validates input, and returns authorization status for multiple students for a single learning track. Returns locked, unlocked, scheduled, or defaulted status along with payment modes for each student. Non-existent student IDs are treated as locked with payment modes rather than errors.

#### Headers

| Name        | Type   | Required | Description                                       |
| ----------- | ------ | -------- | ------------------------------------------------- |
| `x-api-key` | string | Yes      | Provider API key used to authenticate the request |

#### Request Body

```json
{
  "learningTrackId": "string",
  "studentIds": ["string"]
}
```

#### Responses

- 200 OK – Successful response with authorization status for all students

```json
{
  "learningTrackId": "string",
  "results": [
    {
      "studentId": "string",
      "track": {
        "learningTrackId": "string",
        "learningTrackName": "string",
        "appId": "string",
        "isLocked": true,
        "paymentModes": [
          {
            "payee": "string",
            "amount": 0,
            "currency": "string",
            "frequency": "string"
          }
        ],
        "scheduledChargeDate": "2025-09-29T07:22:53.272Z",
        "frequency": "Monthly"
      },
      "defaulted": true,
      "error": "string",
      "status": "unlocked"
    }
  ]
}
```

- 400 Bad Request – invalid input

```json
{ "error": "string" }
```

- 401 Unauthorized – invalid or missing API key

- 404 Not Found – learning track not found

- 500 Internal Server Error

```json
{ "error": "string" }
```

---

## Webhooks

### POST /caliper_webhook

Receive Caliper webhook.

#### Description

Validates and logs Caliper event payloads to Supabase. Only accepts POST requests with valid headers and signature.

#### Headers

| Name                  | Type   | Required | Description                           |
| --------------------- | ------ | -------- | ------------------------------------- |
| `x-webhook-signature` | string | Yes      | HMAC SHA-256 signature of the payload |
| `x-webhook-timestamp` | string | Yes      | Timestamp (milliseconds since epoch)  |
| `x-webhook-source`    | string | Yes      | Identifier for the webhook source     |

#### Request Body

```json
{
  "type": "string",
  "action": "string",
  "additionalProp1": {}
}
```

#### Responses

- 200 OK – Webhook processed successfully

```json
{ "success": true }
```

- 400 Bad Request – invalid input

```json
{ "error": "string" }
```

- 405 Method Not Allowed

- 500 Internal Server Error

---

## Tokens

### POST /submit_token

Submit a learning token for a student.

#### Description

Verifies the API key, validates token input, saves the token, and updates prerequisite slots accordingly. Invalid tokens are recorded in `bad_tokens` for further inspection.

#### Headers

| Name        | Type   | Required | Description                                       |
| ----------- | ------ | -------- | ------------------------------------------------- |
| `x-api-key` | string | Yes      | Provider API key used to authenticate the request |

#### Request Body

```json
{
  "studentId": "string",
  "timeBackAppId": "string",
  "type": "Mastery",
  "data": "string",
  "evidenceId": "string"
}
```

#### Responses

- 200 OK – Token submitted and processed successfully

```json
{ "id": "string" }
```

- 400 Bad Request – invalid input

```json
{ "error": "string" }
```

- 401 Unauthorized – invalid or missing API key

- 404 Not Found – Student or app not found, or not approved

- 500 Internal Server Error

```json
{ "error": "string" }
```

---

## Parent

### POST /generate-parent-token

Generate parent portal access URL.

#### Description

Verifies the API key, validates the parent identifier, and returns a time-limited URL to the Students app subscription portal. The token is embedded in the URL and expires after the specified duration (in minutes).

#### Headers

| Name        | Type   | Required | Description                                       |
| ----------- | ------ | -------- | ------------------------------------------------- |
| `x-api-key` | string | Yes      | Provider API key used to authenticate the request |

#### Request Body

```json
{
  "parentTimebackId": "string",
  "expiryMinutes": 0
}
```

#### Responses

- 200 OK – Successfully generated parent portal URL

```json
{ "url": "string" }
```

- 400 Bad Request – invalid input

```json
{ "error": "string" }
```

- 401 Unauthorized – invalid or missing API key

- 500 Internal Server Error

```json
{ "error": "string" }
```
