# The platform contract
How the platform behaves, as opposed to what it exposes. Authentication, retry safety, the shape of an error body and the status that carries it, and the paging, ordering and filtering parameters — behaviour that applies to every endpoint in this reference and appears on none of them, because it is a property of the framework rather than a row in a catalog.

:::note
**What this page describes, and what it does not**

**Describes:** DgCore framework source, read at a commit pinned by the last ingest.

**Does not describe:** any running deployment, and no individual endpoint. Everything here is platform-wide default behaviour declared in the framework every service is built on. A deployment may configure it differently, and where a specific operation's own specification says something narrower, that operation is the authority for itself.
:::

:::note
**How the sentences on this page are checked**

**64 of 64** factual statements below were checked against framework source on the last ingest. A statement is checked by fetching the source it cites at a pinned commit and asserting that the lines it names still contain the literal text it was written against — so a citation cannot rot into decoration when the code moves. The source coordinates themselves are internal and are not published here.

Every statement published here checked out. When one stops doing so it is shown in place with the reason, and this count drops.

1 further checked statement is marked internal and not published in this build. It is checked on every ingest like the rest; it is not part of the contract a caller implements.
:::

:::note
**Before any of this: getting a token**

Your authorisation server, realm and credentials are deployment-specific. Contact the platform team that owns the gateway you are integrating against.
:::

| What | The question it answers | Checked statements |
| --- | --- | --- |
| Authentication | How do I authenticate? | 13 of 13 |
| Idempotency and retry safety | What happens if my request times out halfway through a transfer, and which calls are safe to retry? | 18 of 18 |
| The error envelope and status mapping | This call returned an error code — what do I do? | 22 of 22 |
| Paging, filtering and ordering | How do I page through a large result set? | 11 of 11 |

## Authentication

*How do I authenticate?*

Aura authenticates every request with a bearer JWT, verified against a public key chosen by tenant. Identity is carried in claims, not in parameters. Which scope or role an individual operation requires is declared nowhere this portal can read, and that absence is stated rather than filled.

13 of 13 checked statements in this section are standing on evidence that matched on the last ingest; 1 further statement is internal and is not published here.

### What to send

Send an access token as a bearer credential in the `Authorization` request header. The platform strips the literal prefix `Bearer ` and treats the remainder as a JWT.

> The access token is read from the Authorization request header and the literal prefix `Bearer ` is stripped before validation.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

The token is decoded as an **RS256** JWT and the signature check is not optional — the platform asks the JWT builder to require it explicitly rather than accepting an unsigned or differently-signed token.

> The token is decoded as an RS256 JWT with signature verification made mandatory.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

The public key is not one platform-wide key. It is chosen by **tenant**, from a configured list. A token presented for a tenant with no configured key is rejected outright — the platform does not fall back to another tenant's key, which is the behaviour that would quietly make the per-tenant split meaningless.

> The public key is selected per tenant from the configured key list, and a tenant with no configured key is rejected outright rather than falling back to another tenant's key.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

### What comes back when the token is bad

An expired token produces **HTTP 401**.

> An expired token sets HTTP 401 on the response and raises an error.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

:::caution
**401 does not carry the standard error envelope**

A status the platform sets on the response BEFORE raising the error is passed through untouched by the error handler, so a 401 arrives without the `error.code` / `error.message` body every other failure carries. The rule and the status list are on the **The error envelope and status mapping** page, which is the only place the two facts can be read together.
:::

There is one exception to expiry, and it is worth knowing because it will otherwise look like a bug: an expired token is accepted when the request already carries the platform's validated-token marker holding that same token's deterministic hash. That marker is set by an earlier hop, so within one already-validated call chain an expiry crossing mid-flight does not fail the downstream hops.

> An expired token is accepted without error when the request already carries the validated-token header holding that token's deterministic hash — the marker an earlier hop in the same call chain set.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

:::note
**A statement here is internal and is not published in this build**

One checked statement at this point in the page is marked internal and is withheld from the external site. It is not part of the contract a caller implements. This build does not name it, and the verified count above does not include it.
:::

