Thanks to visit codestin.com
Credit goes to docs.mantis.privacykey.org

The CLI is the recommended interface, but everything it does is exposed over a plain JSON HTTP API. Useful for scripts in languages without a Mantis client, or for testing the trigger path with curl:

Endpoint reference

Management endpoints are API-key authenticated with Authorization: Bearer mantis_live_.... Dashboard helper endpoints that need browser access also accept the mantis_session httpOnly cookie. Public trigger, status, health, wallet, and dev-inbox routes are called out explicitly below. Parsed request bodies are bounded. POST /api/keys and POST /api/api-keys reject JSON bodies over 64 KiB with 413 payload_too_large. Apple Wallet log bodies are capped at 32 KiB, and the dev inbox capture path is capped at 1 MiB. Supported installer type values are shell, shell-sudo, macos-login, macos-boot, macos-wake, macos-network, linux-boot, linux-wake, linux-network, windows-logon, windows-wake, windows-network, css-background, js-clone-detector, nfc-ndef, homeassistant, homeassistant-receiver, and scrypted. Webhook destinations get an HMAC secret. Outbound raw-webhook deliveries include X-Mantis-Timestamp and X-Mantis-Signature: sha256=<hex> over <timestamp>.<json body>. The plaintext secret is only shown on create, replace, explicit reveal, or rotate responses; normal listing returns a fingerprint.

API key scope: full vs enroll

Every API key carries a scope, orthogonal to is_admin:
  • full (default) — behaves as described throughout this page. Subject to the admin / non-admin visibility rules.
  • enroll — create-only. An enroll key may call only POST /api/keys. Every other management route (list/read/update/delete keys — including the ones it created — plus /api/hits/recent, /api/api-keys, the audit log, and any session-reachable route) returns 403 forbidden, and an enroll key cannot log in to the dashboard. is_admin: true together with scope: "enroll" is rejected at validation.
Enroll keys are the intended credential for MDM / fleet provisioning: you embed one on every managed machine and accept that a curious user will extract it. An extracted enroll key cannot read hit history, alert routing or signing secrets, and cannot enumerate or list keys — but it is not inert, so size the blast radius before you embed one:
  • It can confirm and retrieve any key whose external_id it guesses. A POST /api/keys that collides with an existing external_id returns that key’s trigger URL, memo, public_id and expiry ("reused": true, HTTP 200) — see Idempotent creation — regardless of which API key created it. mantis device derives external_ids deterministically as mantis:device:<os>:<normalized-name>:<slug>, so an attacker who knows your naming convention can guess a machine’s ids and read back that machine’s canary URLs, which is exactly what lets an intruder route around the tripwires. Each such claim is recorded in the audit log as key.claimed.
  • It can supply destinations on creation, and Mantis fires the activation ping synchronously — so the key can make your instance POST to an attacker-chosen HTTP(S) endpoint (private, loopback and metadata addresses are rejected unless ALLOW_PRIVATE_WEBHOOKS=1) or, if SMTP_URL is set, send it mail.
See the Kandji recipe in the product repo’s deploy/kandji/.

Idempotent creation

POST /api/keys also accepts an optional external_id (1–128 chars, matching ^[A-Za-z0-9][A-Za-z0-9._:-]*$) stored on a unique column. When supplied, a repeat POST with the same external_id returns the existing key — "reused": true with HTTP 200 instead of 201 — rather than minting a duplicate. The other body fields (memo, destinations, …) apply only when the row is actually created; a later claim never mutates what the key was first configured with. Keys created without an external_id are unaffected (unique constraint treats NULLs as distinct). This is the mechanism the fleet-enrollment flow relies on — one key per machine serial, so re-running enrollment on a reimaged machine reuses its key instead of littering the list. Enroll-scoped callers (and callers claiming another creator’s external_id) get a reduced response shape — trigger URL and identity only, no alert routing or signing secrets. A claim that races a concurrent delete returns 409 conflict; retry.

Response kinds for the trigger endpoint

Webhook payload

Webhook payloads also include a parsed host_context object when the hit came from one of our installer snippets:
For a shell-sudo hit you’d see "source": "shell-sudo", "sudo_cmd": "apt update --quiet". For a linux-network hit, "source": "linux-network", "network_interface": "wlan0". Fields not relevant to the installer are null. host_context is null for hits that didn’t include X-Mantis-* headers (e.g., a file/folder key, or a regular curl to the URL). Webhooks are sent through a Postgres-backed retry queue (no Redis required). On failure the notification is retried with exponential backoff: 1m, 5m, 30m, 2h, 12h (each with ±20% jitter), giving up after 5 attempts. Delivery state is tracked on each hit’s notifications array.