Skip to content

meta.json — Identity Layer

meta.json is the identity and provenance record of an SDF document. It answers the questions: what is this document, who issued it, and when was it created?

meta.json is intentionally separate from data.json. SDF metadata evolves independently of business document schemas. A change to the SDF specification (e.g., adding a new meta field) does not require changes to invoice schemas, nomination schemas, or any other domain-specific schema.


Fields

FieldTypeRequiredDescription
sdf_versionstringMUSTSDF spec version, e.g. "0.1"
document_idstring (UUID v4)MUSTGlobally unique document identifier
document_typestringMUSTDocument type, e.g. "invoice", "nomination", "purchase_order"
issuerstringMUSTHuman-readable name of the issuing party
created_atstring (ISO 8601)MUSTCreation timestamp with timezone offset
issuer_idstringSHOULDMachine-readable issuer identifier (tax ID, VAT number, DUNS, etc.)
recipientstringSHOULDHuman-readable name of the receiving party
schema_idstring (URI)SHOULDURI identifying the schema used in schema.json
localestring (BCP 47)MAYDocument locale, e.g. "en-US", "de-DE", "tr-TR"
nomination_refstringMAYReference key for nomination-to-invoice matching workflows

The document_id Rule

document_id MUST be a UUID v4 generated at produce time. It MUST NOT be derived from any business identifier.

Invoice numbers, PO numbers, nomination reference codes — these are business identifiers. They belong in data.json, not in document_id. Business identifiers are indexed separately in the SDF server layer for lookup and matching purposes.

The rationale: document_id is a globally unique, collision-resistant identifier for the SDF file itself. Two separately produced SDF files representing the same invoice are different SDF documents and MUST have different document_id values.

Correct document_id generation
// Correct — crypto.randomUUID() produces UUID v4
const documentId = crypto.randomUUID();
// Wrong — derived from business data
const documentId = `inv-${invoiceNumber}`; // MUST NOT do this

Complete Example

meta.json
{
"sdf_version": "0.1",
"document_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"document_type": "invoice",
"schema_id": "https://etapsky.github.io/sdf/schemas/invoice/v0.1.json",
"issuer": "Acme Supplies GmbH",
"issuer_id": "DE123456789",
"created_at": "2026-03-15T14:30:00+01:00",
"recipient": "Global Logistics AG",
"locale": "de-DE"
}

Minimal Valid Example

For contexts where only required fields are known at produce time:

meta.json (minimal)
{
"sdf_version": "0.1",
"document_id": "a1b2c3d4-e5f6-4789-abcd-ef0123456789",
"document_type": "purchase_order",
"issuer": "Procurement Department",
"created_at": "2026-03-15T09:00:00Z"
}

Validation

meta.json MUST validate against the SDF meta schema. Consumers MUST reject any archive where meta.json fails validation with SDF_ERROR_INVALID_META.

Validation rules enforced by the meta schema:

  • sdf_version MUST be a string matching the pattern ^\d+\.\d+$.
  • document_id MUST be a string matching the UUID v4 pattern.
  • created_at MUST be a string matching ISO 8601 with timezone (date-time format).
  • document_type MUST be a non-empty string.
  • issuer MUST be a non-empty string.

Separation of Concerns

meta.json carries SDF-level metadata. It MUST NOT carry business-domain data. The following examples show correct vs incorrect placement:

DataCorrect locationWrong location
SDF spec versionmeta.jsonsdf_versiondata.json
Globally unique document IDmeta.jsondocument_iddata.json
Invoice numberdata.jsoninvoice_numbermeta.json
Line itemsdata.jsonline_itemsmeta.json
Total amountdata.jsontotalmeta.json
Digital signaturesignature.sigmeta.json value

Extending meta.json

meta.json MUST NOT contain fields not defined in this specification at the root level. Producers that need to attach proprietary metadata SHOULD use vendor/ prefixed fields or place the data in a separate vendor/<name>/meta.json entry.

The SDF specification committee may add new optional fields in minor versions. These additions are backward-compatible: consumers on older minor versions SHOULD ignore unknown fields.