Skip to main content

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​

OutcomeMeaningNext step
QueuedThe raw batch is retained; row evaluation has not finished.Wait or refresh the receipt.
Waiting for source evidenceThe 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 evaluatedNo 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 queuedA 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.
QuarantinedThe 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 completedAll submitted rows have canonical delivery-queue entries.Verify downstream arrival separately.
Raw records expiredRetained 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-Key within 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 with failed_precondition and reason review_records_expired as 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 new Idempotency-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; 0 expires 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 distinct count 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 generation when the model it served was built for this source (every automatic model the source used, and an explicitly activated project model); generation_bundle_id names the model counted. A generation that served another source's model, a v1 model or a manual takeover is null with generation_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_slot or no_active_bundle). If the generation has publications recorded before these facts existed it is null with not_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.