Inspect a raw JSON submission
When you send JSON through a source's automatic or guided interpretation form, Tessera returns a durable receipt. Follow Inspect receipt, or reopen the source's JSON ingestion history after reloading the console.
The source, project, environment, submission time and submitted row count identify the batch. Keep the receipt ID when investigating an uncertain submission.
Send a test event
A JSON stream source's page has a Send a test event card. It sends one record to the source, shows the receipt, and follows it until the receipt lists the event and the entity it belongs to, which you can open from the page.
On a source that has no structure yet, the console's test event is accepted on its
own into a separate test entity type (tessera_test): it never mixes with your
records and does not decide the source's structure. On a source that already has a
structure, a test event goes through it like any record. Your own records must not
use the entity type name tessera_test; a follow-up reserves it.
The console's test event has exactly entity_id (text without #, at most 256
characters), event set to test.ping and occurred_at (a time with its offset),
and nothing else. Raw JSON records you send to a source decide how Tessera reads
them once enough have arrived (two by default), with the usual choices: event-name
maps, a time zone declaration, a raw-source preset. Events sent to
POST /events never do.
A test event you edit (another member, another event type, a time without its
offset, an entity id over 256 characters), or one you send in the same batch as
another record, is not accepted on its own: it joins the source's sample under its
own field names. Unless your records also use entity_id, event and
occurred_at, with a time that has its offset, an id of at most 256 characters and
no #, it then holds the source's first interpretation for attention while it is
retained (24 hours by default). The console refuses an entity id with #
before sending; one sent another way has the same effect, and is quarantined once
the source has a structure. Try edited test events on a separate source.
A completed receipt of test events only is never used to decide a source's
structure, and datasets built on your records' entity type never read the test
entity type. While the source has no structure yet, the same test event sent
again to it is stored once. A test event sent with a real customer's id is stored
as tessera_test/<id>, a separate entity: erasing that customer does not reach
it, so erase tessera_test/<id> too. On a source bound to a raw-source preset, on
a source under manual control, or when the project's approved contract excludes
test.ping, the test event is not accepted on its own and is handled like any
other record.
Other records stay queued, or held for review, until Tessera interprets them, which happens once semantic interpretation is running. A source with a saved mapping is under manual control and refuses raw JSON (see below). Until the receipt lists the event, the page says so and does not report it as arrived. Automatic checking stops after two minutes; Check again reads the receipt once more.
The same page shows Stream liveness: when the last event was received, raw record
counts, and the accepted count for events sent to POST /events. Stream sources
have no syncs, so batch sync readiness does not apply.
Sources under manual control
Raw JSON is accepted only while automatic or guided interpretation governs the
source. A source under manual control, including every source with a saved
manual mapping, refuses POST /sources/{source_id}/raw-events with
failed_precondition and reason manual_source, and no receipt is created.
Saving a mapping takes the source under manual control for good. Send that
source's data through its mapped intake (its syncs, or canonical events with
POST /events), or use a source without a mapping for raw JSON.
Understand the outcome
| Outcome | Meaning | Next step |
|---|---|---|
| Queued | The raw batch is retained; row evaluation has not finished. | Wait or refresh the receipt. |
| Waiting for source evidence | The source does not yet have enough sample records for interpretation (two by default). On a source that has no structure yet, the console's test event does not wait for this; see Send a test event. | Add representative records and review source interpretation. |
| Needs review, counts not evaluated | No complete row outcome has been recorded. Guided sources may need proposal approval; small samples may need more evidence. Legacy receipts may also lack counts. | Review the source interpretation panel. |
| Normalized and queued | A canonical event has been written to the durable delivery queue for this row. | Follow the entity link and look for its event ID in the timeline. |
| Quarantined | The row could not be normalized using the approved binding. Other rows may still proceed. | Download row errors and review the binding or correct the input. |
| Normalization completed | All submitted rows have canonical delivery-queue entries. | Verify downstream arrival separately. |
| Raw records expired | Retained raw input is no longer available for reprocessing. | Use recorded outcomes when available. Resend the same batch with a new Idempotency-Key to process its remaining rows (see Retry and duplicate handling), or correct and resubmit input. |
The receipt is not a ledger-delivery confirmation or a model/scoring readiness signal. A source can finish normalization while the shared model is still being prepared. Accepted event references remain inspectable when retained raw input expires. Unknown legacy counts display Not evaluated, never an inferred zero.
When all rows have been evaluated, normalized and quarantined counts add up to the
submitted count. These are input-row counts, not counts of unique downstream
events. Downloaded errors contain zero-based record_index values and safe
normalization guidance; the console displays row numbers starting at 1. Raw record
values and provider exception details are not included.
Semantic preparation is tracked separately
Each normalized row also records its original semantic preparation work. That work is not part of the receipt outcome: it can be pending while the receipt already shows Normalization completed. When the project's daily semantic inference budget is exhausted, the source interpretation panel shows budget exhausted and the deferred requests resume automatically after the next UTC day while their source records are retained. A transient preparation failure is retried automatically and is shown as needing attention until it is published. Neither case changes or re-sends accepted canonical events.
Raising the source policy's daily inference budget makes budget-deferred work due right away; lowering it applies from the next admission. Switching a source to manual pauses its waiting semantic work. Resuming onboarding, or switching back to guided or automatic, picks the paused work up again unless its source records have expired or a manual mapping now applies. Any other policy change, such as a new inference timeout, stops the waiting work instead of preparing it under different rules.
Retry and duplicate handling
An exact repeat of the same raw records, in the same order and in the same
project/environment/source, reuses its receipt ID. It does not create another
receipt. The response still says queued: it acknowledges the batch, so read the
receipt for its true state. A completed receipt is returned unchanged.
Returning this acknowledgement does not change a quarantined row into an accepted
row by itself; a quarantined row is evaluated again only while its receipt is
being processed.
If the receipt's raw input expired before the receipt completed (some rows not
yet evaluated, or rows still quarantined for review), the exact repeat re-admits
it: the same receipt is queued again, the records are retained for a fresh
retention window, and its remaining rows are processed. Rows the receipt had
already normalized are not accepted or counted a second time. Semantic
preparation of those earlier rows that stopped when their records expired is not
resumed. A repeat that reuses an Idempotency-Key within its 24-hour window gets
the stored earlier response back and re-admits nothing, so send the repeat after
expiry with a new key (or none).
A repeat within 30 minutes of a pending receipt's retention end extends its
retained_until to a fresh window, which gives a receipt you have just been told
is queued time to be processed. It can still expire first when processing is far
behind, or near the end of its replay window (below); it then reads Raw records
expired.
A re-admitted or extended window never runs past 37 days after the receipt first
arrived (the replay window). Once less than 30 minutes of that window is left, an
expired receipt is not re-admitted: the repeat is refused with
failed_precondition and reason receipt_beyond_replay_window. Read the receipt
to see which records it did not accept, and send those again as a batch in another
order or split differently, so it gets a new receipt (the same batch maps back to the expired one). A record with a stable event_id that the ledger
already holds is not appended twice (see below), so re-sending an accepted record
is harmless only when its event id is stable.
A sync or upload lands the same rows in the same 64-record batches every time, so it skips such a batch on every later sync too. It takes in every other batch and reports the skipped records on its handoff. To take in the rows those receipts did not accept, land them again in another order or together with other rows (for example a new upload), or send them as a new raw-events batch.
If a batch holds a record of an entity that was deleted, the re-admitted input is dropped again once the receipt is processed, as for any new batch.
A different batch has a different receipt. If it repeats an event with the same
entity, timestamp and stable event_id, the downstream ledger recognizes the
existing event key and does not append a second event. The second receipt still
counts the submitted row as normalized and queued. Changing the timestamp or
entity changes the ledger key; keep all of these fields stable when retrying the
same logical event.
Event ids and entity ids with #
The first 16 characters of an event id must not contain #. A row whose
event-key field yields such an id is quarantined (row error
normalization_failed). The source's exceptions name the cause: A raw record remains quarantined: event_id must not contain '#' in its first 16 characters. The row is never ingested. To recover, correct the ids and send
those records again in a new batch; re-sending the unchanged batch only
returns the same receipt. If the sample records already show the problem
during onboarding, the source needs attention (identity_ambiguous, naming
the event-key path) before anything is approved. Correct the ids, or leave
that field unbound so the platform derives event ids.
An entity id (the entity-key field) must not contain # anywhere. Such a row
is quarantined the same way (row error normalization_failed), and the
source's exceptions say A raw record remains quarantined: entity.id must not contain '#'. If the sample records show it during onboarding, the source
needs attention (identity_ambiguous, naming the entity-key path). The
entity key is required, so the fix is to correct the ids and send those
records again in a new batch.
Records accepted before these rules stay accepted.
Client-side malformed JSON and rejected HTTP requests stay visible inline and do not receive a successful receipt. If the response is uncertain, inspect history before resubmitting the unchanged batch.
Read receipts through the API
Use a key with the read scope for the same project and environment:
GET /sources/{source_id}/ingestion-receipts?limit=20
GET /sources/{source_id}/ingestion-receipts/{receipt_id}
History is newest first. Pass the returned next_cursor unchanged to continue;
cursors cannot be reused for another source, project or environment. These reads
do not trigger ingestion or interpretation.
This receipt history covers raw-source JSON submissions. Canonical events sent to
POST /events and connector batch syncs have different acceptance and verification
paths; a raw receipt must not be used as proof that those workflows completed.
Raw input retention
Each receipt keeps its raw records for the retention set in the source's
onboarding policy (limits.retention_seconds, 24 hours by default). You can
shorten it, for example to keep less raw data, down to one hour. A shorter value
is refused with invalid_argument (a field_violation on
body.policy.limits.retention_seconds), because the records could expire before
they are processed.
Below 24 hours, know what the shorter window means:
- Semantic preparation that waits for the daily inference budget resumes after the next UTC day only while its records are retained, so it may expire first. Sending the batch again recovers its intake, not that preparation: work that stopped when its records expired is not resumed.
- A repeat with the same
Idempotency-Keywithin its 24-hour window returns the stored earlier response even if the receipt has expired since. Only a repeat with a new key (or none) re-admits an expired receipt. - When processing is behind, a receipt can expire before it is processed. It
then reads Raw records expired; send the same batch again with a new
Idempotency-Key(or none) to re-admit it. - A receipt waiting for a person's review also expires at its retention: a
guided source's batches wait for the proposal to be approved, and an automatic
source's batches wait while a decision needs attention. If the review is not
settled within the retention, the receipt reads Raw records expired. An
approval is applied over exactly the records its proposal was derived from
(the batches whose records its samples took, up to the policy's
limits.sample_records, 256 by default), so it is refused withfailed_preconditionand reasonreview_records_expiredas soon as any batch held for the review is no longer retained (its retention ended, or a data-subject erasure dropped its records). Recover in this order: first send every such batch again with a newIdempotency-Key(or none), or land the same rows again for a synced or uploaded source, which queues it for review again; then approve within the retention. When only some batches expired you can instead resume onboarding to re-derive a proposal from the records still retained and approve that one; the expired batches' records are then not taken in (when too few records are left to derive a proposal from, re-sending is the way). A proposal's samples can also include already-accepted records of completed batches; when only those expired, re-sending re-admits nothing, so resume onboarding to re-derive a proposal instead. A batch that first arrived over 37 days ago cannot be re-admitted: send the records its receipt did not accept again as a batch in another order or split differently, so it gets a new receipt (the same batch maps back to the expired one). A repeat within the last 30 minutes of a held receipt's retention extends it, which keeps a review open longer. - A stored policy below one hour (a legacy value;
0expires every batch as it lands) is shown as a notice on the source's onboarding status: supply a policy of at least one hour.
Re-admission works within 37 days of a receipt's first arrival; see Retry and duplicate handling for that limit.
Read processing counts for a source
Receipts describe one batch. For totals across every receipt of a source, read
the per-stage counts with the same read key:
GET /sources/{source_id}/processing-counts
The response is one snapshot (as_of) with the binding and scoring slot
generation it refers to:
- raw: received records split into duplicate, quarantined or excluded (with their recorded reasons), expired before evaluation, pending, and the distinct canonical events. These parts always add up to the received total.
- canonical: records with a canonical outbox entry, the distinct events the ledger stores once, and how many were published.
- semantic: one unit of work per canonical event, split into available, pending, timeout, budget exhausted, unavailable, expired and stopped.
- scoring: entities scored, scored prefixes published and prefixes placed by the trajectory projector for the source's current slot generation.
The scoring slot (binding.slot_id) is the source's automatic slot while that
slot still serves the model automatic selection activated, at that activation.
Read the slot to see which kind it is.
If you activate a model on that slot yourself, the counts follow your activation
only when it is a project-trained semantic model (semantic.v2) whose pinned
semantic inputs were built from this source. Any other model reads unavailable
(no_automatic_slot), unless another slot holds this source's explicit activation.
Otherwise the scoring slot is the live realtime slot where you most recently activated such a project-trained model for this source, for as long as that slot still serves it. If that slot later serves another model, the counts become unavailable until you activate one for the source again; an older slot is not used as a fallback.
An internal generation bump keeps an explicitly activated slot counting at the new generation. The automatic slot is different: a bump changes its generation away from the one automatic selection recorded, so it is treated like a manual change and its counts read unavailable, unless another slot holds this source's explicit activation.
Shadow and batch-only slots publish no scored prefixes, and neither do models
without semantic inputs (v1 and description models), so they report scoring
counts as unavailable (no_automatic_slot), never zero.
Every count comes from facts recorded by the stage that owns it. When a stage has
no evidence yet, the count is null with an unavailable_reason (for example
no_automatic_slot before a model is active, or not_tracked for work recorded
before these facts existed). A null count is never zero.
Model preparation uses a recent window
When an automatic source is prepared for a shared model, preparation reads the
source's most recent 8,192 canonical events at the preparation cutoff, ordered by
event time and then event ID. A larger source is not refused. If older events share
the first selected instant, that whole instant is left out so the window is an exact
time range. The prepared dataset records this window as its time_range. Earlier
events stay stored; they are outside only the preparation sample.
Because a shared instant is left out whole, a window can be much smaller than 8,192 events. For example, if more than 8,192 events share one instant and one newer event arrives later, the window holds only that newer event. Preparation continues with that small window and records it in the service log.
An event that cannot be read stops preparation only when it falls inside the window. Older unreadable events are ignored.
Two conditions still stop preparation. Today their reason is recorded only by the service; it is not yet shown in the API or the console.
snapshot_scan_budget_exceeded: the project's event snapshot is larger than 256 MiB. Preparation is refused for that model; this is a current service limit.source_window_tie_exceeds_budget: more than 8,192 events share the newest timestamp (for example date-only timestamps that all become midnight). Preparation waits and retries hourly with a fresh snapshot, so newer events with later timestamps clear it. Sending full date-time timestamps avoids it.
Read how many events were processed
"Processed" means more than accepted: an event counts once the model actually
scored it. Read it with the same read key:
GET /sources/{source_id}/processed-counts
GET /sources/{source_id}/processed-counts?generation=1
processed is the number of distinct accepted canonical events of the source
that scoring folded into a published scored prefix in the source's scoring slot
(the slot processing-counts reports) at one activation generation. It is counted
from facts scoring records with each publication, never estimated.
- An event is the exact ledger event (its entity, timestamp and event ID), the
same identity the accepted
distinctcount uses: two entities that share a customer event ID are two events. - By default the current generation is read. Activating a model again starts a
new generation, and with it a new count. An earlier generation of the same
slot is readable with
generationwhen the model it served was built for this source (every automatic model the source used, and an explicitly activated project model);generation_bundle_idnames the model counted. A generation that served another source's model, a v1 model or a manual takeover isnullwithgeneration_not_attributed. - An event older than the model's scoring window when its entity was scored is not folded into any prefix, so it is not counted as processed.
- Semantic enrichment is its own stage: an enriched event is not processed until it is scored.
- With no scoring slot the count is
null(no_automatic_slotorno_active_bundle). If the generation has publications recorded before these facts existed it isnullwithnot_tracked, never a smaller number. This holds for every generation published before this count was introduced; the next activation or generation bump starts tracking. - Deleting an entity does not reduce the count: processed events are kept only as one-way digests of their ledger identity, with no entity or event ID.
The console's automatic view shows the same counts. Each source on the overview has a summary row, and the source page lists every stage with the reasons records were quarantined or excluded. A count without a value says why instead of showing a number, and enrichment that is waiting for the daily budget is marked as resuming after the next UTC day.