### Identity is claims, not parameters

Nothing about who you are travels as a request parameter. The platform reads it from the token.

> Tenant, user, session, client and customer identity are read from the token's iss, sub, sid, azp and customerId claims by default.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

So a request does not carry a tenant id, a user id or a customer id as an argument, and an endpoint page on this portal will never show you one — the fields are simply not in the catalog, because they are not in the request.

Two claims carry authorisation material rather than identity.

> Two claims carry authorisation material: realm_access and privatePermission.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

### Headers that travel with every request

> The correlation and tenant headers are named Aura-Correlation-Id and Aura-Tenant-Code by default.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> The already-authorised signature header is named Aura-Authorized-Signature by default.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

### When validation does not run at all

Three conditions switch it off, and all three are ordinary operation rather than an error path.

> Token validation runs only when the cached TokenValidationOptions say it is enabled.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> When no options are cached, the middleware substitutes a disabled default — absence of configuration means validation does not run.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> Validation is skipped for the configured health-check path, and for any request path that matches no registered API, distributed-event or dynamic-API endpoint.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

The third is the service-to-service case. An internal hop that arrives with a signature header proving it was already authorised upstream is not re-validated.

> A request is treated as already authorised, and is not re-validated, when it carries a signature header whose decrypted value equals the request's current correlation id.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

:::note
**Why the bypass is scoped to one request**

The signature is the ENCRYPTED CORRELATION ID, compared against the correlation id of the request being handled. A captured header therefore does not authorise a different request; it authorises the one it was minted for.
:::

### What this page cannot tell you

:::info
**Per-operation scope and role are declared nowhere this portal reads**

Nothing on this page names the scope, role or permission a specific endpoint requires, because no source in this portal's store records it. That is not the same as the platform having no authorisation: `realm_access` and `privatePermission` above are read on every request, and the service-catalog layer acts on them. It is that the binding from an endpoint to the permission it demands is not in the service catalog, not in the BPMN, and not in the route table this portal parses from C# source — the fluent-builder walker in this repository sees `.RequireAuthorization(...)` and deliberately discards it as builder noise, so even the signal that passes through our hands is not kept. Ask whoever onboards you; do not infer it from anything here.
:::

:::note
**An absence carries no file and line**

Every factual sentence on this page is anchored to a line of framework source and re-checked on each ingest. The paragraph above is not, and cannot be: there is no line of code that says a thing is missing. It is reported as a gap, in prose, and is the one kind of statement here that a future run cannot verify for you.
:::

## Idempotency and retry safety

*What happens if my request times out halfway through a transfer, and which calls are safe to retry?*

Every non-GET endpoint on this platform honours one idempotency header. It is on by default, it replays only successful responses, it re-executes after a failure, and its identity includes the request body. Those four properties are the whole answer to "is this retry safe", and none of them was written down anywhere before this page.

18 of 18 checked statements in this section are standing on evidence that matched on the last ingest.

### The short version

Send `Aura-Idempotency-Key` on any mutating request you may need to retry. Send the **same key and the byte-identical body** when you retry it. Everything else on this page is the consequence of those two sentences.

> The idempotency header is named Aura-Idempotency-Key by default.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

### It is on everywhere, without anyone opting in

This is not a per-endpoint feature that a service enables. It is a platform default, so an endpoint page on this portal does not need to tell you that a particular route supports it: every non-GET route does, unless a deployment has switched the feature off in configuration.

> Idempotency is enabled by default, with a cache duration of 60.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> Idempotency engages only when the key header is present, the method is not GET, the feature is enabled, and the request is not a distributed event.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

Four conditions, and the ones that surprise people are the second and the fourth. **GET is excluded** — sending the header on a read does nothing. **A distributed event is excluded** — event delivery has its own delivery semantics and is not deduplicated here.

> When idempotency does not engage, the key header is removed from the request rather than left in place.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

That last detail matters for a gateway or BFF hop: when idempotency does not engage at a hop, that hop **removes the header** before continuing, so it is not forwarded onward from there.

