PADI API
Base URL: https://api.ronniri.com
PADI supports inline email/password or encrypted saved credentials. POST /padi/certifications returns consumer cards and, for professionals, pro items. POST /padi/credentials reads professional credentials. Supply raw:true to preserve upstream fields. Consumer credentials are not applicable. GET variants use a reusable PADI session. Interactive MFA/email verification must be completed on PADI itself. Never disable TLS verification.
Authentication and credential choices
RonNiri API: authentication and accounts
The Copy to agent button at /docs copies the complete Markdown reference: shared guidance and every service section. On /docs/:service, it copies that service's reference and its shared authentication guidance only. The full export is also available at /docs/index.md; individual exports use /docs/:service.md.
Base URL: https://api.ronniri.com. Local default: http://localhost:3000.
Two ways to supply service credentials
Every service request needs gateway authorization: X-API-Key: rni_... or Authorization: Bearer rni_.... A browser signed in to the console can also call APIs with its HttpOnly session cookie and CSRF token for writes. Registration alone cannot access the service. Keys belong to approved users, including the admin. An optional deployment-wide API_KEY enables direct requests without creating a user account; treat it as a root secret. It cannot access any user's saved credentials or management endpoints.
- Inline credentials: send provider credentials in a JSON POST body over HTTPS. They are used for the upstream operation and are not saved in MongoDB. Never put passwords or keys in query strings. No extra client-side encoding replaces HTTPS. Configure proxies and clients not to log request bodies or authorization headers.
- Saved credentials: sign in, save credentials in the console, then send
useSavedCredentials: truewith a user API key. The server decrypts only the caller's service credentials. Passwords are never returned by the credential-list endpoint. Credentials are encrypted with AES-256-GCM, bound to the user and service. Back up the encryption key separately from the database.
Inline PADI request
POST /padi/certifications
X-API-Key: YOUR_GATEWAY_KEY
Content-Type: application/json
{"email":"[email protected]","password":"YOUR_PADI_PASSWORD","raw":false}
PADI uses email as the username field. Stateless calls clear their in-memory provider client after the response. To reuse login, POST the same email/password to /padi/sessions, then use its sessionId in X-Session-Id on GET /padi/certifications, GET /padi/credentials, GET /padi/session, POST /padi/sessions/refresh, and DELETE /padi/sessions.
Saved PADI request
Save {"email":"[email protected]","password":"..."} with PUT /auth/credentials/padi, then:
POST /padi/certifications
X-API-Key: YOUR_USER_KEY
Content-Type: application/json
{"useSavedCredentials":true,"raw":false}
The same flag works on POST /padi/sessions, POST /padi/credentials, POST /lifesavingSociety/lookup and POST /piitel/login. Do not mix saved and inline credential fields. Piitel login requires an existing Piitel X-Session-Id; create it with POST /piitel/sessions first.
One-request wrapper
POST /:service/request accepts method (default GET), path (service-relative, no query string), query object, body object, and either credentials or useSavedCredentials:true. It dispatches only an existing service endpoint and enforces that endpoint's key permission. It cannot invoke account/admin routes or session/login routes. Query values follow the target endpoint's rules. Responses are wrapped in { "data": ... }.
{
"method": "GET",
"path": "/orders",
"useSavedCredentials": true
}
Send this to /piitel/request with a key containing piitel:account:login and piitel:orders:read. For inline Piitel login replace the flag with "credentials":{"username":"[email protected]","password":"..."}. For PADI, POST /padi/request with method:"POST", path:"/certifications", credentials {email,password}, and optional body:{raw:true}. Lifesaving credentials are {identifier}; AliExpress buyer credentials are {accessToken}. KSP/Amazon account login is unsupported.
Provider sessions created by the wrapper are discarded after the operation by default. Set keepSession:true to return sessionId and expiresAt for follow-up cart/session operations; this additionally requires that service's sessions:manage permission. Set sessionId instead of credentials to reuse a session. Piitel supports cartMode:"classic" for bundles. Discarding a local session does not undo an upstream action: a Piitel account cart change persists at the store. Check state before retrying an uncertain mutation.
Account lifecycle
On the first successful database initialization, the server creates admin username admin, generates a random password and prints it once to server logs. Sign in and change it before using APIs or creating keys. This is an ordinary user account with an additional admin role; its saved provider credentials are private to it. Startup does not reset an existing admin or reprint its password.
POST /auth/register with {username,password} creates a pending registration request, not an active user. Passwords must be 8โ256 characters. Usernames are normalized to lowercase, 3โ64 characters, using letters, digits, dots, dashes and underscores. The admin approves/rejects requests in the console. Pending and rejected applicants cannot sign in, save credentials, or create keys. There is no automatic approval. Admin can disable approved users; disabling invalidates access immediately. Re-enabling requires new login and keys.
POST /auth/login accepts {username,password} and sets an 8-hour HttpOnly, SameSite=Strict cookie (Secure in production). Response includes user and csrfToken. GET /auth/me returns the current user and CSRF token. All authenticated browser writes require X-CSRF-Token. Account management accepts browser sessions only; even a root API key cannot approve users or manage accounts.
PATCH /auth/profile accepts currentPassword, optional username and optional password. Changing profile requires the current password and invalidates existing browser sessions and API keys. Initial admin login requires a new password. POST /auth/logout invalidates the current browser session.
API keys and permissions
POST /auth/keys: {name,scopes,expiresAt?} creates a key and returns the plaintext key exactly once. MongoDB stores only its SHA-256 hash and display prefix. GET /auth/keys lists metadata; DELETE /auth/keys/:id revokes one. Optional expiresAt is an ISO future timestamp. Maximum 50 unrevoked keys per user. Revocation, user disablement and password/profile changes are checked on every request.
GET /auth/permissions lists the current service permissions. Scope syntax is service:resource:action, for example piitel:catalog:read, piitel:cart:write, padi:certifications:read. A key may include several services and permissions. root grants all current and future service API permissions, not administrator account-management access. No scopes are granted by omission. A write permission does not imply read; select both if needed. Session creation/refresh/deletion requires sessions:manage. Checkout submission has its own checkout:submit permission. The wrapper checks its target and any login/session permission it needs. Each service documentation page lists the exact permission for every registered route.
GET /capabilities reports adapter support and verification state. Provider session IDs are random, scoped by service and owned by the authenticated user (or the legacy direct key). They cannot be reused across users. Users may use their own session across their own keys only if each key has the requested permission. Local provider sessions expire after 30 minutes idle and are lost on restart. Account data, keys and saved credentials persist in MongoDB.
Saved credential management
GET /auth/credentials returns configured services and update timestamps, never plaintext secrets. PUT /auth/credentials/:service replaces credentials; DELETE removes them. Supported schemas:
| Service | JSON |
|---|---|
| padi | { "email": "...", "password": "..." } |
| piitel | { "username": "...", "password": "..." } |
| lifesavingSociety | { "identifier": "ABC123" } |
| aliexpress | { "accessToken": "..." } |
Administrator endpoints
All require an admin browser session, completed initial password change, and CSRF on writes.
| Method | Endpoint | Effect |
|---|---|---|
| GET | /auth/admin/registrations |
Pending requests without password hashes |
| POST | /auth/admin/registrations/:id/approve |
Explicitly approve; body {} |
| DELETE | /auth/admin/registrations/:id |
Reject pending registration |
| GET | /auth/admin/users |
Users without password hashes |
| PATCH | /auth/admin/users/:id |
{status:"disabled"} or {status:"approved"}; cannot disable admin |
Request security and errors
Production requires HTTPS, a configured PUBLIC_URL and the trusted Coolify reverse proxy. Do not expose the Node port directly to the internet. The app never logs request bodies, keys or saved provider passwords. The initial admin password is intentionally printed once as requested; rotate it immediately and restrict log access. Account passwords use salted scrypt (N=131072,r=8,p=1). Login/registration attempts are rate-limited, and concurrent password hashing is bounded.
Common errors: 400 INVALID_INPUT/JSON_REQUIRED, 401 UNAUTHORIZED/SESSION_EXPIRED, 403 FORBIDDEN/ADMIN_REQUIRED/CSRF_REQUIRED/SESSION_FORBIDDEN/PASSWORD_CHANGE_REQUIRED, 409 USERNAME_UNAVAILABLE/SESSION_BUSY, 429 RATE_LIMITED, 503 BUSY. Errors have {code,error,details?}. Provider errors preserve their existing codes. Keys and credentials belong in headers/JSON bodies, not URL query parameters. No live payment validation has been performed.
Every registered endpoint
| Method | Path | Required key permission |
|---|---|---|
| POST | /padi/certifications |
padi:certifications:read |
| POST | /padi/credentials |
padi:credentials:read |
| POST | /padi/sessions |
padi:sessions:manage |
| GET | /padi/session |
padi:sessions:manage |
| POST | /padi/sessions/refresh |
padi:sessions:manage |
| GET | /padi/certifications |
padi:certifications:read |
| GET | /padi/credentials |
padi:credentials:read |
| DELETE | /padi/sessions |
padi:sessions:manage |
| POST | /padi/request |
Target operation permission |
PADI
curl https://api.ronniri.com/padi/certifications \
-H 'X-API-Key: YOUR_API_KEY' -H 'Content-Type: application/json' \
-d '{"email":"[email protected]","password":"your-password","raw":false}'
POST /padi/certificationslogs in for this request, selects consumer/pro endpoints, and returnseCards,withoutECards, endpoint statuses, andproItemsfor professional accounts.POST /padi/credentialsaccepts the same body and returns separate professional credentials asitemswith endpoint status.raw: truepreserves all upstream issuer fields; default is false.
For a consumer account, the professional credentials route returns empty items with endpoint status not_applicable, without making an unnecessary professional-endpoint request. Professional endpoint failures retain status failed and their diagnostic code.
Stateless requests own their PADI client and clear password/token references on success and failure. For fewer upstream logins, use a reusable session:
| Method | Path | Body / behavior |
|---|---|---|
| POST | /padi/sessions |
{ "email": "...", "password": "..." }; local session ID and public account/token-expiry metadata |
| GET | /padi/session |
X-Session-Id; public session metadata |
| GET | /padi/certifications |
X-Session-Id, optional ?raw=1 |
| GET | /padi/credentials |
X-Session-Id, optional ?raw=1 |
| POST | /padi/sessions/refresh |
X-Session-Id and {}; refresh or re-login |
| DELETE | /padi/sessions |
X-Session-Id; clears local password/token references |
Sessions are isolated, held in memory, expire after 30 idle minutes and disappear on restart. Passwords remain in the session client only to support re-login after refresh rejection; tokens/passwords are never returned to callers. Token refresh and one retry on upstream authentication rejection are retained. Interactive verification must be completed with PADI when required. Partial endpoint failures remain visible; a failed endpoint with an empty array is not proof of no certifications.
The client uses strict certificate verification and Node 24 system CAs. Its upstream response and timeout limits remain 10 MiB and 20 seconds. Tests cover consumer/pro routing, expiry, renewal, partial failures, raw data and cleanup. Explicit eCardInfo: null is empty; an omitted field or malformed list is a schema failure, rather than a successful zero-certification result.
Live verified on 2026-10-20 with the authorized consumer account: login, both certification endpoints, token renewal, and the shared HTTP API's session creation, raw certification response, consumer credentials not_applicable, refresh and logout/session rejection. Professional account routing and credentials remain contract tested; no professional test account has been verified.