# Sandbox limitations

> What the Transaction Intelligence sandbox offers and what it does not promise: a single environment, no billing, uncalibrated scores, bounded retention, rate limits and twelve currencies.

The Transaction Intelligence API is open in a single environment: the sandbox. This page gathers what a developer needs to know before integrating: what the sandbox keeps and for how long, what it bounds, and what it does not promise. The API's limits can also be read in the contract at `https://api.noryo.app/openapi.json`; Platform's limits (usage register, active-key cap) are read on Platform.

## A single environment

Every key issued on [Platform](https://platform.noryo.app/keys) is a sandbox key, of the form `nry_txn_sbx_<key_id>.<secret>`. The contract also describes the form of a production credential (`nry_txn_live_<key_id>.<secret>`), but no key of that form can be created today: production is not open. A credential presented under a prefix that does not match the registered key is refused with a 401.

| Aspect | Sandbox | Production |
| --- | --- | --- |
| Keys | `nry_txn_sbx_…`, created on Platform once an owner or admin of the workspace has enabled the sandbox | On request, reviewed by Noryo: no `live` key is issued |
| Billing | None | Not open |
| Rate limits | Settings published in `x-noryo-rate-limit` (`debit_ti_v1`) | Not open: the provisional settings are not a commitment |

The limiter's buckets belong to one environment (`environment_scoped: true`). A `nry_mer_*` key (Merchant API) is refused on this API, and the reverse holds.

## No billing

The sandbox bills nothing. The only numbers that bound your requests are the operational rate limits, and the contract itself says what they are not: the `not` array of `x-noryo-rate-limit` lists it, in the [Reference](https://developer.noryo.app/en-GB/transaction-intelligence/reference).

The [Usage](https://platform.noryo.app/usage) page on Platform counts your authenticated requests; it is not a billing ledger. A 422 counts there as a request, a 401 never does, and nothing on it is billed. See [Usage and API keys](https://developer.noryo.app/en-GB/transaction-intelligence/usage-and-api-keys).

## Uncalibrated scores

No number returned by the API is a probability. The `confidence`, `strength` and `value` fields each carry an `x-noryo-score` extension that states their class, what they can be compared with and what they cannot; `calibration` is `non_calibree` everywhere and `is_probability` is `false`. A `0.85` is not "an 85% chance": it is a deterministic strength of evidence. For the `declared_constant` class (the `confidence` block of `resolution`), every transaction that hits the same pattern receives the same number. To decide, use `policy.outcome`, never a threshold on these values.

| Class | Meaning | Monotonic | Use |
| --- | --- | --- | --- |
| `declared_constant` (constante declaree) | Une valeur POSEE par la personne qui a ecrit le motif ou la regle, lue telle quelle. Elle ne resulte d'aucun calcul sur la transaction : toute transaction qui touche le meme motif recoit le meme nombre. Mesure sur 644 libelles : la precision n'est PAS monotone en ce nombre (0,85 -> 0,86 ; 0,88 -> 0,33 ; 0,92 -> 0,25 ; 0,97 -> 0,79). | false | Ne l'utilisez ni comme seuil ni comme rang. Utilisez `policy.outcome`. |
| `evidence_ratio` (part des signaux corroborants) | `present / possible` : combien de signaux corroborants ce TYPE sait produire, et combien sont effectivement la. Le denominateur DEPEND du type — 5 pour `refund_of`, 4 pour `reversal_of`, 2 pour `transfer_pair`, 5 criteres heterogenes pour une serie — et `corroborations` le rend a cote, parce que sans lui le nombre ne se lit pas. | within_type | Comparable entre deux objets du MEME type et de la meme `detector_version`. Jamais entre un `refund_of` et un `transfer_pair`, jamais entre une relation et une serie. |
| `calendar_fill` (taux de remplissage calendaire) | La part des periodes calendaires de l'etendue observee qui portent au moins une occurrence, pour l'hypothese de cadence retenue. ★ `0` signifie « aucune hypothese calendaire n'a ete retenue » (cadence `irregular_recurring`), pas « faible ». | within_cadence | Comparable entre deux series de MEME cadence. Une hebdomadaire sur 8 semaines et une annuelle sur 2 ans ne se comparent pas. |
| `anchor_stability` (stabilite de l'ancrage) | Un jeu de constantes par TYPE d'ancrage (1,0 pour un jour fixe de semaine, 0,9 pour un jour ajuste au jour ouvre) et deux formules lineaires sur l'etendue des jours pour `fixed_day_of_month` et `flexible_calendar_window`. `0` pour `unknown`. ★ `flexible_calendar_window` a DEUX regles selon la cadence : une constante 0,3 sans `day_range` en cadence `weekly`/`biweekly`, et `1 - etendue/10` (plancher 0,1) avec `day_range` en cadence mensuelle ou plus longue. | within_anchor_type_and_cadence | Comparable entre deux series de MEME type d'ancrage ET de MEME cadence. Un 0,33 `fixed_day_of_month` et un 0,3 `flexible_calendar_window` ne disent pas la meme chose ; un 0,3 `flexible_calendar_window` hebdomadaire et un 0,3 mensuel non plus. |
| `authority_tiebreak` (departage a autorite egale) | La confiance declaree de la source d'une activite. Elle n'entre en jeu qu'APRES la nature d'autorite (`curated` > `deterministic` > `provider` > `inferred` > `participant`), pour departager deux sources de meme nature. | within_nature | Ne compare jamais deux natures : un 0,95 `inferred` ne l'emporte pas sur un 0,90 `curated`. |
| `text_coverage` (couverture textuelle) | Un rapport de longueurs : la part du libelle utile que le motif apparie couvre, ou la part d'un motif compact qu'un fragment tronque recouvre. C'est une propriete du TEXTE, pas une preuve d'identite : un seuil dessus couterait 61 justes pour ecarter 17 faux. | true | Comparable entre libelles. N'en faites pas un seuil d'identite. |

Statistical calibration is not available in the sandbox and is not announced: when it exists, it will enter the contract through a declared version. See [Score semantics](https://developer.noryo.app/en-GB/transaction-intelligence/score-semantics).

## Retention

| Object | Duration | What happens next |
| --- | --- | --- |
| Enrichment (raw transaction) | 30 days: `raw_days` and `retention_limit_days` in `series[].history_coverage`, exact deadline in `expires_at` on read | `GET /transactions/v1/enrichments/{enrichment_id}` returns 404; the transaction is deleted and the API never brings it back |
| Derived memory of a series | 400 days (`derived_days`), counted from the last occurrence | Without a new occurrence, the derived state is deleted at the deadline; a later occurrence opens a new series |
| `Idempotency-Key` | At least 24 hours | A replay (200 with the original response) is only guaranteed within that window |
| Usage register on Platform | 35 days, per UTC day | Older days leave the register; a zero is neither "not observed" nor "unavailable" |

The two memories must not be confused: a purged transaction stays purged, but the series remembers that it happened. An enrichment response is operational and bounded: it is not a corpus. See [Read an enrichment](https://developer.noryo.app/en-GB/transaction-intelligence/read-an-enrichment).

## Recurrence: what the window can establish

The contract says it in one sentence: two occurrences make a series emerge, three can establish it, one never does. A monthly series can therefore become established within a few occurrences; a yearly series gains only one per year, in a derived memory (`derived_days`) counted from the last occurrence. Before reading `status` as a verdict, read `history_coverage` rather than `occurrence_count` alone:

- `sufficiency` answers a single question, "can the cadence be trusted?", with `sufficient`, `limited` or `insufficient`. `insufficient` means "our memory is too short to judge", never "there is no older occurrence";
- `observed_days` is the span actually observed, from the first known occurrence to the last;
- `first_occurrence_bounded` is `true` when observation begins with the first known occurrence: the series may have started earlier.

There is deliberately no `started_at` field: `first_observed_on` is the first day Noryo observed the series, not the day it began. See [Recurring series](https://developer.noryo.app/en-GB/transaction-intelligence/recurring-series).

## Rate limits and the key cap

Rate limits apply per key and per workspace; the numbers come from the contract:

`debit_ti_v1` — operational_burst_protection · token_bucket

- per key: 120 requests / minute (capacity 120, refill 2/s)
- per workspace: 600 requests / minute (capacity 600, refill 10/s)

« 120 requetes / minute » est traduit en seau a jetons : capacite 120 (la rafale toleree) rechargee a 2 jetons / seconde (le regime durable). Une clef au repos peut envoyer 120 requetes d'un coup, puis 2 par seconde ; sur la premiere minute apres un repos, jusqu'a 240 passent. Le workspace, lui, borne la somme de toutes ses clefs a 600 / minute (capacite 600, 10 / s) : cent clefs ne font pas cent fois la limite.

It is not: `sandbox_lifetime_quota`, `production_usage_quota`, `billing_entitlement`, `commercial_promise`.

Beyond them, the API answers 429 with `Retry-After`: nothing was computed or stored, and the request can be replayed after the delay. Creating several keys does not multiply the limit: the workspace bucket bounds the sum of all its keys. See [Rate limits](https://developer.noryo.app/en-GB/transaction-intelligence/rate-limits).

The number of active keys per workspace is capped; Platform reads the cap from the server, and revoked keys do not count towards it. Revocation is immediate: from the next request on, the key receives a 401 and no usage is counted. The secret is shown once, at creation, and Noryo does not keep it: a lost secret cannot be recovered, so revoke the key and create a new one. See [Authentication](https://developer.noryo.app/en-GB/transaction-intelligence/authentication).

## Accepted currencies

`amount.currency` accepts the twelve currencies of the `Money.currency` enum; any other is refused with a 422 `unknown_currency`. The number of decimal places allowed depends on the currency (`amount_precision_exceeds_currency`): `5.905 EUR` is refused, and so is `100.50 ISK`, because ISK and JPY have none and KWD has three. `amount.value` is a string, never a JSON number.

**Schema `Money`**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `value` | string | yes | Decimal as a string. A float would lose pence in silence. The number of decimal places allowed depends on the currency: `5.905 EUR` is rejected, and so is `100.50 ISK` (ISK and JPY have none, KWD has three). Pattern: `^-?(0\|[1-9][0-9]{0,15})(\.[0-9]{1,4})?$`. |
| `currency` | string | yes | A currency outside this list is rejected with 422 `unknown_currency`. Values: `CAD`, `CHF`, `DKK`, `EUR`, `GBP`, `ISK`, `JPY`, `KWD`, `NOK`, `PLN`, `SEK`, `USD`. |

See [Enrich a transaction](https://developer.noryo.app/en-GB/transaction-intelligence/enrich-a-transaction).

## What the sandbox does not promise

- **Production access.** It is not obtained through any sequence of actions: it is requested, Noryo reviews it, and an approval issues no `live` key: other conditions follow it. Billing is not open. See [Production access](https://developer.noryo.app/en-GB/transaction-intelligence/production-access).
- **A precision figure.** No score is calibrated and no accuracy rate is announced. A score compares only within its class and under the conditions its `x-noryo-score` declares (`comparable_within`: same type, same cadence or same nature depending on the class), at equal `detector_version`.
- **A forecast.** `expected_next_at` is absent until a series is established and its cadence regular; `expected_next_window` is a window, never a date.
- **Verification of your coverage declarations.** `provenance` is always `caller_asserted`: Noryo records what you assert and does not verify a bank's completeness. See [Observation coverage](https://developer.noryo.app/en-GB/transaction-intelligence/observation-coverage).
- **A public-holiday calendar.** No holiday is modelled; a shifted payment is absorbed by the window's tolerance, not recognised.
- **An ending inferred from silence.** `lifecycle.state = ended` is reserved for explicit terminal evidence; without complete coverage, an absence yields `unknown`, not `late`. See [Series lifecycle](https://developer.noryo.app/en-GB/transaction-intelligence/series-lifecycle).
- **A location.** `confidence.location` is always `0`: nothing is resolved on that axis in this version.
- **Shared truth from a single workspace.** Series and relationships stay private; knowledge of nature `participant` never becomes a truth of the entity graph.
- **An SDK.** The examples rely on `curl` and `fetch`, runnable as they are against the sandbox.
- **A payment role.** Noryo Transaction Intelligence is not a payment service provider, a bank, a BaaS or an accounting ledger.

## Further reading

- [Quickstart](https://developer.noryo.app/en-GB/transaction-intelligence/quickstart): from a Noryo identity to the first enrichment.
- [Errors](https://developer.noryo.app/en-GB/transaction-intelligence/errors): the problem catalogue, including `unknown_currency`, `not_found` and `rate_limited`.
- [Reference](https://developer.noryo.app/en-GB/transaction-intelligence/reference): the generated contract, with `x-noryo-rate-limit` and `x-noryo-score-classes`.

---

Canonical page: https://developer.noryo.app/en-GB/transaction-intelligence/sandbox-limitations
