Skip to content

Certificates data map

Domain: certificatesStatus: implementedReviewed: 2026-09-04

The certificates service stores calibration-certificate metadata and the corresponding PDF. It supports three use cases:

  1. a signed-in customer searches and downloads only its own certificates;
  2. an authorized application creates, replaces, lists, and downloads records;
  3. a recipient without an account retrieves one PDF by proving that it knows the certificate number and a second matching business number.
flowchart TB
  subgraph portal["Customer portal (session)"]
    account["CustomerAccount"]:::corebox
  end
  subgraph machine["Machine API (bearer scopes)"]
    client["Authorized application"]:::ext
  end
  subgraph public["Public lookup (no credentials)"]
    recipient["Certificate holder"]:::ext
  end

  account -->|"own customer number only"| record["Certificate metadata"]:::corebox
  client -->|"read and write scopes"| record
  recipient -->|"certificate number plus one matching number, throttled"| record
  record -->|"PDF at certificates/<id>.pdf"| pdf["Object storage"]:::store

  classDef ext fill:#f9d6d6,stroke:#b04,color:#000
  classDef corebox fill:#cfeec9,stroke:#2a6,color:#000
  classDef store fill:#dbe4ff,stroke:#3949ab,color:#000

Certificate record

Certificate is the metadata index for one immutable public certificate identity. Replacing a certificate keeps its UUID, certificate number, and object-storage key while writing the replacement PDF and metadata.

Field Type Purpose
id UUID Internal stable identity and basis of the object-storage key.
file PDF Document stored at certificates/{id}.pdf; user input never becomes the storage path.
certificate_number string(255), unique Public calibration-certificate identifier. Limited to URL-safe letters, numbers, dots, underscores, tildes, and hyphens.
serial_number string(255) Device serial number; searchable.
test_equipment_number string(255) Customer test-equipment number; searchable.
customer_number string(50) Customer ownership and API filter key.
order_number string(255) Related order number; searchable.
certificate_date date Calendar date of the calibration certificate.
uploaded_at, updated_at timestamps Record lifecycle.

Uploads must carry a PDF signature and are limited by CERTIFICATES_MAX_UPLOAD_BYTES (25 MB by default). A create with an existing certificate number returns a conflict rather than overwriting it. An explicit replace operation is required to update an existing certificate.

Customer account and portal

CustomerAccount links one signed-in user to one unique customer number. Every portal list, search, single download, and bulk download starts with this number, so a URL or submitted UUID cannot cross the customer boundary.

Field Type Purpose
user one-to-one user relation Portal identity.
customer_number string(50), unique Restricts all portal records to this customer.
company_name string(255) Customer-facing account label.
filename_pattern string(255) Customer-selected download filename template.

The portal supports free-text search across certificate, serial, test-equipment, and order numbers; individual filters; date ranges; sorting; and 25-row pages. Selected results can be downloaded together as kalibrierzertifikate.zip. CERTIFICATES_MAX_ARCHIVE_ENTRIES (100 by default) refuses an oversized selection before any work starts, and CERTIFICATES_MAX_ARCHIVE_BYTES (200 MB by default) stops an archive that grows past the budget while it is written, so one selection cannot occupy a worker for minutes.

Download names can use the German tokens {zertifikatsnummer}, {seriennummer}, {pruefmittelnummer}, {kundennummer}, {auftragsnummer}, {datum}, and {firmenname}. Rendered names are sanitized and duplicate names inside a ZIP receive a numeric suffix. The visible filename never changes the immutable object-storage key.

Machine API

Read operations require certificates:records:read; create and replace require certificates:records:write. Clients that both read and write request both scopes.

Endpoint Purpose
GET /v1/certificates Cursor-paginated metadata list. Filters: serial_number, test_equipment_number, order_number, and exact customer_number.
POST /v1/certificates Create metadata and a PDF as multipart/form-data. Returns 409 Conflict when the certificate number already exists.
GET /v1/certificates/{certificate_number} Retrieve one metadata record.
PUT /v1/certificates/{certificate_number} Replace the PDF and all writable metadata as multipart/form-data while preserving the certificate number.
GET /v1/certificates/{certificate_number}/download Stream the PDF as application/pdf.

Metadata responses contain every certificate field above except file; the PDF is available only from the download route. List results are ordered newest first (uploaded_at, then id) with a stable cursor. Paging follows the platform convention described in Paging: follow the returned next URL, and set page_size between 1 and 200 if the default of 50 does not suit.

The machine API has no delete route. Removing a certificate is a deliberate admin action, so an integration cannot destroy a delivered document.

Public lookup

GET /v1/certificates/lookup downloads one PDF without authentication. It requires certificate_number plus at least one of serial_number, test_equipment_number, order_number, or customer_number. Every supplied number must match exactly and case-insensitively.

The HTML equivalent is /zertifikat/. Both interfaces use the same lookup rule and the same per-client throttle, CERTIFICATES_LOOKUP_RATE (20 requests per hour by default); an exhausted client is answered with 429. A wrong number pairing is answered like an unknown certificate, so the response does not reveal which part was valid.

The route is unauthenticated by design, because a certificate is handed on to whoever received the calibrated device. The pairing rule is what keeps it from being an open index: a certificate number alone is not enough, and the numbers that complete it are known only to parties that already hold the paperwork. Certificate records carry customer identifiers, so the handling rules in Data protection apply to them as to any other customer data.

Deletion and storage safety

The PDF is stored outside Postgres in the configured S3-compatible object store. Replacement writes the new object without deleting the old one first, so a failed upload does not intentionally remove the readable certificate. Deletion removes the object before deleting its metadata; if storage deletion fails, the database row remains available for an operator to retry.

Deletion happens in Django admin only, and each deletion is logged with the certificate number, the customer number, and the acting admin user. There is no automatic retention rule: a certificate stays until someone removes it, so a customer erasure request is an operator task rather than a scheduled job.