idempotency_key_id
Primary key
|
uuid
Required; default gen_random_uuid()
|
Stable identifier for the retry ledger row. Audit rows refer to this value rather than repeating replay internals.
Edge case: This is an internal row id; the externally supplied key is idempotency_key.
|
Must be a valid PostgreSQL UUID and unique as the primary key.
Invalid when: Not a UUID, reused across rows, or exposed as the customer Idempotency-Key header value.
|
Inherited platform table
PITD-007 requires a shared retry ledger. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Referenced by platform.audit_log.idempotency_key_id. One idempotency row can explain many audit rows for retries and final outcomes.
Example: b81db6f4-c7ea-4757-b5aa-87570f7ad119
|
tenant_id
Foreign key
|
uuid
Required; no default
|
Tenant that owns the retry scope and the mutation being protected.
Edge case: Platform-wide maintenance operations that do not have a tenant must not use this table unless the operation is deliberately scoped to a tenant row.
|
Must reference platform.tenant(tenant_id).
Invalid when: Null, not found in platform.tenant, or different from the tenant_id claim in the Bearer JWT for a tenant-scoped route.
|
Inherited platform table
PITD-007 defines tenant_id as part of idempotency scope. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Many idempotency rows belong to one platform.tenant.
Example: 0d4ce2f4-1c42-4f3c-9f0d-03fb7f5271d3
|
module
Scope key
|
text
Required; no default
|
Module namespace whose operation claimed the idempotency key.
Edge case: module=qti remains valid identity for QTI-owned retry scopes while qti/1edtech is under_reconciliation. Public registries must separately expose module_release_status and must not advertise QTI 1EdTech as approved until loop/qti/state.json says the surface has re-approved.
|
Must satisfy module_key_allowed: platform, qti, oneroster, caliper, case, nweamap, ed_fi, people_and_orgs, curriculum, content, events, results, analytics.
Invalid when: Null, outside module_key, used to store a route group instead of the module namespace, or used by /platform/modules as a release-status substitute.
Allowed values: Platform module_key
|
Inherited platform table
PITD-002 requires module schemas and shared platform operational tables. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Pairs with surface, route_template, and operation_id to define replay scope.
Example: qti
|
surface
Scope key
|
text
Required; no default
|
Surface whose API contract produced the retryable operation.
Edge case: The platform surface uses module=platform and surface=platform.
|
Must satisfy surface_code_allowed: platform, 1edtech, or alpha.
Invalid when: Null, outside surface_code, or used to hide whether an Alpha divergence changed operation behavior.
Allowed values: Platform surface_code
|
Inherited platform table
PITD-015 defines platform, 1EdTech, and Alpha surfaces. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Pairs with module so qti/1edtech and qti/alpha operations can have different customer contracts over shared persistence.
Example: 1edtech
|
method
Scope key
|
text
Required; no default
|
HTTP method for the mutation protected by the key.
Edge case: PUT/PATCH operations that can overwrite user work still need If-Match or an equivalent validator; idempotency does not replace optimistic concurrency.
|
Must satisfy mutation_http_method_allowed: POST, PUT, PATCH, or DELETE.
Invalid when: Null, GET, lowercase if the implementation normalizes to uppercase, or method does not match the documented endpoint.
Allowed values: Platform mutation_http_method
|
Inherited platform table
PITD-007 defines retryable create, upload, import, export-job, and command operations. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Part of the unique retry scope. Same route and key under different mutation methods are distinct scopes.
Example: POST
|
route_template
Scope key
|
text
Required; no default
|
Stable route pattern from the customer website or OpenAPI operation, with variable path segments expressed as braces.
Edge case: If the Alpha route names the tenant as workspaceId, the template still uses the public Alpha route name documented by that surface.
|
Must start with /. Must not contain a query string. Path variables use {name}. Must not include concrete tenant IDs, resource IDs, learner refs, or secrets.
Invalid when: Contains concrete UUIDs, query strings, raw learner refs, access tokens, or an undocumented internal route.
|
Inherited platform table
PITD-006 requires endpoint-local contracts and PITD-007 requires route template scope. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Part of the unique retry scope; links the row back to a documented endpoint.
Example: /tenants/{tenant_id}/qti/packages
|
operation_id
Scope key
|
text
Required; no default
|
Stable operation identifier used by docs, OpenAPI, logs, audit, and idempotency middleware.
Edge case: If two routes intentionally share replay behavior, they still need separate operation_id values unless the architecture explicitly declares them equivalent.
|
1 to 120 characters. Lowercase letters, digits, dots, underscores, and hyphens. Must be stable across wording-only documentation edits.
Invalid when: Blank, generated from a localized title, contains spaces, or changes without a customer-site and implementation update.
|
Inherited platform table
PITD-010 and PITD-009 require shared operation_id fields. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Part of the unique retry scope and repeated in audit rows.
Example: qti.packages.import
|
idempotency_key
Scope key
|
text
Required; no default
|
Opaque customer-supplied Idempotency-Key header value for one retryable operation.
Edge case: Same key with same request_hash replays; same key with different request_hash returns 409.
|
1 to 128 printable ASCII characters after trimming. Unique within the tenant/module/surface/method/route_template/operation_id scope. Must not be a token, password, email, phone number, student identifier, or raw request hash.
Invalid when: Blank, reused for different request_hash in the same scope, contains credentials or direct learner PII, or exceeds the documented length.
|
Inherited platform table
QTI package ingest used Idempotency-Key; PITD-007 promotes the rule. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Part of the unique retry scope. Not a foreign key.
Example: pkg-upload-2026-05-21-001
|
request_hash
|
text
Required; no default
|
Digest of the canonical replay identity for the first request. It lets middleware distinguish a safe retry from key reuse with different content.
Edge case: Large uploads hash normalized bytes or a precomputed payload hash, never raw bytes stored in this table.
|
Must be sha256:<64 lowercase hex characters>. Hash input is method, normalized route template, canonical JSON/body digest, tenant_id, module, surface, operation_id, and any idempotency-relevant headers documented by the endpoint.
Invalid when: Missing algorithm prefix, not sha256, uppercase/malformed hex, computed from non-canonical JSON, or includes raw secrets in a way that would be logged.
|
Inherited platform table
PITD-007 requires request hash conflict detection. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
No foreign key. Compared with later requests in the same unique scope.
Example: sha256:3d9f7a3a4e3c7ff4a2dcb2de9339f3c5a9b4f7d58f0a20d6f127f452e4f7e8b1
|
status
|
text
Required; default 'in_progress'
|
Replay lifecycle state of the idempotency row.
Edge case: failed_transient rows can be taken over after locked_until if no side effect committed; failed_permanent rows replay the safe Problem response.
|
Must satisfy idempotency_status_allowed.
Invalid when: Outside idempotency_status, null, or incompatible with response_status/locked_until, such as completed with no final status.
Allowed values: Platform idempotency_status
|
Inherited platform table
PITD-007 requires original outcome replay and conflict behavior. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
No foreign key. Determines whether middleware replays, rejects, waits, or allows takeover.
Example: completed
|
response_status
|
integer
Nullable; no default
|
HTTP status originally returned for a final replayable outcome.
Edge case: 204 outcomes store response_status=204 and response_body=null.
|
Nullable while in_progress or failed_transient. When present, must be an integer from 100 through 599 and match the stored response_body/resource outcome.
Invalid when: Present while status is in_progress, outside 100-599, or does not match the Problem/status or resource creation outcome.
|
Inherited platform table
PITD-006 defines HTTP status policy and PITD-007 defines replay. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
No foreign key. Used with response_body to replay the original result.
Example: 201
|
response_body
|
jsonb
Nullable; no default
|
Redacted JSON body safe to replay to the same tenant for completed or failed_permanent outcomes.
Edge case: For async operations, response_body can be a 202 job resource even though final module processing continues elsewhere.
|
Nullable. When present, must be a JSON object or array that matches the documented response schema. Must not include secrets, raw package bytes, headers, access tokens, IP addresses, user agents, direct learner PII, or unredacted processing traces.
Invalid when: Stores raw request body, file bytes, secrets, PII, non-JSON text, or a body that cannot be returned to the authenticated tenant.
|
Inherited platform table
PITD-006 requires redacted Problem/errors; PITD-007 requires replayable outcomes. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
May contain resource identifiers that point into module tables, but the module table remains source of record.
Example: {"packageId":"7f2e4dd2-c147-49ea-af77-41a6fdd70980","importStatus":"imported"}
|
resource_type
|
text
Nullable; no default
|
Optional type of resource created, imported, deleted, or accepted by the operation.
Edge case: For operations that create multiple resources, store the primary customer-visible resource and put redacted counts in response_body or audit metadata.
|
Nullable. When present, use a stable table or API object path such as qti.content_package, qti.delivery_session, platform.tenant, or alpha.activity.
Invalid when: Contains a display title, localized wording, raw URL, or path with tenant/resource IDs.
|
Inherited platform table
PITD-009 uses resource_type in audit and replay support. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Pairs with resource_id for audit and support lookup. Does not enforce a foreign key because target tables vary by module.
Example: qti.content_package
|
resource_id
|
text
Nullable; no default
|
Optional identifier of the primary resource associated with the replayable outcome.
Edge case: Async accepted work can use a job/run id here while the final content resource appears later in module tables.
|
Nullable. When present, must be the canonical resource identifier for resource_type and tenant_id.
Invalid when: Contains a student name, email, phone number, raw package path, or ID outside the tenant.
|
Inherited platform table
PITD-009 requires resource identifiers for operational traceability. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Pairs with resource_type; target cardinality is many idempotency rows may point at one resource only when retries or aliases are documented.
Example: 7f2e4dd2-c147-49ea-af77-41a6fdd70980
|
first_request_id
|
text
Required; no default
|
Request identifier of the first request that claimed this key.
Edge case: Retry requests have their own request_id in access logs, but this field stays fixed to the first attempt.
|
Required non-empty text, 1 to 120 characters. Must be safe for logs and customer support. Must not contain tokens, IP addresses, user agents, or PII.
Invalid when: Null, blank, regenerated on replay, or includes sensitive request details.
|
Inherited platform table
PITD-010 requires request_id in structured operational evidence. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Should match platform.audit_log.request_id for the original attempt when that attempt is audited.
Example: req_20260521_0001
|
locked_until
|
timestamptz
Nullable; no default
|
Temporary lock deadline used while an in-progress operation is executing.
Edge case: Long-running async jobs should complete the idempotency row with a 202 response rather than hold this lock for the whole job.
|
Nullable. When status is in_progress, should be a future timestamp. Must be null for completed and failed_permanent rows.
Invalid when: Expired while status remains in_progress without takeover logic, set on completed rows, or earlier than created_at.
|
Inherited platform table
PITD-007 centralizes replay and conflict behavior. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
No foreign key. Used by middleware to decide whether another worker may take over a stale in-progress row.
Example: 2026-05-21T12:01:30Z
|
expires_at
|
timestamptz
Required; no default
|
Timestamp after which the key is no longer promised to replay the original response.
Edge case: Expired rows may remain queryable for audit but must not silently replay after the documented window.
|
Required timestamp with time zone. Must be later than created_at. Default policy is at least 24 hours after first claim; modules may document longer windows for async jobs or exports.
Invalid when: Null, before created_at, shorter than the customer website promises, or extended without retention/audit reason.
|
Inherited platform table
PITD-007 requires a shared idempotency policy rather than per-module drift. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
Indexed for cleanup and expiry marking.
Example: 2026-05-22T12:01:00Z
|
created_at
|
timestamptz
Required; default now()
|
Timestamp when the first request claimed the idempotency key.
Edge case: Use created_at plus expires_at for replay-window reporting.
|
Required timestamp with time zone.
Invalid when: Null, changed after insert, or compared without timezone normalization.
|
Inherited platform table
PITD-017 requires lifecycle timestamps. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
None.
Example: 2026-05-21T12:01:00Z
|
updated_at
|
timestamptz
Required; default now()
|
Timestamp when the row last changed status, replay body, lock, or expiry.
Edge case: Retries that merely read/replay do not need to update updated_at unless the implementation records replay counts elsewhere.
|
Required timestamp with time zone. Must be greater than or equal to created_at.
Invalid when: Null, earlier than created_at, or left unchanged after finalizing the row.
|
Inherited platform table
PITD-017 requires DDL discipline and auditability. Inherited into QTI by ITD-025; do not redefine this field in qti.*.
Platform shared, ITD-025 Platform Substrate Inheritance
|
None.
Example: 2026-05-21T12:01:08Z
|