### What a retry actually does

| You retry | The platform does |
| --- | --- |
| while the first request is still running | refuses with **409**, error code `ERR_AURA_3` |
| after the first request succeeded | **replays** the stored response, without running the handler again |
| after the first request failed | **re-executes**, because the failure evicted the entry |
| with the same key but a different body | **executes as a new operation** — the body is part of the key |
| after the cache window has passed | **re-executes**, because there is nothing left to replay |

> A second request arriving while the first is still in flight is refused with an idempotency error rather than being executed or queued.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> A completed cached response is written back to the caller without re-executing the handler.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> If the handler throws, the cache entry is removed and the exception rethrown, so a later retry re-executes rather than replaying the failure.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> The status returned for an idempotency error is Conflict.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> The error code carried by that 409 is ERR_AURA_3.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

:::caution
**A 409 here means "keep waiting", not "it failed"**

The in-flight refusal is the same HTTP status as a concurrency conflict, and the two are told apart only by the error code in the body: `ERR_AURA_3` is your own earlier request still running. Retry it again after a pause; do not treat it as a rejection and do not reissue with a fresh key, which would execute the operation twice.
:::

### Only success is replayed

> Only a 2xx response body is cached for replay.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> Success means a status of 200 through 299.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> A completed request whose response was not 2xx has its cache entry removed, so the key is free to be used again.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

So the cache is a **success cache**. A 400 or a 500 leaves no trace, and the same key retried afterwards runs the operation again. That is the right default for a client that is retrying because it did not like the answer — but it also means the header gives you no protection against re-submitting an operation that failed halfway through on the server. For that, see the note on partial work below.

### The body is part of the identity

This is the property most likely to catch you out, and it is the reason a retry must be byte-identical.

> The request body is part of the idempotency identity: the cache key ends with a deterministic hash of the body.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> An empty body contributes nothing to the key; a non-empty one contributes a deterministic hash of its content.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

Two requests with the same key and different payloads are treated as two distinct operations and both execute. A client that regenerates its payload before retrying — re-serialising with a new timestamp, a new request id, or a differently-ordered JSON object — will get a second execution and no warning.

> The key is scoped to the target application and the service code (or, where there is no catalog service, the route pattern and HTTP method) — it is not a platform-global identifier.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

The key is also scoped to the destination, so the same key value used against two different services does not collide.

### How long the window is

> The cache duration is in MINUTES: the cached response is given an absolute expiration of TimeSpan.FromMinutes(CacheDuration).
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

The unit is minutes, which is not visible from the option itself — the default of 60 is **60 minutes**, not 60 seconds. Read the deployment's own configuration before depending on the number; the framework default is all this page can state.

### Does the header survive a BFF or gateway hop

Yes, on hops the framework itself makes.

> On an internal service-to-service hop, every inbound request header except a fixed blacklist is forwarded to the downstream call.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> That blacklist is Host, User-Agent, Content-Length, Content-Type, Cookie and Transfer-Encoding — the idempotency key is not in it.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

:::info
**This is verified for framework-mediated hops only**

The forwarding above is the framework's own HTTP client. A service that hand-writes a forwarder, rather than using it, is outside anything this portal has read, and this page does not claim the header survives such a hop. If your call reaches its target through a service-specific forwarder, verify it there.
:::

### Timing out mid-transfer

Putting the above together for the question that actually gets asked — a payment POST that never returned:

- **With the key.** Retry with the same key and the same body. If the original is still running you get 409/`ERR_AURA_3` and should wait. If it completed successfully you get the original response, and the operation happened exactly once. If it completed unsuccessfully, or never got that far, you re-execute.
- **Without the key.** There is no deduplication at all, and a timed-out POST may or may not have committed. Nothing in the platform will tell you which.

:::caution
**Idempotency is not a transaction**

