DocsDocuments & compliance
Documents and verification
Uploading, the verification queue, rejection reasons, expiry handling, file storage and access control, and the limits the API enforces on every upload.
Document control is where a recruitment CRM either helps a DMW inspection or hinders it. HRTEQ treats a document as evidence with a state: it is uploaded by one person, verified by another, and stops counting the moment it expires.
The document lifecycle#
| Status | Set by | Meaning |
|---|---|---|
PENDING | — | Expected but not received. Used where a document is known to be required and nothing has arrived. |
SUBMITTED | Upload | A file is on record. This is the status every upload starts at — it does not satisfy a requirement. |
VERIFIED | Document Officer | Checked against the original. Stamps verifiedAt and verifiedByUserId. Only now does it satisfy a requirement. |
REJECTED | Document Officer | Not acceptable. A rejectionReason is mandatory and is shown to the officer chasing the gate. |
EXPIRED | Nightly sweep | Was verified, but its expiry date has passed. Set automatically at 01:00 Asia/Manila. |
SUBMITTED, and the gate reports it as “Uploaded but not yet verified by the Document Officer.” This surprises new agencies constantly. Verification is a separate act by a separate role, and that separation is the point.Uploading#
Uploads are multipart, one file per request, and the metadata travels in the query string rather than the body — a consequence of streaming the file rather than buffering a JSON envelope around it.
| Parameter | Notes |
|---|---|
documentTypeId | Required. Must be an active document type in your organization. |
applicantId or applicationId | Exactly one. Which one is correct is decided by the type’s appliesTo — sending the wrong one returns 400 saying which it applies to. |
expiresAt | A date. Required in practice for any type with requiresExpiry. |
documentNo | The document’s own reference number, up to 100 characters. |
# Applicant-level document: passport, with its expiry.
curl -X POST "$API_URL/v1/documents?documentTypeId=$TYPE&applicantId=$APPLICANT&expiresAt=2031-06-30&documentNo=P1234567A" \
-H "X-CSRF-Token: $CSRF" \
--cookie "hrteq_session=$SESSION" \
-F "file=@passport.pdf"
# 201 { "ok": true, "data": { "id": "..." } }Upload limits#
- One file per request. Up to 20 form fields.
- 10 MB maximum per file.
- Allowed types: PDF, DOC, DOCX, JPEG, PNG, WEBP and HEIC. HEIC is on the list because that is what an iPhone produces at the front desk.
- JSON bodies elsewhere in the API are capped at 1 MiB, and every endpoint is rate limited to 300 requests per minute per IP.
What is stored#
Each upload creates a FileObject alongside the Document. The file record holds the storage key, original filename, MIME type, size, uploader, and a SHA-256 checksum of the bytes. The checksum is what lets you prove the file on record is the file that was uploaded.
A ScanStatus field — PENDING, CLEAN, INFECTED, SKIPPED — is reserved for malware scanning. Uploading a résumé also sets resumeFileId on the applicant, which is what may queue a parse job.
The verification queue#
The Documents screen is a queue: the 300 most recently updated documents, filterable by status, each showing its type, the applicant it belongs to, and a link to the file. From there a Document Officer verifies or rejects.
Open the file
File links resolve through the API rather than pointing at storage directly, so every access is authorised.
Verify, or reject with a reason
Verifying stamps who verified it and when. Rejecting requires a reason of up to 1000 characters — a rejection without one returns
400.The gate re-evaluates itself
If the document belongs to an application and was just verified, the server checks whether the immediate next stage is now fully satisfied and advances it if so. Verifying the last outstanding document moves the application without anyone touching the board.
Verification writes document.verified; rejection writes document.rejected with the reason attached. Both are permanent audit rows.
Expiry#
A document type either tracks expiry or it does not. Where it does — passport, NBI, police clearance, TESDA NC, medical result, visa, OEC, OWWA, insurance — two things follow.
- The gate refuses to count a verified document whose expiry date is missing, and refuses one whose date has passed. It reports the exact date in the refusal.
- A nightly sweep at 01:00 Asia/Manila flips lapsed documents from
VERIFIEDtoEXPIRED, so the status you see matches reality even if nobody looked.
Ahead of that, the DOCUMENT_EXPIRING automation trigger warns you: a general rule 30 days out that tasks the Document Officer and emails the applicant, and a passport-specific rule 180 days out, because most destinations require at least six months of passport validity at deployment. See Automation rules.
File access control#
Every file request is authenticated and scoped to the organization. Beyond that, each surface gets its own check.
| Surface | May open a file when |
|---|---|
| Staff | It belongs to their organization. |
| Applicant | The document is their own — theirs directly or on one of their applications — or the file was attached to a conversation they are in. |
| Employer | The file was attached to a conversation they are in. Nothing else. |
| Platform | Cross-organization, for operator support. |
On object storage the API redirects to a signed URL valid for 300 seconds; on local storage it streams the bytes with a private, one-minute cache header. Either way the storage key is never a public URL.
The type catalog#
Twenty-seven document types are seeded, split between applicant-level and application-level, each with its category, expiry behaviour and portal-upload flag. The full table is in Document types.
