Overview
The NorthDuty REST API (v1) lets you read your projects and recent alerts, and subscribe a URL that receives every new alert the moment it opens. It is the same API the NorthDuty apps for Zapier, n8n and Make use, and you can call it directly from your own code.
- Base URL:
https://northduty.com/api/v1 - Format: requests and responses are JSON. Send
Content-Type: application/jsonwith request bodies. - Envelope: every successful response wraps its result in a top-level
datakey. - Timestamps: ISO 8601 with a UTC offset, for example
2026-11-06T09:14:03+00:00. - IDs: integers.
| Method | Path | What it does |
|---|---|---|
| GET | /me | The authenticated user and the projects the token can access |
| GET | /alerts | Recent alerts, newest first |
| GET | /hooks | Webhook subscriptions on your projects |
| POST | /hooks | Subscribe a URL to new alerts on a project |
| DELETE | /hooks/{id} | Remove a webhook subscription |
Authentication
The API uses personal API tokens, sent as a bearer token in the Authorization header.
- Sign in to NorthDuty and open Profile → API tokens.
- Create a token and give it a label, for example "Zapier".
- Copy the token. It starts with
nd_and is shown only once.
curl https://northduty.com/api/v1/me \
-H "Authorization: Bearer nd_your_token_here"
- A token acts as the user who created it and can reach every project that user can access.
- Tokens stay valid until you revoke them on the same screen (maximum lifetime 10 years). Treat a token like a password.
- A missing, invalid or revoked token returns
401with{"message": "Authentication failed."}. - Reading data works on every plan. Subscribing a webhook (
POST /hooks) needs a paid plan or an active trial, because the Free plan delivers alerts by email only.
GET /me
Returns the authenticated user and the projects the token can access. Integration platforms use it to test a connection and to fill project dropdowns.
curl https://northduty.com/api/v1/me \
-H "Authorization: Bearer nd_your_token_here"
Response 200:
{
"data": {
"email": "you@example.com",
"projects": [
{ "id": 81, "name": "Example Shop", "base_url": "https://shop.example.com" }
]
}
}
| Field | Type | Description |
|---|---|---|
email | string | Email of the user who owns the token |
projects[].id | integer | Project ID, used by /alerts and /hooks |
projects[].name | string | Project name |
projects[].base_url | string | The monitored site's base URL |
GET /alerts
Returns recent alerts across your projects, or for one project, ordered by when they were last detected (newest first). Both open and resolved alerts are included.
| Query parameter | Type | Required | Description |
|---|---|---|---|
project_id | integer | No | Only return alerts for this project. Omit for all projects. |
limit | integer | No | Number of alerts to return, 1–50. Default 10. |
curl "https://northduty.com/api/v1/alerts?project_id=81&limit=5" \
-H "Authorization: Bearer nd_your_token_here"
Response 200: {"data": [ ...alert objects... ]}. Each item is an alert object. An unknown or inaccessible project_id returns 404 with {"message": "Project not found."}.
GET /hooks
Lists the webhook subscriptions on every project you can access, including webhooks added in the NorthDuty app.
curl https://northduty.com/api/v1/hooks \
-H "Authorization: Bearer nd_your_token_here"
Response 200:
{
"data": [
{ "id": 312, "project_id": 81, "url": "https://hooks.example.com/northduty", "enabled": true }
]
}
| Field | Type | Description |
|---|---|---|
id | integer | Hook ID, used to unsubscribe |
project_id | integer | Project the hook belongs to |
url | string | URL that receives alert deliveries |
enabled | boolean | Whether deliveries are active |
POST /hooks
Subscribes a URL to new alerts on a project. Every alert that opens on the project is then delivered to the URL (see Webhook deliveries). Subscribing the same URL to the same project twice returns the existing hook instead of creating a duplicate.
| Body field | Type | Required | Description |
|---|---|---|---|
project_id | integer | Yes | Project to subscribe to |
url | string | Yes | HTTPS URL that receives deliveries, up to 255 characters |
curl -X POST https://northduty.com/api/v1/hooks \
-H "Authorization: Bearer nd_your_token_here" \
-H "Content-Type: application/json" \
-d '{"project_id": 81, "url": "https://hooks.example.com/northduty"}'
Response 201:
{
"data": { "id": 312, "project_id": 81, "url": "https://hooks.example.com/northduty", "enabled": true }
}
Errors: 402 on the Free plan, 404 if the project is not found, 422 if a field is invalid (for example a plain http:// URL).
DELETE /hooks/{id}
Removes a webhook subscription. Only webhook channels can be removed through the API; email, Slack and other alert channels on the project are not affected.
curl -X DELETE https://northduty.com/api/v1/hooks/312 \
-H "Authorization: Bearer nd_your_token_here"
Response 204 with an empty body. An unknown ID returns 404.
Webhook deliveries
When an alert opens on a project that has a hook, NorthDuty sends an HTTP POST to the hook URL with the alert object as the JSON body.
- When: once, when the alert opens. It is not sent again while the alert stays open, and it is not sent when the alert resolves. Poll
GET /alertsto seeresolved_at. - Maintenance windows: alerts that open during a project's maintenance window are recorded but not delivered.
- Which alerts: every alert rule on the project delivers to every hook. Filter on the
ruleandseverityfields. - Response: reply with any
2xxstatus within 10 seconds. Failed or timed-out deliveries are not retried.
POST /northduty HTTP/1.1
Host: hooks.example.com
Content-Type: application/json
{ ...alert object... }
The alert object
Returned by GET /alerts and sent as the body of every webhook delivery, so live and polled alerts have the same shape.
{
"id": 144,
"title": "[NorthDuty] Example Shop: SSL certificate nearing expiry",
"rule": "ssl_expiry",
"ruleLabel": "SSL certificate nearing expiry",
"severity": "warning",
"status": "investigating",
"project": {
"id": 81,
"name": "Example Shop",
"baseUrl": "https://shop.example.com"
},
"threshold": 14,
"observedValue": 9,
"unit": "days",
"comparator": "days until SSL expiry below threshold",
"dashboardUrl": "https://northduty.com/app/projects/81",
"message": "NorthDuty alert: SSL certificate nearing expiry\nProject: Example Shop\nURL: https://shop.example.com\nThreshold: 14 days\nObserved: 9 days (days until SSL expiry below threshold)\n\nOpen project dashboard: https://northduty.com/app/projects/81",
"first_detected_at": "2026-11-06T09:14:03+00:00",
"last_detected_at": "2026-11-06T09:14:03+00:00",
"resolved_at": null
}
| Field | Type | Description |
|---|---|---|
id | integer | Alert ID |
title | string | Short human-readable title |
rule | string | Alert rule type, see the table below |
ruleLabel | string | Human-readable name of the rule |
severity | string | warning or critical |
status | string | investigating, identified, monitoring or resolved |
project.id | integer | Project ID |
project.name | string | Project name |
project.baseUrl | string | The monitored site's base URL |
threshold | number or null | The threshold set on the alert rule |
observedValue | number, string or null | The measured value that tripped the rule. For user_flow_failure alerts it is the string failed. |
unit | string | Unit of threshold and observedValue: %, ms or days. Empty for journey failures. |
comparator | string | Plain-language description of the condition, for example below threshold |
dashboardUrl | string or null | Link to the project in the NorthDuty app |
message | string | Full plain-text alert, one line per detail |
first_detected_at | string | When the problem was first detected |
last_detected_at | string | When the problem was last seen |
resolved_at | string or null | When the alert resolved, or null while it is open |
rule value | ruleLabel | Unit |
|---|---|---|
health_score_below | Health score below threshold | % |
response_time_above | Page load above threshold | ms |
user_flow_failure | User journey failure | none |
ssl_expiry | SSL certificate nearing expiry | days |
security_score_below | Security score below threshold | % |
domain_expiry | Domain name nearing expiry | days |
Errors
Errors return a JSON body with a message. Validation errors also include errors, keyed by field name.
| Status | Meaning | Example body |
|---|---|---|
401 | Missing, invalid or revoked token | {"message": "Authentication failed."} |
402 | The workspace's plan does not include this (webhooks on Free) | {"message": "The Free plan only supports email notifications. Upgrade to a paid plan to send webhook alerts."} |
404 | Project or hook not found, or not accessible to this token | {"message": "Project not found."} |
422 | Request body failed validation | {"message": "Validation failed.", "errors": {"url": ["Hook URL must use https."]}} |
500 | Unexpected server error | {"message": "Internal server error."} |
Integrations built on this API
- Zapier: the "New Alert" trigger subscribes with
POST /hooksand loads sample data fromGET /alerts. - n8n: the verified NorthDuty node uses the same token and endpoints.
- Make: connect alerts to Make scenarios.
Questions about the API? Contact us.