# Events API: curl and scripts

> Send and resolve events from any script, cron job or piece of your own code.

Web page: https://infrainbox.app/docs/sources/events-api/

`curl` is a first-class integration. If a tool has no adapter of its own, or you're writing your own check, POST JSON to `/v1/events` with your source's key and you're done.

**Tested with:** curl 8.x, but any HTTP client works — examples below in bash, Python and PowerShell.

## Prerequisites

- A source's ingest key, shaped `iik_src_…`. Create one under **Add source** (dashboard screen `/sources/new`) → a generic source, or reuse an existing generic source's key.

## Send an event

Only `title` is required:

```bash
curl -fsS https://infrainbox.example.com/v1/events \
  -H "Authorization: Bearer iik_src_…" \
  -H "Content-Type: application/json" \
  -d '{"title":"Backup failed","severity":"critical"}'
```

```python
import requests

requests.post(
    "https://infrainbox.example.com/v1/events",
    headers={"Authorization": "Bearer iik_src_…"},
    json={"title": "Backup failed", "severity": "critical"},
    timeout=10,
).raise_for_status()
```

```powershell
Invoke-RestMethod -Method Post -Uri "https://infrainbox.example.com/v1/events" `
  -Headers @{ Authorization = "Bearer iik_src_…" } `
  -ContentType "application/json" `
  -Body (@{ title = "Backup failed"; severity = "critical" } | ConvertTo-Json)
```

## The fields

| Field | Meaning |
|---|---|
| `title` | What happened, one line. The only required field. |
| `severity` | `DEBUG`, `INFO`, `SUCCESS`, `WARNING`, `ERROR` or `CRITICAL` (case-insensitive; `warn`, `err`, `crit`, `fatal` are accepted aliases). Missing means `INFO`. `SUCCESS` always acts as a recovery. |
| `action` | `trigger` (default) or `resolve`. |
| `type` | A dotted event type you choose, e.g. `backup.failed` — useful for routing rules later. |
| `resource` | What it's about, as `kind:name`, e.g. `vm:103`. |
| `dedupKey` | Your own grouping key — the strongest signal for "this is the same problem as last time." Compared verbatim, case-sensitively. |
| `body` | Plain-text detail, up to 16 KiB. |
| `tags` | An array of free-form labels, up to 20. |
| `url` | An absolute link back to your own view of this. |
| `occurredAt` | RFC 3339 timestamp, if different from now. More than 7 days old or 5 minutes in the future is replaced by the receive time. |

## Firing and resolving with the same `dedupKey`

The pattern for anything with a clear start and end — a backup job, a sync, a scheduled task:

```bash
# It fails
curl -fsS https://infrainbox.example.com/v1/events \
  -H "Authorization: Bearer iik_src_…" -H "Content-Type: application/json" \
  -d '{"title":"Backup failed","severity":"CRITICAL","dedupKey":"backup:vm103"}'

# Later, it succeeds — this closes the incident the first call opened
curl -fsS https://infrainbox.example.com/v1/events \
  -H "Authorization: Bearer iik_src_…" -H "Content-Type: application/json" \
  -d '{"title":"Backup succeeded","action":"resolve","dedupKey":"backup:vm103"}'
```

Reuse the exact same `dedupKey` for every event about the same thing; InfraInbox never guesses it for you.

Most event types you send resolve on their own after 24 hours of quiet if nothing else says otherwise. The [InfraInbox watcher](https://infrainbox.app/docs/sources/watcher.md)'s own condition types are the exception: they ship with a preset that never expires them on a timer, so a container still down or a disk still full stays open until a matching recovery event (or a manual resolve) — not just because a day went by.

## Sending several at once

```bash
curl -fsS https://infrainbox.example.com/v1/events \
  -H "Authorization: Bearer iik_src_…" -H "Content-Type: application/json" \
  -d '{"events":[{"title":"Backup failed","severity":"ERROR","dedupKey":"backup:vm103"},{"title":"Backup succeeded","severity":"SUCCESS","dedupKey":"backup:vm104"}]}'
```

Up to 100 events per request, all-or-nothing.

## Retrying safely

Send an `Idempotency-Key` header so a retried request (after a timeout, say) can't create a duplicate — the same key replays the original response instead of sending twice. Reusing a key with a different body is rejected.

## A sender that can't set a header

For a tool that can only POST to a fixed URL with no custom header (Dozzle, for one): enable the **path** auth mode on this source, create a path key, and post to:

```
https://infrainbox.example.com/hooks/generic/iik_src_…
```

> **Warning:** A key in the URL can end up in a reverse proxy's access log or your shell history. Prefer the `Authorization` header wherever the sender supports it.

A body that isn't valid event JSON still isn't lost — it's kept as an unparsed event you can look at, rather than rejected outright.

## Check it works

Send one event and look at **Sources** (dashboard screen `/sources`) — the source switches to **Receiving events**, and (for `WARNING` or above) an incident opens.

## Next

- [Event fields and severities](https://infrainbox.app/docs/reference/event-fields.md) — the full reference
- [REST API](https://infrainbox.app/docs/reference/rest-api.md) — everything else the API can do
- [Routing rules](https://infrainbox.app/docs/notifications/routing.md) — route your own event types
