think
16px
820px

Offer letters

Valid sends the offer in three versions. Which one a candidate gets is chosen
per candidate, on the offer card, and recorded on the offer.

Letter When Staging template id
Indonesian The default, and the legally safest 01a02f58-f962-774e-a1cc-935ae9332b35
English Candidates who do not read Indonesian 01a02d60-0e52-7bd0-ae37-ec5f85e867e2
Bilingual Indonesian above English, Indonesian prevails 01a02d8d-4fc9-7e7f-9b49-434f73d9610a

All three are .dotx files in Obscura, in the category Offer Letter, and
all three declare the same 15 fields — so switching a draft between them never
invalidates values already typed.

Why Indonesian is the default

  • UU 24/2009 Pasal 31 makes the Indonesian text the operative one for an
    agreement with an Indonesian party. The English is a courtesy translation
    either way.
  • UU 13/2003 Pasal 57: a PKWT not made in Indonesian is deemed PKWTT. An
    English-only contract hire is accidentally permanent.
  • Two texts is two texts to maintain. A careful translation of the English
    block still produced five substantive divergences, one of them a blocker
    (gaji pokok bersihgaji pokok is statutorily the gross basic wage).
    Three reviewers caught it. Nobody runs three reviewers on the next edit.

None of this is a lawyer's opinion. The PKWT/PKWTT point in particular is worth
confirming with one.

How the portal finds them

OBSCURA_OFFER_TEMPLATE_CATEGORY (default Offer Letter). Obscura already
groups templates this way — Kop Surat for letterheads, Quotation for quotes
— and the category is editable from its admin, so a fourth offer letter becomes
available by being uploaded and categorised. No deploy.

The category is a gate, not a label: every template in Obscura shares one id
space, and a letterhead declares no fields, so filing an offer on one would
consume a permanent number for a document that is entirely {{TOKEN}}s.

Setting Default What it does
OBSCURA_OFFER_TEMPLATE_CATEGORY Offer Letter Which templates the picker offers
OBSCURA_OFFER_TEMPLATE_ID first in category The one a new draft starts on
OBSCURA_OFFER_FOLDER_ID root Where the document sits before publishing
OBSCURA_OFFER_DOC_CLASSIFICATION confidential The document registry's code
OBSCURA_OFFER_CLASSIFICATION rahasia The letter sifat — a different vocabulary
OBSCURA_OFFER_READ_GROUPS Groups that may open a filed offer

The last one is not optional in practice. A confidential letter's derived ACL is
its creator plus correspondence admins, and the creator is the portal's API key
— a bot. Without the grants, the people who made the offer cannot read it.

The form is not in this repository

GET /document-templates/{id}/fields is read at runtime, every time. Adding a
clause to the letter is a re-upload in Obscura; the field that fills it appears
in the console without a deploy. The completeness check is re-run against the
template's current declaration immediately before filing, so a .dotx
re-uploaded mid-draft cannot leave a blank in a numbered document.

The typographic punctuation trap

Obscura's docx→PDF step has no glyph for , or and emits a literal
? for each.

It is not a warning, and it is invisible in the .docx — the XML holds the
right character the whole way through, so every check that reads the document's
text passes. Only the rendered PDF, which is what the candidate opens, is wrong.

VDI/OFR/260007 was filed that way, before this was found:

… in accordance with the Company ? s standard payroll schedule …   (×3)
… (Karyawan tetap / Permanent, Hybrid ? Jakarta) …                 (×2)
… Efran Nathanael  Direktur ? Val.id …

That letter is numbered and cannot be corrected. All three templates have been
re-uploaded with ASCII punctuation and re-verified: 0 ? in the rendered
PDF, no unfilled tokens, every value present.

The characters came in with Valid's own .dotx — Word inserts them by
autocorrect — so this is a trap for any future template, not just generated
ones. /tmp/sanitise_dotx.py in the session that found it does the replacement;
the check that matters is counting ? in the PDF, never in the .docx.

Proving a template without burning a number

Publishing consumes a number from a gapless sequence and cannot be undone.
official-copy does not. So:

POST /document-templates/{id}/use     → a document, deletable
PUT  /documents/{id}/fields           → stamp NOMOR/TANGGAL by hand
POST /documents/{id}/official-copy    → the merged PDF
GET  /documents/{id}/versions/{v}/content   → read it
DELETE /documents/{id}                → gone

Use an unclassified document for this. A confidential one's bytes are
watermark-gated and the docx route answers protection.unmarkable_format;
classification has no bearing on how fields merge.

What the ceremony still is

Unchanged by the picker, and the reason the code is shaped the way it is:

pre-flight     scheme, sifat, template — nothing exists, everything is cheap
/use           a document exists                    → checkpoint: filed
read back      values that did not land, caught free
publish-letter a NUMBER is consumed. Permanent.     → checkpoint: numbered
set fields     the number onto the page
official-copy  the fields are finally merged        → checkpoint: copied
acl            the people who made the offer can read it

filed is a visible state in the console because it is the last honest cancel
point: the document exists, no number has been consumed, and both Obscura and
the portal will still take it back. After the number, neither will.