BInformed

API documentation

v2 contract. When in doubt, the server code is authoritative.

Base URL:https://api-binformed.gericos.com

Authentication: httpOnly token cookie (send requests with credentials: 'include') or Authorization: Bearer <token>. Sending notifications: X-API-Key. Emails are normalized (lowercase/trim).

1) Authentication & registration

Short JWT access token (15 min) + opaque rotating refresh token. On 401, make one /v1/auth/refresh then replay the request.

POST/v1/auth/signup

Creates an unverified account and sends the verification email. CAPTCHA (Turnstile) required once the server enables it.

curl -X POST https://api-binformed.gericos.com/v1/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"your-password","captcha_token":"<turnstile>"}'
# -> { "ok": true, "verification_required": true }

Password ≥ 8; disposable emails refused. After signup: “Verify your email” screen (no direct login).

POST/v1/auth/resend-verification

Resends the verification link. Generic response (no account enumeration).

curl -X POST https://api-binformed.gericos.com/v1/auth/resend-verification \
  -H "Content-Type: application/json" -d '{"email":"user@example.com"}'

GET/v1/auth/verify?token=…

Link clicked in the browser (single-use token, 24 h) → HTML page served by the API. Nothing to code on the front.

POST/v1/auth/login

Returns token + refresh_token (and sets the cookie). No longer returns api_key. 403 email_not_verified if the account is unconfirmed.

curl -s -X POST https://api-binformed.gericos.com/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"your-password"}'
# -> { "token": "<JWT 15 min>", "refresh_token": "<opaque>", "refresh_expires_at": 1739999999 }

POST/v1/auth/refresh

Rotation: returns a new pair and resets the cookie; the old refresh is revoked.

curl -s -X POST https://api-binformed.gericos.com/v1/auth/refresh \
  -H "Content-Type: application/json" -d '{"refresh_token":"<your-refresh-token>"}'
# -> { "token": "…", "refresh_token": "…", "refresh_expires_at": … }

POST/v1/auth/logout

Authenticated. Revokes all refresh tokens and clears the cookie.

curl -s -X POST https://api-binformed.gericos.com/v1/auth/logout \
  -H "Authorization: Bearer <JWT>" -H "Content-Type: application/json" -d '{}'

GET/v1/me

Current profile: { id, email, created_at }. No more api_key.

2) API key

POST/v1/keys/rotate

Authenticated. The only way to obtain/reset the API key, shown once (format gn_…, stored hashed). Regenerating immediately invalidates the old one.

curl -s -X POST https://api-binformed.gericos.com/v1/keys/rotate \
  -H "Authorization: Bearer <JWT>" -H "Content-Type: application/json" -d '{}'
# -> { "api_key": "gn_XXXXXXXX…" }

3) Devices

POST/v1/devices/register

curl -s -X POST https://api-binformed.gericos.com/v1/devices/register \
  -H "Authorization: Bearer <JWT>" -H "Content-Type: application/json" \
  -d '{"platform":"android","fcm_token":"<FCM/APNS token>","app_version":"1.0"}'

platform"android" | "ios" (validated).

POST/v1/devices/unregister

curl -s -X POST https://api-binformed.gericos.com/v1/devices/unregister -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" -d '{"fcm_token":"<FCM token>"}'
curl -s -X POST https://api-binformed.gericos.com/v1/devices/unregister -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" -d '{"all": true}'

GET/v1/devices?limit=&offset=

The fcm_token is not exposed.

4) Sending notifications

POST/v1/notify

Auth via X-API-Key or Bearer. message required (≤ 2000), title ≤ 200, url must be https. Refused (403) if the account is unverified.

curl -s -X POST https://api-binformed.gericos.com/v1/notify \
  -H "Content-Type: application/json" -H "X-API-Key: <your-api-key>" \
  -d '{"title":"Build finished","message":"CI OK","url":"https://example.com/build/123"}'
# -> { "ok": true, "pushed": 1, "failed": 0 }

5) Message history

GET/v1/messages?days=&limit=&before=

Paginated (keyset). days default 30, max 365; limit default/max 500; before = cursor (messages with strictly lower id). id stable, ts in ms.

curl -s -X GET 'https://api-binformed.gericos.com/v1/messages?days=365&limit=500' -H "Authorization: Bearer <JWT>"
# -> {
#   "items": [ { "id": 128, "title": "…", "body": "…", "url": "https://…", "ts": 1752345600000 } ],
#   "paging": { "limit": 500, "has_more": true, "next_before": 97 }
# }

Next page: pass next_before into ?before= until has_more: false. Client-side dedup via id.

6) Account export & deletion

GET/v1/me/export[?format=zip]

GDPR export: JSON, or ZIP (JSON + CSV per collection).

curl -L -X GET 'https://api-binformed.gericos.com/v1/me/export?format=zip' -H "Authorization: Bearer <JWT>" -o export.zip

DELETE/v1/me/delete

Deletes the account and all data (cascade, irreversible).

7) Errors

JSON responses { "error": "<code>" } with HTTP status:

  • 400invalid email, password too short (min 8), disposable_email_not_allowed, captcha_failed, url must be https, missing_refresh_token
  • 401invalid credentials, Invalid token (→ try a refresh), invalid_refresh, invalid api key
  • 403email_not_verified (login & notify), cors_forbidden
  • 409email exists
  • 429too_many_attempts (10 / 15 min / IP), signup_ip_daily_limit

8) Security

  • HTTPS only; notification url forced to https.
  • Short access token (15 min); refresh opaque, stored hashed and rotated.
  • Per-user API keys, stored hashed, regenerable via /v1/keys/rotate.
  • CORS: origin allowlist with Access-Control-Allow-Credentials.
  • Hardened signup: CAPTCHA + disposable blocking + mandatory email verification.