> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pharussecurity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Register and draft

> POST /api/v1/incidents and the report draft endpoints: what an agent connected over OAuth can write, and how it is refused.

> [!NOTE]
>
> **Plans:** Every plan and the free trial. A read-only workspace answers `402`. **Who:** an agent connected over OAuth with the `incidents:draft` scope, acting for a member of a workspace that allows agents.

These endpoints are what the [MCP server](https://docs.pharussecurity.com/api-reference/mcp-server.md)'s register and draft tools call. They open incidents to be assessed and hold proposed report text for a person to review. Nothing here starts a clock, changes a pack or files anything. There is no endpoint for that, on any token.

```text theme={null}
POST https://app.pharussecurity.com/api/v1/incidents
POST https://app.pharussecurity.com/api/v1/incidents/{reference}/families
GET  https://app.pharussecurity.com/api/v1/incidents/{reference}/reports/{report}
POST https://app.pharussecurity.com/api/v1/incidents/{reference}/reports/{report}/draft
GET  https://app.pharussecurity.com/api/v1/token
```

`{report}` is `early_warning`, `notification` or `final_report`.

## Authentication

Send an OAuth access token from an [MCP client's sign-in](https://docs.pharussecurity.com/api-reference/mcp-server.md#connect-a-client):

```http theme={null}
Authorization: Bearer pho_...
```

An API token gets `401` on these endpoints, whatever its scopes. Everything an agent writes shows in the [audit trail](https://docs.pharussecurity.com/reports/audit-trail.md) as the agent, on behalf of the person who connected it.

## Register a vulnerability

`POST /api/v1/incidents` opens one incident per product family, being assessed. No clock starts: a person says whether it's exploited in the product. See [work with an agent](https://docs.pharussecurity.com/incidents/agents.md#register-a-vulnerability).

| Field | Type | |
| - | - | - |
| `vulnerability` | string | Required. A CVE, GHSA or EUVD id, or the team's own id, in letters, digits and `.`, `_`, `:` and `-`. 3 to 64 characters. |
| `summary` | string | What is known so far, for the person assessing it. At most 4 000 characters. It shows on the incident as the agent's words and never goes into a report. |
| `familyIds` | array of string | Required. 1 to 10 product family ids, from [`GET /api/v1/families`](https://docs.pharussecurity.com/api-reference/read-api#product-families). |

Pharus titles each incident from the id and the family. Versions come from the family's SBOMs, else its current version, and member states from the family. A person can correct them.

A field it doesn't know is refused with `400`, so nothing can set the awareness time, the source, the title or the versions.

A family that already has an incident for this id, open or closed and whoever opened it, gets that incident back. So a retry is safe, and the answer says what happened. If one family is wrong, nothing opens.

```json theme={null}
{
  "vulnerability": "CVE-2026-1234",
  "incidents": [
    {
      "reference": "INC-0042",
      "family": { "id": "0f8fad5b-d9cb-469f-a165-70867728950e", "name": "Gateway G3" },
      "created": true,
      "status": "open",
      "assessing": true,
      "awareAt": null,
      "assessBy": "2026-11-04T14:00:00.000Z",
      "matchVex": null,
      "url": "https://app.pharussecurity.com/incidents/INC-0042"
    }
  ]
}
```

`matchVex` is the VEX status on the family's match, if there is one: read `not_affected` before going on. The response is `201` when any incident opened and `200` when none did.

## Link a product family

`POST /api/v1/incidents/{reference}/families` opens an incident for another family, with the same id and the agent's summary. Body: `familyId` (required). The response is `{ incident }`, with the same fields as above: `201` opened, `200` it was already there.

`409` with `no_vulnerability` when the incident isn't about a vulnerability with an id. An incident's own family can't change.

## Read a report's pack

`GET /api/v1/incidents/{reference}/reports/{report}` returns the [SRP pack](https://docs.pharussecurity.com/incidents/srp-pack.md) as it stands. It holds draft text, so it needs `incidents:draft`: a read token never sees it.

| Field | What it holds |
| - | - |
| `reference`, `report`, `status`, `assessing` | The incident, and whether it's still being assessed |
| `deadline`, `state`, `recipient`, `filed` | The report's clock, where it goes, and whether it's marked as submitted |
| `fields` | Each field: `ref`, `label`, `value`, `source` (`auto` or `draft`), `required`, `maxLength`, `missing`, `gaps`, `filledBySrp` and `agentMayWrite` |
| `latestDraft` | The latest proposal, or `null`: `id`, `status` (`pending_review`, `accepted`, `dismissed` or `superseded`), `fields`, `note`, `warnings`, `createdAt`, `agent`, `onBehalfOf`, `reviewedBy` and `reviewedAt` |
| `url` | The pack in Pharus |

## Propose report text

`POST /api/v1/incidents/{reference}/reports/{report}/draft` holds text for a person to review. It doesn't touch the pack. A person accepts it, changes it or dismisses it on the incident's **SRP pack** tab. See [review a draft](https://docs.pharussecurity.com/incidents/agents.md#review-a-draft).

| Field | Type | |
| - | - | - |
| `fields` | object | Required. Field reference to text, for fields with `agentMayWrite`. Within ENISA's length for each. |
| `note` | string | For the person reviewing. At most 1 000 characters. |

Write in English, as the SRP takes English only. Write a fact you don't have as `[[what is missing]]`: Pharus counts that field as missing until someone fills it in.

The response is `201` with the proposal and its `url`. `warnings` lists what to check, such as `gap:3:affected versions`, or `ungrounded:v21:CVE-2026-9999` for an id or link that isn't in the incident's record.

A new proposal replaces one not yet reviewed. The same text again changes nothing.

## Check a token

`GET /api/v1/token` says whether a token still works and what it was given: `{ scopes, workspace, onBehalfOf, agent }`. It doesn't count against your reads.

## Errors

Errors have the same shape as everywhere in the API: `{ "error": "...", "message": "..." }`.

| Status | `error` | Meaning |
| - | - | - |
| 400 | `invalid_json`, `invalid_request` | The body isn't JSON, is missing something, or has a field it doesn't know |
| 401 | `unauthorized` | The token is missing, expired or revoked, its person has left, it's an API token, or it wasn't issued for the MCP server. `WWW-Authenticate` says where to sign in. |
| 402 | `billing_lapsed` | The workspace is read-only or closed |
| 403 | `agents_disabled` | An owner or admin hasn't allowed agents in the workspace |
| 403 | `insufficient_scope` | The person didn't allow this. `WWW-Authenticate` names the scope. |
| 404 | `not_found`, `family_not_found` | No such incident or report, or a family that isn't active in this workspace |
| 409 | `no_vulnerability` | There's no vulnerability id to link from |
| 409 | `incident_closed`, `report_filed` | The incident is closed, or the report is marked as submitted |
| 413 | `too_large` | The body is too large |
| 422 | `invalid_fields` | A field an agent can't write here, or too long. `allowedFields` lists the ones it can. |
| 429 | `rate_limited` | 60 writes in the last hour on this connection. Wait the seconds in `Retry-After`. |
| 429 | `registration_limit` | Agents opened 20 incidents in this workspace in the last 24 hours |

## Related

> [card] [MCP server](https://docs.pharussecurity.com/api-reference/mcp-server.md)

> [card] [Work with an agent](https://docs.pharussecurity.com/incidents/agents.md)

> [card] [Read workspace data](https://docs.pharussecurity.com/api-reference/read-api.md)

> [card] [SRP paste pack](https://docs.pharussecurity.com/incidents/srp-pack.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.