# API Documentation

## Overview

The Herd API provides programmatic access to training completion data, user information, and tag management. All endpoints require API key authentication and are scoped to your organization.

## Creating your API Key

### Getting Your API Key

1. Log into your Herd dashboard
2. Navigate to Administration on the left sidebar > API Keys
3. Click "Create New API Key"
4. Save the key securely - Please note: **The key will only be shown once**

<figure><img src="/files/vK17bUa5kCdzi5dep039" alt=""><figcaption></figcaption></figure>

#### Using Your API Key

Include your API key in the 'X-API-Key' header with every request:

```shellscript
 curl -H "X-API-Key: herd_a1b2c3d4_..." https://app.security.io/api/v1/enrollments
```

**API Key Format:** `herd_<8chars>_<64chars>` (73 characters total)

#### Response Format

All API responses follow this structure:

{% code title="Success Response" %}

```json
{ 
    "status": "success", 
    "data": [...], 
    "pagination": { "
    total_records": 100, 
    "current_page": 1, 
    "total_pages": 2, 
    "per_page": 50, 
    "next_page_url": "https://app.herdsecurity.io/api/v1/enrollments?page=2&per_page=50" } 
}
```

{% endcode %}

{% code title="Error Response" %}

```json
{ 
    "status": "error",
    "error": {
        "code": 401,
        "message": "Invalid API key",
        "details": {}
    }
} 
```

{% endcode %}

### Endpoints

#### Get Training Completions

```
GET https://api.herdsecurity.io/api/v1/enrollments
```

Retrieves all training enrollments and completion status for your organization. This is the primary endpoint for tracking training progress and completions.

**Query Parameters:**

| Parameter  | Type    | Required | Default | Description                         |
| ---------- | ------- | -------- | ------- | ----------------------------------- |
| `page`     | integer | No       | 1       | Page number (min: 1)                |
| `per_page` | integer | No       | 50      | Results per page (min: 1, max: 100) |

{% code title="Response" %}

```json
{
  "status": "success",
  "data": [
    {
      "enrollment_id": "enr_clxyz456ghi",
      "user_id": "usr_clxyz789def",
      "training_id": "trn_clxyz123abc",
      "tag_id": "tag_UGhpc2hpbmc",
      "enrollment_date": "2024-01-15T10:30:00.000Z",
      "start_date": "2024-01-15T14:20:00.000Z",
      "completion_date": "2024-01-16T09:45:00.000Z",
      "status": "Passed"
    }
  ],
  "pagination": {
    "total_records": 500,
    "current_page": 1,
    "total_pages": 10,
    "per_page": 50
  }
}
```

{% endcode %}

**Status Values:**

* `Not started` - User has not begun the training
* `In Progress` - User started but has not completed
* `Passed` - User completed the training

**Date Fields:**

* `enrollment_date` - When user was enrolled (always present)
* `start_date` - When user started training (null if not started)
* `completion_date` - When user completed training (null if not completed)

**Example Request:**

