# List one of your students’ released certificates

- Endpoint: `GET /v1/{organizationId}/certificate/{userId}`
- Production: `https://api.main-team.org/v1/{organizationId}/certificate/{userId}`
- Sandbox: `https://apisnd.main-team.org/v1/{organizationId}/certificate/{userId}`
- Operation: `listStudentCertificates` (Documents)
- Authentication: `Authorization: Bearer <token>`, a short-lived token you sign with your API key and secret
- Permission: `certificate/read:$org:$ID`

## Description

Lists the certificates this organization has released for one of your students, across every season. A certificate the organization has not released yet is not listed. One is listed whether it was issued for one of the student’s applications or to the student directly. Download the file with `downloadCertificate`, by `_id` or `shortId`.

A page at a time: `limit` is 20 by default and at most 100. No order is guaranteed.

Your students are the ones your account registered: anyone else’s student answers `404`, exactly as an unknown id does. A student who has never signed in to this organization answers `409`; they cannot have sat its exams, so there is nothing to collect.

## Parameters

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | yes | string | The organization’s `_id`: 24 hexadecimal digits, as `listOrganizations` (`GET /v1/organization`) lists it. |
| `userId` | path | yes | string | The student’s `_id`: the id `registerStudent` returned. |
| `page` | query | no | number | Page number. Defaults to 1. |
| `limit` | query | no | number | Items per page. Defaults to 20, max 100. |

## Response

`200` Success: `message` is "Certificates fetched successfully.".

```json
{
  "success": true,
  "message": "Certificates fetched successfully.",
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 57,
    "totalPages": 3
  },
  "data": [
    {
      "_id": "6650a1b2c3d4e5f6a7b8c9e7",
      "shortId": "K7Q2M9X4TB",
      "title": "Certificate of Participation",
      "application": "6650a1b2c3d4e5f6a7b8c9e5",
      "user": "6650a1b2c3d4e5f6a7b8c9e9",
      "active": true,
      "createdAt": "2026-09-01T09:30:00.000Z",
      "updatedAt": "2026-09-02T14:05:00.000Z"
    }
  ]
}
```

## Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | [`bad_request`](https://hub.main-team.org/api/errors#bad_request) | `userId` is not 24 hexadecimal digits. |
| 401 | [`unauthorized`](https://hub.main-team.org/api/errors#unauthorized) | The token is missing or malformed, is not signed with your account’s `apiSecret`, breaks the `iat` and `exp` rules, has expired or been revoked, or its account is not active. All of these answer the same. |
| 403 | [`forbidden`](https://hub.main-team.org/api/errors#forbidden) | The token is valid, but no role on your account allows `certificate/read` on the organization in the path, or a role denies it. |
| 404 | [`not_found`](https://hub.main-team.org/api/errors#not_found) | `organizationId` is not the `_id` of an organization. |
| 404 | [`not_found`](https://hub.main-team.org/api/errors#not_found) | No student of yours has this id: it matches nobody, or another account registered the student. |
| 409 | [`conflict`](https://hub.main-team.org/api/errors#conflict) | The student has never signed in to this organization, so it holds no record of them. |
| 429 | [`too_many_requests`](https://hub.main-team.org/api/errors#too_many_requests) | Your account has made more than 100 requests to this operation in the current 60-second window. Wait the seconds in `Retry-After` before sending again. |
| 500 | [`internal_error`](https://hub.main-team.org/api/errors#internal_error) | Something failed on our side. Retry later, and quote `request_id` if it goes on. |

## Code samples

### curl

```bash
# $TOKEN: a short-lived token you minted with your API key and secret
curl -sS 'https://api.main-team.org/v1/<organizationId>/certificate/<userId>?page=1&limit=20' \
  -H "Authorization: Bearer $TOKEN"
```

### Node.js

```js
const token = process.env.TOKEN; // a short-lived token you minted with your API key and secret

const res = await fetch('https://api.main-team.org/v1/<organizationId>/certificate/<userId>?page=1&limit=20', {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
console.log(body.data, body.pagination);
```

### PHP

```php
<?php
$token = getenv('TOKEN'); // a short-lived token you minted with your API key and secret

$ch = curl_init('https://api.main-team.org/v1/<organizationId>/certificate/<userId>?page=1&limit=20');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $token,
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
if ($response === false) {
    throw new RuntimeException(curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$body = json_decode($response, true);
if ($status >= 400) {
    $error = $body['error'];
    throw new RuntimeException("$status {$error['code']}: {$error['message']}");
}
print_r($body['data']);
print_r($body['pagination']);
```
