Send your app's events
Report your own sign in, MFA, role, and API key events to a source, in one small signed JSON format.
These pages describe ShellOrbit as it runs today. Breaking changes to the HTTP API are announced before they ship, and existing request and response shapes stay supported.
Your own app knows things no vendor sends: who failed to sign in, who turned MFA off, who was made an admin. Send those events to a source of kind Your app, and the ready-made rules turn bursts and risky changes into findings.
The format
One JSON object per event, or an array of them:
{
"type": "login.failed",
"actor": "user_42",
"ip": "203.0.113.7",
"country": "IN",
"role": "admin",
"at": "2026-10-25T09:30:00Z"
}| Field | Required | Meaning |
|---|---|---|
type | Yes | login.failed, login.succeeded, mfa.disabled, role.granted, api_key.created, or password.reset |
actor | Yes | The account it is about: your user id, or an email address |
ip | No | The client address |
country | No | Two letter country code, if you already know it |
role | No | For role.granted, the role given |
at | No | When it happened, RFC 3339. Defaults to when it arrived |
Send an id rather than an email where you can: the event is kept for your plan’s history window.
Prove each request
Send the source secret in the X-ShellOrbit-Token header. A request without it, or with the wrong one, is kept as evidence and marked, and no rule reads it.
export async function report(event) {
const response = await fetch(process.env.SOURCE_URL, {
method: "POST",
headers: { "content-type": "application/json", "x-shellorbit-token": process.env.SOURCE_SECRET },
body: JSON.stringify(event),
});
if (response.status !== 202) console.warn("ShellOrbit did not accept the event", response.status);
}To sign instead of sending the secret, an admin sets the source’s proof to ShellOrbit signature under Manage. Then send ShellOrbit-Signature: t=<timestamp>,v1=<hex>, where v1 is an HMAC SHA256 of the Unix timestamp, a dot, and the exact body, made with the source secret. Requests more than five minutes old are refused.
Report after the response to your user has gone, or from a queue, so a slow request never slows a sign in.
What the ready-made rules detect
| Finding | Severity | Events |
|---|---|---|
| Possible credential stuffing: 10 failed sign ins on the source in 5 minutes | High | login.failed |
| Repeated failed sign ins for one account: 5 in 10 minutes | Medium | login.failed with the same actor |
| Administrator role granted | High | role.granted with role set to an administrator role |
| MFA turned off | Medium | mfa.disabled |
| API token created | Low | api_key.created |
| Sign in from a new country for an account | Low | login.succeeded with a country not seen for that actor |
password.reset and login.succeeded are kept as history and feed the rules above; add your own with custom rules.