Skip to content

Conventions

Shared rules that apply across every endpoint of the e-Próspera API. Most reference pages link here instead of restating these conventions.

Base URL

All paths in this documentation are relative to one of these origins:

EnvironmentOrigin
Productionhttps://portal.eprospera.com
Staginghttps://staging-portal.eprospera.com

So GET /api/v1/legal_entities/{id} in production is GET https://portal.eprospera.com/api/v1/legal_entities/{id}.

There is no separate api.eprospera.com host — the API ships from the same origin as the consumer portal.

Authentication

Every authenticated request uses the Authorization header with a Bearer token:

text
Authorization: Bearer <token>

The token can be one of three things:

Prefix / shapeTypeNotes
sk-...Standard API keyPersonal / first-party use. Never share. Not scoped.
ak-...Agent KeyDelegated, scope-checked. See Agent Keys.
JWT (RS256)OAuth 2.0 access tokenIssued by /api/oauth/token. Carries scopes from the user's consent grant.

Not every endpoint accepts every credential — see the Authentication block on each reference page. In particular, /api/v1/me/* does not accept standard API keys.

Content type

  • Request bodies on POST endpoints (other than OAuth /token) are application/json.
  • OAuth /api/oauth/token is application/x-www-form-urlencoded per RFC 6749.
  • All responses are application/json unless explicitly noted.

Response envelopes

Most endpoints wrap successful payloads:

Endpoint shapeEnvelope
Single resource (GET /resource/{id}){ "data": { ... } }
Collection (GET /resource){ "data": [ ... ] } (see Pagination)
Create flows{ "data": { ... }, "nextSteps": { ... } }
Coupon-pay endpoints{ "success": true, "data": { ... }, "message": "..." }
Search (POST .../search){ "results": [ ... ] }
Verify (POST /verify_rpn){ "result": "...", "active": true | false }

OAuth and JWKS endpoints follow their own RFCs (RFC 6749 / RFC 7517 / OIDC Core 1.0) and do not use the data wrapper.

Error envelope

Errors return a non-2xx status and a JSON body shaped like:

json
{
  "error": "short_machine_readable_code_or_message",
  "error_description": "Human-readable explanation. Optional.",
  "details": [
    /* Zod issues or per-field errors. Optional. */
  ]
}

Some legacy endpoints return only { "error": "..." }. Treat both shapes as the same contract: error is always present on a non-2xx response.

details is most often a Zod issue array on 400 responses:

json
{
  "error": "Invalid request",
  "details": [
    {
      "code": "invalid_type",
      "expected": "string",
      "received": "undefined",
      "path": ["applicationData", "name"],
      "message": "Required"
    }
  ]
}

HTTP status codes

StatusMeaning in this API
200Success.
400Validation error or conflicting state (e.g. application already submitted).
401Missing, malformed, or invalid credential. Re-authenticate.
403Credential is valid but lacks the required scope (Agent Key) or insufficient OAuth scope.
404Resource does not exist or the caller cannot see it. The two are intentionally indistinguishable.
409Conflict (e.g. legal-entity name already taken).
429Rate limit exceeded. See Rate limits.
500Server error. Safe to retry with backoff for idempotent operations only.

Timestamps

All timestamps are strings in ISO 8601 / RFC 3339 UTC form, e.g. 2024-01-15T10:30:00.000Z. Never assume local time. Date-only fields (none currently in the API) would use YYYY-MM-DD.

Nullability

Optional fields are explicitly typed as T | null in this documentation and are present in responses with the value null rather than being omitted. If a field is documented as non-nullable, expect it to always be present.

IDs

All identifiers are RFC 4122 v4 UUIDs (string form, lowercased, dashed) unless noted. Resident Permit Numbers (RPNs) are 14-digit numeric strings that start with 8 (legal entity) or 9 (natural person).

Rate limits

Rate limiting is per-endpoint, not global, and is rolling out gradually. Currently:

EndpointLimits
POST /api/v1/verify_rpn5,000 requests / 24 h and 50 / minute, per API key
POST /api/v1/registries/legal_entities/search5,000 / 24 h and 50 / minute, per API key
All other endpointsNo documented limit today.

When throttled, the response is 429 with { "error": "Rate limit exceeded", ... }. No Retry-After header is currently emitted; back off exponentially. Expect rate limits to be added to more endpoints over time — design for retries.

Pagination

List endpoints other than /api/v1/nomadlayer/applications are not currently paginated. They return the full result set under a top-level data array in a single response. This is the current contract, but agents should design clients to tolerate a future pagination envelope being added without breaking. The pattern we will converge on is the one already shipped on /api/v1/nomadlayer/applications:

json
{
  "data": [
    /* ... */
  ],
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalCount": 134,
    "pageCount": 7
  }
}

When that ships, query parameters will be ?page=<int> (default 1, min 1) and ?pageSize=<int> (default 20, max 100). Until then, treat result sets as bounded by the underlying data — there are no current pagination parameters to send and no pagination field to read on these endpoints.

Versioning

All public endpoints live under /api/v1/. Breaking changes will go to a new /api/v2/ prefix; existing /api/v1/ endpoints remain stable. Additive changes (new fields, new optional query params, new endpoints) can land under /api/v1/ without notice — clients should ignore unknown fields rather than reject them.

Idempotency

There is no Idempotency-Key header today. POST /legal_entity_applications is not idempotent: a repeated request creates a duplicate Draft application. Implement deduplication on the caller side until idempotency keys are added.

Webhooks

There are no public webhooks. Approval, payment, and signature events must be discovered by polling the relevant GET endpoint (e.g. GET /api/v1/legal_entity_applications/{id} for statusId changes). Recommended polling cadence is 5–30 seconds during an active flow, then exponential back-off.