The eviction on failure is what makes a retry re-execute, and it is deliberate. It also means that if the handler failed AFTER doing part of its work, the retry starts again over that partial state. This layer guarantees at-most-once delivery of a SUCCESSFUL response for a given key and body; it does not roll anything back. Compensation on this platform is a separate mechanism, modelled in neither this layer nor in the process XML the flows section renders.
:::

## The error envelope and status mapping

*This call returned an error code — what do I do?*

Every failure the platform maps produces the same body shape and one of six statuses, chosen by exception class rather than by error code. The error catalog on this portal lists the codes and their messages; this page is the other half — the envelope they arrive in, the status that accompanies them, and the one case where no envelope is written at all.

22 of 22 checked statements in this section are standing on evidence that matched on the last ingest.

### The body

Every mapped failure returns the same shape: one `error` object, with a `code` and a `message`.

> The response body is an object with a single `error` member carrying `code` and `message`.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> The status is taken from the mapping and the response content type is set to application/json.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

Two members are added for the failures that carry more than one fact.

> A validation failure attaches a `validationErrors` list to the same `error` object.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> A bulk failure attaches a `businessErrors` list of the same code/message shape, one per failed item.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

So a client that wants to extract the code it just searched for on this portal reads `error.code`. There is no other place the code appears, and no variant envelope for a different failure family.

### The status is chosen by exception class, not by error code

This is the part that makes the mapping predictable and the part that most surprises readers: the HTTP status does **not** depend on which `ERR_*` code you get back. It depends on the kind of failure.

| Failure | Status | Code on the wire |
| --- | --- | --- |
| Validation failure | **400** | the validation code, plus `validationErrors` |
| Business rule refused the operation | **400** | the service's own business code |
| Malformed request body | **400** | `ERR_AURA_39` (not in the error catalog at this ref) |
| Service timeout | **408** | `ERR_AURA_33` (not in the error catalog at this ref) |
| Database concurrency conflict | **409** | `ERR_AURA_1` |
| Idempotency conflict, see **Idempotency and retry safety** | **409** | `ERR_AURA_3` |
| Downstream success body over the size limit | **502** | `ERR_AURA_41` (not in the error catalog at this ref) |
| Anything unmapped | **500** | `ERROR` |

> A validation failure is HTTP 400.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> A business exception is HTTP 400, and it is found by walking the inner-exception chain rather than only the outermost throw.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> A malformed request body is converted into a validation failure and therefore returns 400, not 500.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> A service timeout is HTTP 408 Request Timeout.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> A database concurrency conflict is HTTP 409 Conflict.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> An idempotency conflict is also HTTP 409 Conflict.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> A downstream success body over the configured size limit is HTTP 502 Bad Gateway.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> Anything the map does not name is HTTP 500.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> The generic internal-error code is the literal string ERROR.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> The concurrency-conflict code is ERR_AURA_1.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> The service-timeout code is ERR_AURA_33.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> The malformed-request-field code is ERR_AURA_39.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> The response-too-large code is ERR_AURA_41.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

:::caution
**409 is two different things**

A concurrency conflict and an idempotency conflict share a status and are told apart only by the code. `ERR_AURA_1` means someone else wrote the row while you were writing it, and retrying is reasonable. `ERR_AURA_3` means your own earlier request is still running, and retrying immediately will just get the same answer.
:::

> The business-exception search recurses through inner exceptions.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

A business exception is found by walking the inner-exception chain, so a business refusal that surfaced wrapped inside a transport or framework exception still returns 400 with its own code rather than a generic 500.

### The one case where the status is passed through and no envelope is written

> When the response status has already been set to one of the traced statuses, the mapper is not run and no error body is written.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> Those statuses are 401, 403, 404 and 408 by default.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

This is a real and useful behaviour and it has a consequence a client has to code for: **a 401, 403, 404 or 408 that was set on the response before the failure was raised arrives with no `error` body at all.** The expired-token 401 described on **Authentication** is exactly this case.

:::caution
**Do not assume every non-2xx has a parseable body**

Parse the envelope defensively. On the four statuses above there may be nothing to parse, and a client that unconditionally reads `error.code` will fail on the response rather than on the failure it describes. This exception applies to a status ALREADY SET on the response, not to the status the mapping chooses: a timeout the mapper handles returns 408 WITH an envelope, because the mapper set that 408 itself.
:::