`curl -H "X-API-Key: herd_a1b2c3d4_..." \ "`[`https://app.security.io/api/v1/enrollments?page=1&per_page=100`](https://your-domain.com/api/v1/enrollments?page=1\&per_page=100)`` ` ``

**Use Cases:**

* Export completed trainings to Google Sheets
* Generate completion reports
* Track training progress across your organization
* Identify users who need to complete specific trainings

#### 2. Get Users

**`GET /api/v1/users`**

Retrieves user details for your organization.

**Query Parameters:**

| Parameter  | Type    | Required | Default | Description                 |
| ---------- | ------- | -------- | ------- | --------------------------- |
| `page`     | integer | No       | 1       | Page number                 |
| `per_page` | integer | No       | 50      | Results per page (max: 100) |

{% code title="Response" %}

````json
{
  "status": "success",
  "data": [
    {
      "user_id": "usr_clxyz789def",
      "first_name": "John",
      "last_name": "Doe",
      "email": "john.doe@company.com",
      "status": "Active"
    }
  ],
  "pagination": {
    "total_records": 250,
    "current_page": 1,
    "total_pages": 5,
    "per_page": 50
  }
}
```
````

{% endcode %}

**Status Values:**

* `Active` - User has Slack or Teams integration
* `Inactive` - User not connected to any platform

3. Get Tags

**`GET /api/v1/tags`**

Retrieves all tags used to categorize trainings in your organization.

**Query Parameters:**

| Parameter  | Type    | Required | Default | Description                 |
| ---------- | ------- | -------- | ------- | --------------------------- |
| `page`     | integer | No       | 1       | Page number                 |
| `per_page` | integer | No       | 50      | Results per page (max: 100) |

{% code title="Response" %}

````json
{
  "status": "success",
  "data": [
    {
      "tag_id": "tag_UGhpc2hpbmc",
      "name": "Phishing Awareness",
      "status": "In Progress",
      "start_date": null,
      "end_date": null,
      "created_at": "2024-01-15T10:30:00.000Z"
    }
  ],
  "pagination": {
    "total_records": 25,
    "current_page": 1,
    "total_pages": 1,
    "per_page": 50
  }
}
```
````

{% endcode %}

{% code title="Response" %}

```
{
  "status": "success",
  "tag_id": "tag_UGhpc2hpbmc",
  "data": [
    {
      "training_id": "trn_clxyz123abc",
      "title": "Phishing Detection 101",
      "description": "Learn to identify phishing attempts"
    }
  ],
  "pagination": {
    "total_records": 10,
    "current_page": 1,
    "total_pages": 1,
    "per_page": 50
  }
}
```

{% endcode %}

### Common Use Cases

### **Filter by Date Range**

```javascript
const enrollments = await fetch('https://your-domain.com/api/v1/enrollments?per_page=100', {
  headers: { 'X-API-Key': 'herd_...' }
}).then(r => r.json());

// Filter completions from last 30 days
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

const recentCompletions = enrollments.data.filter(e => {
  if (!e.completion_date) return false;
  return new Date(e.completion_date) >= thirtyDaysAgo;
});
```

### **Group Completions by User**

```javascript
const enrollments = await fetch('https://your-domain.com/api/v1/enrollments?per_page=100', {
  headers: { 'X-API-Key': 'herd_...' }
}).then(r => r.json());

const byUser = enrollments.data.reduce((acc, enrollment) => {
  if (!acc[enrollment.user_id]) {
    acc[enrollment.user_id] = {
      total: 0,
      completed: 0,
      in_progress: 0,
      not_started: 0
    };
  }

  acc[enrollment.user_id].total++;
  if (enrollment.status === 'Passed') acc[enrollment.user_id].completed++;
  else if (enrollment.status === 'In Progress') acc[enrollment.user_id].in_progress++;
  else acc[enrollment.user_id].not_started++;

  return acc;
}, {});

console.log(byUser);
```

### **Pagination**

All list endpoints support pagination. When results exceed one page, use the `next_page_url` or increment the `page` parameter.

**Example: Fetch all enrollments**

```javascript
async function fetchAllEnrollments(apiKey) {
  let allEnrollments = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(
      `https://your-domain.com/api/v1/enrollments?page=${page}&per_page=100`,
      { headers: { 'X-API-Key': apiKey } }
    );

    const result = await response.json();
    allEnrollments = allEnrollments.concat(result.data);

    hasMore = page < result.pagination.total_pages;
    page++;
  }

  return allEnrollments;
}
```

### Error Codes

| Code | Meaning               | Solution                                       |
| ---- | --------------------- | ---------------------------------------------- |
| 400  | Bad Request           | Check query parameters are valid               |
| 401  | Unauthorized          | Verify API key is correct and active           |
| 404  | Not Found             | Resource doesn't exist (e.g., invalid tag\_id) |
| 429  | Too Many Requests     | Slow down request rate                         |
| 500  | Internal Server Error | Contact support if persists                    |

### Rate Limiting

Rate limits are enforced to ensure API stability. If you receive a 429 error, implement exponential backoff:

```javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);

    if (response.status !== 429) {
      return response;
    }

    // Exponential backoff: 1s, 2s, 4s
    const delay = Math.pow(2, i) * 1000;
    await new Promise(resolve => setTimeout(resolve, delay));
  }

  throw new Error('Max retries exceeded');
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://herd-security.gitbook.io/herd-security-docs/api-documentation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
