Certificates data map¶
The certificates service stores calibration-certificate metadata and the corresponding PDF. It supports three use cases:
- a signed-in customer searches and downloads only its own certificates;
- an authorized application creates, replaces, lists, and downloads records;
- 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 |
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.