### The message and the error catalog on this portal

> The message is looked up BY THE ERROR CODE in the platform's localization data, falling back to a built-in default only when the code has no resource.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

The message is resolved by looking the **error code** up in the platform's localization data — the same identifier space as the codes in the error catalog this portal already renders. That is why the catalog is useful at all: the code you get on the wire is the key you search here.

:::info
**What the join does and does not prove**

Every code named above is checked against the ingested error catalog as this page is built, and rendered with a chip saying whether it is there. A present code means the catalog carries an entry under that identifier. It is NOT proof that a running service's localizer reads THIS catalog: the framework declares only the `ILocalizer` interface, the concrete implementation is registered outside anything this portal has read, and no page here will claim a resource set it has not opened. An absent code is the stronger statement in the other direction — a code the framework can emit and the catalog does not carry, which is a real coverage gap in the catalog rather than a rendering problem.
:::

> When an error carries a downstream service's own error envelope in its message, that envelope's code and message are passed through — but the status becomes 500.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

One asymmetry worth knowing when you are reading a 500: if the failure came from a downstream service that returned its own envelope, that service's **code and message are preserved** but the status you receive is 500. So a 500 whose code is not `ERROR` is a downstream business failure wearing a transport status, not an unhandled crash.

### What this page still cannot tell you

:::info
**Which endpoint can return which code**

Unchanged by this page, and correctly so. The error catalog spans services, code prefixes do not partition by service, and nothing in any ingested source records the emitting endpoint. The error catalog page explains why that is not guessed at, and this page adds only the envelope and the status — not the endpoint join.
:::

## Paging, filtering and ordering

*How do I page through a large result set?*

The service catalog records that an operation supports paging, filtering or ordering, and does not record the parameter names — which is why several hundred endpoint pages on this portal say the names are unknown. The framework declares all five of them, in four files of a dozen lines each. Here they are.

11 of 11 checked statements in this section are standing on evidence that matched on the last ingest.

### The five names

> The catalog's own routing list query carries all five members at once, which is the shape a dynamically-routed list endpoint accepts.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

The catalog's own routing list query implements all four interfaces at once, so a single citation shows the whole surface. Individually:

> A paged request carries PageIndex and an optional PageSize.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> An ordered request carries OrderBy and OrderSort.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> A filtered request carries a single Filter member.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

### Paging

`pageIndex` and `pageSize`, and a response envelope of `total` and `data`.

> A paged response is an object with Total and Data.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

`pageSize` is nullable, so omitting it is meaningful — the platform's own type says a request may page without stating a size. What the service then does is a per-service decision this page has not read, so do not treat "omit it and get everything" as a contract.

:::note
**Why the endpoint pages still say the parameters are unrecorded**

They are describing the CATALOG, and they are right about it: `x-aura-capabilities` records that an operation supports paging and does not record what to call the parameters. This page is a different source — the framework — and the two are not merged, because a platform-wide framework default is a weaker claim about any individual endpoint than a per-endpoint catalog entry would be. Where an endpoint declares the capability, these are the names the platform uses.
:::

### Ordering

`orderBy` and `orderSort`, both plain strings.

:::info
**The framework does not constrain either value**

`orderBy` is a string and nothing in the type says whether it is a field name, a column name or a catalog code; `orderSort` is a string and nothing says whether the accepted values are `asc`/`desc`, `ASC`/`DESC`, or something else. This portal will not invent a vocabulary for a field whose type is `string`. Both are the one part of this page's subject that the framework declares without documenting.
:::

### Filtering

The single most misleading thing on the API reference today is `filter` rendered as type `object`, which tells a reader nothing. It has a declared shape.

> That filter is an object of Code, Expression and a Parameters map — not the untyped object the endpoint pages render.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

Three members:

