Define a label
Time to declare what "success" means. The sample dataset ships
labels_repeat_purchase.csv — one row per user who placed an order:
entity_id,value,observed_at
u_00001,false,2025-05-05T09:53:00Z
u_00003,true,2025-03-18T14:02:00Z
value is true iff the user placed a second order within 30 days of
their first; observed_at is when that outcome became knowable
(first_order + 30 days). You define repeat_purchase_30d as a binary
label reading that table, then upload
the file to its own source:
- curl
- Python SDK (planned)
#!/usr/bin/env bash
# Quickstart step 4 — define the binary label and upload its observations.
set -euo pipefail
: "${TESSERA_API_BASE:?e.g. https://api.staging.tessera.zone/v1}"
: "${TESSERA_API_KEY:?your tsk_test_… key}"
: "${TESSERA_STATE_DIR:=.}"
AUTH=(-H "Authorization: Bearer $TESSERA_API_KEY")
SAMPLE_DIR="$TESSERA_STATE_DIR/sample"
# --- 1. a source for the label table -----------------------------------------
LABEL_SOURCE="$(curl -sS -X POST "$TESSERA_API_BASE/sources" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-label-source" \
-d '{"name": "quickstart-labels", "kind": "file_upload", "config": {}}')"
LABEL_SOURCE_ID="$(printf '%s' "$LABEL_SOURCE" | python3 -c 'import json,sys; print(json.load(sys.stdin)["source_id"])')"
# --- 2. the label definition --------------------------------------------------
# `observed_at` is when each outcome became knowable; `maturation_days: 7`
# masks any supervision where the entity was active within 7 days of the
# observation — the model must genuinely predict, never read back.
LABEL="$(curl -sS -X POST "$TESSERA_API_BASE/labels" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-label" \
-d '{
"name": "repeat_purchase_30d",
"entity_type": "user",
"task": "binary",
"source": {
"kind": "label_table",
"source_id": "'"$LABEL_SOURCE_ID"'",
"entity_id_column": "entity_id",
"value_column": "value",
"observed_at_column": "observed_at"
},
"maturation_days": 7
}')"
LABEL_ID="$(printf '%s' "$LABEL" | python3 -c 'import json,sys; print(json.load(sys.stdin)["label_id"])')"
printf '%s' "$LABEL" | python3 -c '
import json, sys
label = json.load(sys.stdin)
print(json.dumps({k: label[k] for k in ("label_id", "name", "entity_type", "task", "maturation_days")}, indent=2))
'
# --- 3. upload the observations to that source --------------------------------
SESSION="$(curl -sS -X POST "$TESSERA_API_BASE/uploads" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-upload-labels" \
-d '{"filename": "labels_repeat_purchase.csv", "format": "csv", "source_id": "'"$LABEL_SOURCE_ID"'"}')"
UPLOAD_ID="$(printf '%s' "$SESSION" | python3 -c 'import json,sys; print(json.load(sys.stdin)["upload_id"])')"
UPLOAD_URL="$(printf '%s' "$SESSION" | python3 -c 'import json,sys; print(json.load(sys.stdin)["upload_url"])')"
curl -sS -X PUT "$UPLOAD_URL" --data-binary "@$SAMPLE_DIR/labels_repeat_purchase.csv" > /dev/null
curl -sS -X POST "$TESSERA_API_BASE/uploads/$UPLOAD_ID:complete" "${AUTH[@]}" > /dev/null
LABEL_SYNC_ID="$(curl -sS "$TESSERA_API_BASE/uploads/$UPLOAD_ID" "${AUTH[@]}" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["sync_id"])')"
for _ in $(seq 1 300); do
STATUS="$(curl -sS "$TESSERA_API_BASE/syncs/$LABEL_SYNC_ID" "${AUTH[@]}" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["status"])')"
if [ "$STATUS" = "succeeded" ] || [ "$STATUS" = "failed" ]; then break; fi
sleep 1
done
echo "label sync: $STATUS"
# --- 4. sanity-check the ingested observations ---------------------------------
curl -sS "$TESSERA_API_BASE/labels/$LABEL_ID/stats" "${AUTH[@]}" | python3 -m json.tool
printf '%s' "$LABEL_ID" > "$TESSERA_STATE_DIR/label_id"
Download quickstart/04-define-label.sh — this exact file runs in CI on every change (hosts shown are for this environment).
"""Quickstart step 4 (planned Python SDK) — define the label, upload observations."""
import os
import time
from pathlib import Path
import httpx
from tessera import Tessera
from tessera._generated import models
client = Tessera(base_url=os.environ["TESSERA_API_BASE"])
state_dir = Path(os.environ.get("TESSERA_STATE_DIR", "."))
sample_dir = state_dir / "sample"
label_source = client.sources.create(
models.SourceCreateRequest(name="quickstart-labels", kind="file_upload", config={})
)
label = client.api.create_label(
models.LabelCreateRequest(
name="repeat_purchase_30d",
entity_type="user",
task="binary",
source=models.LabelSourceInput(
root=models.LabelTableSource(
kind="label_table",
source_id=label_source.source_id,
entity_id_column="entity_id",
value_column="value",
observed_at_column="observed_at",
)
),
maturation_days=7,
)
)
print(f"label {label.name}: task={label.task.value} maturation_days={label.maturation_days}")
session = client.api.create_upload(
models.UploadCreateRequest(
filename="labels_repeat_purchase.csv",
format="csv",
source_id=label_source.source_id,
)
)
httpx.put(
str(session.upload_url),
content=(sample_dir / "labels_repeat_purchase.csv").read_bytes(),
).raise_for_status()
client.api.complete_upload(session.upload_id)
upload = client.api.get_upload(session.upload_id)
for _ in range(300):
sync = client.syncs.get(str(upload.sync_id))
if sync.status.value in ("succeeded", "failed"):
break
time.sleep(1)
print(f"label sync: {sync.status.value}")
stats = client.api.get_label_stats(label.label_id)
print(f"observations: {stats.observations}")
print(f"matured: {stats.maturation_coverage.matured}")
(state_dir / "label_id").write_text(label.label_id)
client.close()
Download quickstart-sdk/04-define-label.py — this exact file runs in CI on every change (hosts shown are for this environment).
Three deliberate choices in that definition:
kind: label_table— rows of the referenced source become label observations and are never normalized into events. Labels are targets, never inputs, structurally.maturation_days: 7— supervision only attaches where the user had been quiet for a week before the outcome was recorded; a model state from inside the outcome window would be grading itself on a half-known answer (masked, reasonimmature).- Unobserved stays unobserved — users without a row are masked, not assumed negative.
The label needs a registered entity type — user was auto-registered in
step 1 when its first event streamed in, and the batch
sync in step 2 independently ensures every mapped entity type is registered
before its events commit.
GET /labels/{id}/stats confirms what the builder will see: 1,700
observations, all matured, positive rate ≈ 0.40.
Next: build the dataset.