Signing Platform Core API
On this page
- At a glance
- What it does
- A. Runs the signing of documents
- B. Manages people and their identities
- C. Issues and watches certificates
- D. Organises who may do what
- E. Lets other systems sign through it
- F. Shows the platform to the public
- Interface
- Standards
- Inside
- Design decisions
- Specifications
- Related
- Terms
Signing Platform Core API is the heart of a document-signing platform: the Laravel application that owns the people, the organisations, the client systems, and the records, and that orchestrates every specialised service around it. It does far more than sign. It manages user accounts and their identities, organisations and their units, the external systems that sign through the platform, documents with their signers and their history, signature stamps, and the certificates people sign with — and it hands the heavy work of signing, rendering, timestamping, and storing to services built for it.
It is the one module in the platform that belongs to a single project: it holds that project's rules and data, while the services it calls are reusable anywhere. I still build and describe it as a module, so the platform stays one consistent set of parts with one core at the centre.
At a glance
| Language & framework | PHP, with Laravel, on PostgreSQL and Redis |
|---|---|
| Architecture | Stateless Laravel Core |
| Version | 2.15.1 |
| Status | Stable, in production |
| Form | An HTTP API with background workers, plus a server-rendered public home page; the main user interface is a separate single-page application |
| Source code | Private |
What it does
A. Runs the signing of documents
A document is uploaded, given one or more signers in ordered stages, and signed by each in turn. Every signature is a background job: the request is accepted at once, the document is marked as being signed, and the signer service does the cryptographic work with the signer's own certificate. The signed file's hash is recorded, so a document can be found again by its content, and each new signed version is chained to the one before it.
B. Manages people and their identities
Accounts can be created directly or through external identity providers, protected by two-factor authentication, and managed across devices. Before someone can hold a signing certificate, their identity is verified: personal data, an identity document, and a live face challenge, checked by a human reviewer.
C. Issues and watches certificates
The core requests certificates from the signing certificate authority on a person's behalf, keeps at most one active certificate per person in each organisation, and checks revocation live against the authority, updating its own records when the authority's answer differs.
D. Organises who may do what
Organisations contain units, units contain members, and roles are ranked so nobody can act on a peer or on someone above them. Organisations can admit members automatically from an allowlist of e-mail addresses, and administrators can suspend accounts without deleting them.
E. Lets other systems sign through it
Client systems — the platform's software-as-a-service customers — call a separate client API with their own secrets, allowed origins, and permissions granted per operation. Trusted internal clients can bind users directly; external clients need the user's consent. Results are delivered back to each client through webhooks, sent in the background and recorded until they succeed.
F. Shows the platform to the public
The public home page is rendered by the core itself, with content that administrators can update, so search engines can index it; the application people work in lives in its own repository.
Interface
| Area | What it exposes |
|---|---|
| Authentication | login, rotating refresh tokens, two-factor, external identity providers, the public signing keys of its tokens |
| Users | accounts, devices, roles, stamp templates |
| Organisations | organisations, units, members, e-mail allowlists |
| Identity | identity verification, face challenges, reviewer queues |
| Certificates | requests, approvals, revocations, live status |
| Documents and files | uploads through short-lived links, signers, signing, signed versions |
| Client systems | client management, and a separate client API with its own authentication |
| Administration | dashboard, failed jobs, public page content |
| Readiness | one endpoint that checks the database, the cache, storage, and every service the core depends on |
Standards
| Standard | What it guarantees here |
|---|---|
| JSON Web Tokens with RS256, and a published JWKS | one token format for every login, verifiable by any service without calling the core |
| OAuth 2.0 and OpenID Connect, with PKCE | sign-in through external providers, exchanged for the core's own token |
| TOTP (RFC 6238) | two-factor authentication with any authenticator app |
| OCSP (RFC 6960) | live revocation status for every certificate the platform uses |
| Presigned URLs, AWS Signature Version 4 | files moved between browsers and storage without passing through the core |
| W3C Trace Context and OpenTelemetry | one trace from the user's request through every service and background job |
| UUIDv7 | public identifiers and request ids that sort by time and reveal no internal sequence |
Inside
The core follows Stateless Laravel Core: nodes hold nothing, slow work runs as queued jobs, files never touch the server, every login ends in the core's own token, and every request, job, and outgoing call is traced. Each specialised service is reached through one class of its own, with its own configuration, timeout, and family of errors.
Design decisions
The core decides; specialised services do the work. Signing, timestamping, issuing certificates, rendering thumbnails, challenging faces, storing files, and looking up regions each belong to a service built for that job, and the core orchestrates them. When a service lacks something the core needs, I ask for it in that service rather than work around it here — the batch endpoint the storage service now offers exists because the core needed it. Regional data used to live in the core's own tables; it moved to the address service, and organisations now keep a snapshot of their region instead of a foreign key, so a change in official region codes can never silently break an existing record.
Signing never makes anyone wait. A signing request is checked before it is queued — the signer's passphrase is verified, so a job never starts from a wrong one — then accepted at once, with the document marked as being signed rather than signed. The passphrase travels encrypted through the queue. A failed webhook to a client never undoes a signature; the delivery is retried and recorded separately.
Personal data is protected as data, not only as traffic. Phone numbers, birth details, identity numbers, and two-factor secrets are encrypted in the database, and identity numbers can be looked up without decrypting anything. The plain identity number never appears in a login response, where it would end up in browser storage and logs. One registry is the only place that decides whether an identity number is already taken, so no two code paths can disagree. Identity photos shown to reviewers carry a watermark identifying the viewer, so a leaked image can be traced, and face images are deleted from storage once a verification is approved.
Nothing reveals what exists. Public identifiers are random UUIDs, never the internal sequence, so records cannot be enumerated by counting. Endpoints that could reveal whether an account exists — password resets, verification e-mails — answer the same way either way. Actions with lasting impact require the password again, and administrators cannot act on peers or on roles above their own.
Every signed version stays connected. The hash of each signed file is stored, so a document can be identified from the file alone. When a signed document is uploaded again for further signatures, its new version is chained to the previous hash, and because file bytes never pass through the core, that hash is read back from storage, not computed from an upload the core never saw.
Two kinds of client, two levels of trust. Internal clients — systems run by the same organisation — are trusted to bind their users directly and receive default permissions. External clients must obtain each user's consent through a one-time token and are limited to the operations they were granted, checked on every call. Client secrets are stored hashed, and the user a client refers to is identified by a reference the platform derives itself rather than an identifier the client invented.
Certificates are checked where they live. Revocation is asked of the certificate authority live, and when its answer differs from the core's records, the records follow the authority, with the real revocation reason kept. The authority's own certificates are fetched once and cached rather than stored as files on the server.
Storage is accounted for. Every object key the database refers to is known to one index, and nothing is deleted from storage without asking it first, and a read-only audit compares storage with the database before anything is swept.
Specifications
| Inputs | Requests from the web application, from client systems, and from administrators |
|---|---|
| Outputs | JSON responses, signed documents in storage, certificates on behalf of users, webhooks to client systems, e-mail |
| Depends on | PostgreSQL, Redis, and the platform's services for storage, addresses, certificates, timestamps, signing, thumbnails, and face challenges |
| Background work | Queued jobs for signing, thumbnails, webhooks, e-mail, and allowlist processing |
| Security | RS256 tokens with rotating refresh tokens, two-factor authentication, rate limits in Redis, encrypted personal data, ranked roles |
| Observability | OpenTelemetry traces, logs, metrics, and audit events, continued across every service and job |
| Runs as | A container with the web server and queue workers under one supervisor, scaled by adding copies |
Related
The storage service through which every file, stamp, and signed document travels.
The address service that supplies regions for organisations.
The certificate authority that issues each signer's certificate and answers revocation checks.
The timestamp authority behind every signature's date.
The root publisher at the top of the platform's chain of trust.
The signer that turns each queued request into a long-term PDF signature.
The renderer that gives every document its first-page thumbnail.
The face challenge that proves a person was present during identity verification.
Where the trace of a request through all these services ends up.
Terms
- Orchestration
- Coordinating several services so that together they complete one piece of work, without doing that work itself.
- Enumeration
- Discovering which records or accounts exist by trying identifiers one after another and comparing the answers.
- Webhook
- A request a system sends to another system's address to tell it that something has happened.
- Software as a service
- Software that customers use through the provider's servers, here by connecting their own systems to the platform.
Technologies covered
- PHP
- Laravel
- PostgreSQL
- Redis
- OpenTelemetry
- OAuth 2.0