- `code` — names a **formula definition**. The platform's formulas are seed data, not code, and this portal already renders them: the formulas and queries section lists every one with its expression and its named inputs.
- `expression` — the filter expression itself, validated separately from the code.
- `parameters` — a map from parameter name to value, feeding the expression.

> The filter's code names a formula definition, which is validated against the platform's formula catalog.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> The filter's expression is itself validated, separately from the code.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

That link is worth stating plainly, because it turns two unrelated-looking parts of this portal into one mechanism: **the filter you send names a formula that this portal documents.** A filter whose code is not a formula the deployment carries is rejected.

#### What the platform refuses

> Sending a filter to an operation whose catalog entry does not declare filtering is refused.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> When a filter is sent, code and expression must both be filled.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

> A filter's parameter names must be unique.
>
> *Checked against platform source at a commit pinned by the last ingest. The file and line are internal and are not published here.*

| If you | You get |
| --- | --- |
| send a filter to an operation that does not declare filtering | `ERR_AURA_20` |
| duplicate a parameter name inside the filter | `ERR_AURA_21` |
| send a filter with an empty code or expression | `ERR_AURA_22` |
| name a code that resolves to no formula definition | `ERR_AURA_23` |
| send an expression that does not validate | `ERR_AURA_24` |

All five are 400s, by the validation rule on **The error envelope and status mapping**. The first is the one to check first: the endpoint page on this portal shows a `filtering` capability chip exactly when the catalog entry declares it, so an operation with no such chip will refuse a filter rather than ignore it.

### What this page does not claim

:::info
**It does not say WHERE the parameters go**

Query string, request body, or both, and under what casing, is a routing-layer decision this page has not read. The names above are the property names on the platform's own query types; the wire placement for a given operation is not stated here and is not in the catalog either.
:::

:::info
**It does not override an endpoint**

If a specific endpoint's catalog entry declares request fields with different names, those fields are what that endpoint accepts. The endpoint page is the authority for one operation; this page is the platform default behind the capability flags.
:::

## String lengths

String fields carry no length bound in these documents: no `maxLength`, `minLength` or `pattern` is published on any of them. That is not an omission — the values are stored as text, so there is no highest-to-lowest range for a caller to code against.

What constrains a string is validation declared in the service catalog rather than a limit built into the application, and the catalog is per deployment. So a length that is accepted on one environment may be rejected on another, and the environment you were granted is the authority on what it takes. Treat these fields as unbounded here and let the service reject what it will not accept.

## Error codes this page names

This page names 11 platform error codes. Each is looked up in this site's error catalog as the page is built — the codes are not restated here, they are joined.

:::info
**3 of 11 are declared in framework source and absent from the catalog**

`ERR_AURA_33`, `ERR_AURA_39`, `ERR_AURA_41` are codes the framework can emit, with a default message compiled into it, and the ingested error catalog at this ref carries no entry under any of them.

That is a coverage gap in the catalog rather than a rendering problem here: a caller who receives one of these on the wire and searches this site for it finds nothing. It is stated rather than papered over, and the count is computed at build time, so it moves when the catalog does.
:::

| Code | In the error catalog at this ref |
| --- | --- |
| `ERROR` | present |
| `ERR_AURA_1` | present |
| `ERR_AURA_20` | present |
| `ERR_AURA_21` | present |
| `ERR_AURA_22` | present |
| `ERR_AURA_23` | present |
| `ERR_AURA_24` | present |
| `ERR_AURA_3` | present |
| `ERR_AURA_33` | absent |
| `ERR_AURA_39` | absent |
| `ERR_AURA_41` | absent |

## What is deliberately not here

- **Per-operation scope and role.** No source behind this site records which permission a specific operation requires, and nothing here guesses it. Ask whoever onboards you.

- **Rate limits.** Nothing in the framework this page reads declares one, and nothing here infers one from that absence.

- **Which operation returns which error code — partly answered.** 2568 of the 4984 published operations carry the codes found in their handler and validators, on the operation page and in the downloaded document.

- **Deployment configuration.** Every default named here can be overridden per deployment. This page states the framework default and says that is what it is.
