# API quickstart

> Your first request in a minute, then a monitor and a test webhook your server receives: an API key, GET /me, POST /monitors, a webhook subscription and its test delivery.

Source: https://upfour.io/docs/api/quickstart

Afterwards you have made an authenticated request, created a monitor and received a signed webhook on your own server. You need a terminal with curl and jq, and for the webhook a URL that can be reached from the internet.

## 1 · Create a key

In the app open **Settings → API tokens** and create a key with the scopes `self.read`, `monitor.read`, `monitor.write`, `monitor.delete` and `webhook.write`. The key is shown once; put it in an environment variable, not in a script.

```bash
export UP4_API_KEY="pls_…"
```

## 2 · Ask who you are

The first request needs only `self.read` and shows what the key may do:

```bash
curl -s https://app.upfour.io/v1/me \
  -H "Authorization: Bearer $UP4_API_KEY"
```

```json
{
  "data": {
    "auth_method": "api_key",
    "org_role": "owner",
    "scopes": ["self.read", "monitor.read", "monitor.write", "monitor.delete", "webhook.write"],
    "api_version": "2026-05-07",
    …
  }
}
```

Every successful answer sits in `data`. A `401` means the header or the key is wrong: [keys and scopes](https://upfour.io/docs/api/keys-and-scopes.md) lists the causes.

## 3 · Create a monitor

The `Idempotency-Key` makes a retry safe: the same key never creates a second monitor. Put the key into a variable first, so that running the command again sends the same key. The id goes into a variable for the next steps.

```
MONITOR_KEY=$(uuidgen)

MONITOR_ID=$(curl -s -X POST https://app.upfour.io/v1/monitors \
  -H "Authorization: Bearer $UP4_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $MONITOR_KEY" \
  -d '{
    "name": "Example",
    "url": "https://example.com",
    "monitor_type": "http",
    "interval_seconds": 60,
    "regions": ["eu"]
  }' | jq -r .data.id)

echo $MONITOR_ID
```

The monitor runs from now on and appears in the app's monitor list. Every field it accepts: [monitors reference](https://upfour.io/docs/api/monitors.md).

## 4 · Receive a webhook

Subscribe a URL of yours to the events you care about. It must be reachable from the internet and answer with a 2xx status: an endpoint of your application, or a tunnel to your machine while you try this out. Addresses in private networks are refused.

```
SUBSCRIPTION_KEY=$(uuidgen)

curl -s -X POST https://app.upfour.io/v1/webhook-subscriptions \
  -H "Authorization: Bearer $UP4_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $SUBSCRIPTION_KEY" \
  -d '{
    "url": "https://your-server.example/up4-hook",
    "events": ["monitor.status_changed", "incident.opened", "incident.resolved"]
  }' > subscription.json

SUBSCRIPTION_ID=$(jq -r .data.id subscription.json)
jq -r .data.secret subscription.json   # whsec_…, shown only in this response
```

Store the `secret` with your other secrets; your server needs it to check the signature. If you run the create command again with the same key, the answer is the stored one, secret included, and no second subscription appears. Then have up4 send a test event right away. The call waits for that one delivery attempt (at most 10 seconds), which is why the `202` already carries the outcome:

```bash
curl -s -X POST https://app.upfour.io/v1/webhook-subscriptions/$SUBSCRIPTION_ID/test \
  -H "Authorization: Bearer $UP4_API_KEY" | jq '.data | {event, response_code, response_body}'
```

- `"response_code": 200`: your server received a `webhook.test` event. Its body is JSON with `id`, `type`, `created_at` and `data`; the `X-Up4-Signature` header signs it. `response_body` reads `"HTTP 200"`.
- `"response_code": null`: up4 got no answer. `response_body` says `"Delivery timed out"` when the server did not answer in time and `"Delivery failed"` for other errors such as a refused connection. Check that the URL is public and answers quickly. A host that does not resolve, or that resolves to a private address, is refused with a `400` before anything is sent.
- Any other code: your server answered with an error, and `response_body` is only the status line, for example `"HTTP 502"`. up4 never keeps the text of your answer.

From now on a monitor that goes down or comes back sends `monitor.status_changed` to this URL. How to check the signature and handle retries: [webhook receiver recipe](https://upfour.io/docs/api/recipes/webhook-consumer.md).

## 5 · Clean up

Delete the test monitor and the subscription when you are done. Both answer with `204`.

```bash
curl -s -X DELETE https://app.upfour.io/v1/monitors/$MONITOR_ID \
  -H "Authorization: Bearer $UP4_API_KEY"

curl -s -X DELETE https://app.upfour.io/v1/webhook-subscriptions/$SUBSCRIPTION_ID \
  -H "Authorization: Bearer $UP4_API_KEY"
```

## Where next

- [The contract](https://upfour.io/docs/api.md): envelopes, errors, versions and limits in one page.
- [Pagination](https://upfour.io/docs/api/pagination.md): lists return pages; follow `pagination.next_cursor`.
- [For AI assistants](https://upfour.io/docs/api/for-ai-assistants.md): which files to give a coding assistant.
- [Monitors as code](https://upfour.io/docs/api/recipes/monitor-as-code.md): keep your monitors in a file in your repository.
