Activity and account API
These endpoints read your activity log and manage your own account: profile, time zone, recovery email, password, two-factor sign-in and API key. They are the API side of the Activity and Settings pages.
Read your activity
GET /api/activity/ returns your account's audit trail, the same entries the Activity log page shows: sign-ins, machine changes, key generation and API calls. It is a read.
GET /api/activity/?action=&outcome=&before_ts=&before_id=
| Parameter | Meaning |
|---|---|
action |
One exact action name, such as api.request, machine.create, auth.login or apikey.generate. No wildcards. |
outcome |
success or failure. |
before_ts, before_id |
The next page: the time and id of the last entry you received. An unreadable before_ts is ignored. |
Entries come newest first, 50 per page, and are kept for 30 days (see retention).
curl -s "https://eyalsec.com/api/activity/?action=machine.create&outcome=success" \
-H "X-API-Key: es2_EXAMPLEKEYdoNotUse0000000000000000000000"
{ "entries": [ { "time": "2026-07-16T06:20:00Z", "id": 4812,
"action": "machine.create", "description": "Created machine web-prod-01",
"target": "web-prod-01", "source_ip": "203.0.113.4",
"auth_method": "apikey", "outcome": "success" } ] }
auth_method is session or apikey, so you can tell what your scripts did from what you did in the browser.
Your account
Everything on the Settings pages is also available here. Every GET below is a read; every POST is a write, so a read-only key is refused all of them.
Proof the API asks for
These endpoints ask for the same proof the Settings pages ask for, so the API is never an easier way into your account than the browser. An API key alone can read your profile and change your time zone; anything sensitive also needs your password, and some calls need a two-factor code too.
Send the proof as form fields with the rest of the request: password (or current_password when changing it), and totp where noted, when your account has two-factor sign-in on. A backup code works anywhere a totp code does. A wrong password is a 403; too many attempts are a 429 (see lockout).
Three of these calls destroy the key you are calling with: changing your password, generating a new key, and revoking the key. Read their warnings.
Read your profile
GET /api/account/ returns your profile, limits and permissions. It needs no password.
GET /api/account/
{
"username": "alice",
"email": "alice@example.com",
"timezone": "Asia/Jerusalem",
"email_verified": true,
"totp_enabled": true,
"email_2fa_enabled": false,
"machine_limit": 5,
"event_limit": 5000,
"event_limit_monthly": true,
"grant_raise": false
}
This is where a script checks what the account may do. machine_limit is your machine limit for each product, event_limit your event quota for each product's list (-1 means none), and grant_raise whether Raise rules are enabled for you. It never returns your password, your two-factor secret or any key.
Set your time zone
POST /api/account/timezone sets the time zone dates are shown in. It needs no password, like the time zone setting.
POST /api/account/timezone
Form field timezone: an IANA name such as Asia/Jerusalem, or empty to go back to automatic (your browser's zone). An unknown zone is a 422 and changes nothing.
curl -s https://eyalsec.com/api/account/timezone \
-H "X-API-Key: es2_EXAMPLEKEYdoNotUse0000000000000000000000" \
-d timezone=Asia/Jerusalem
{ "timezone": "Asia/Jerusalem" }
Change your recovery email
POST /api/account/email changes or removes your recovery email. It needs password, plus totp when two-factor sign-in is on.
POST /api/account/email
Form field email. A new address is not applied straight away: a confirmation link is sent to it, and the change happens only when that link is clicked, exactly as on the Settings pages. This stops someone holding a stolen session from pointing your recovery address at themselves.
{ "status": "confirmation_sent", "email": "new@example.com",
"note": "not applied yet: click the link sent to the new address to confirm" }
An empty email removes the address at once, which also turns off email sign-in codes and signs out every session; the answer's status is then email_removed. Sending the address you already have is a 409, an invalid address a 422, and asking again too soon after a confirmation was sent a 429.
Change your password
POST /api/account/password changes your password. It needs current_password and new_password; confirm is optional and must match when sent. The new password must follow the password rules (422 otherwise). See Change password.
POST /api/account/password
This revokes your API key. A password change signs out every session and deletes your key, so the key making this call stops working the moment it succeeds. Generate a new one afterwards.
{ "status": "password_changed",
"note": "your API key and all sessions were revoked; generate a new key" }
Delete your account
POST /api/account/delete deletes your account and everything in it. It needs password and answers 204. There is no undo. See Delete account.
POST /api/account/delete
Two-factor sign-in
These calls turn the authenticator app and email sign-in codes on and off, and replace backup codes. They mirror the Two-factor settings.
Start authenticator setup
GET /api/account/2fa/setup returns a new secret for your authenticator app. Turn otpauth_url into a QR code, or type secret into the app by hand.
GET /api/account/2fa/setup
{ "secret": "JBSWY3DPEHPK3PXP",
"otpauth_url": "otpauth://totp/EyalSec:alice?secret=...",
"expires_in_seconds": 600,
"note": "call /api/account/2fa/enable with a code from this secret to activate it" }
The secret is held for you for 10 minutes. Calling this again starts over with a new one.
Turn on the authenticator app
POST /api/account/2fa/enable finishes setup. It needs password, and totp: a code from the secret you just got, which proves the app is set up (it is not a second factor on this request).
POST /api/account/2fa/enable
The answer holds your backup codes, shown only this once. Every session is signed out.
{ "status": "2fa_enabled", "backup_codes": ["...", "..."],
"note": "backup codes are shown once; store them now. All sessions were revoked." }
A wrong code is a 422 and turns nothing on. Calling this without starting setup first is a 409.
Turn off the authenticator app
POST /api/account/2fa/disable turns off the authenticator app. It needs password, and signs out every session, so your other devices have to sign in again.
POST /api/account/2fa/disable
{ "status": "2fa_disabled", "note": "all sessions were revoked" }
New backup codes
POST /api/account/2fa/backup/regenerate replaces your backup codes; the old ones stop working. It needs password, and the new codes are shown only once. Without the authenticator app set up it is a 409.
POST /api/account/2fa/backup/regenerate
{ "status": "backup_codes_regenerated", "backup_codes": ["..."],
"note": "shown once; these replace any previous codes" }
Email sign-in codes
POST /api/account/2fa/email/enable and POST /api/account/2fa/email/disable turn email sign-in codes on and off. Both need password.
POST /api/account/2fa/email/enable
POST /api/account/2fa/email/disable
They answer {"status": "email_2fa_enabled"} and {"status": "email_2fa_disabled"}. Turning codes on needs a recovery email on file, since that is where they are sent (409 without one).
Your API key
These calls show, replace and revoke your account's API key. The account has one key at a time.
Look at your key
GET /api/account/apikey describes the key on your account: its first characters, its scope, when it was made, when it expires, and when and from where it was last used.
GET /api/account/apikey
{ "exists": true, "key_prefix": "es2_3f9a...", "scope": "full",
"created_at": "2026-07-01T09:00:00Z", "expires_at": "2027-01-01T00:00:00Z",
"last_used_at": "2026-07-16T06:20:00Z", "last_ip": "203.0.113.4" }
scope is full or read_only. expires_at and last_used_at are absent when there is no expiry or no use yet, and with no key the answer is just {"exists": false}. The key itself is never returned: only a fingerprint of it is stored.
Generate a key
POST /api/account/apikey/generate creates a new key. It needs password, plus totp when two-factor sign-in is on. See Generate.
POST /api/account/apikey/generate
Optional form fields: expires, a future date as YYYY-MM-DD (omit it for a key that never expires; a past date is a 422), and read_only, any non-empty value for a read-only key.
This replaces your current key. Generating a new key ends the one you are calling with. Copy
api_keyfrom the answer, or you will lock your script out.
{ "api_key": "es2_NEWKEY...",
"note": "shown once, and it replaces any previous key for this account" }
Revoke your key
POST /api/account/apikey/revoke deletes your key; it stops working at once. It needs password and answers 204. If you revoke the key you are calling with, this is the last call it makes. See Revoke.
POST /api/account/apikey/revoke
What the API does not do
A few account actions are only available in the dashboard.
- Signing out. A key has no session to sign out of; revoke the key instead.
- Session timeout. The idle timeout applies to browser sessions and is set on the Settings > Sign-in & sessions page.
- Confirming a recovery-email change. That happens by clicking the link sent to the new address; a key cannot prove you own an address.
- The es-chromium install command. It is on the Machines page. See the es-chromium install command.