X-06 · Client portal sign-off (optional)
SOP:
SOP_XBRL_Filing.md§6 / Step 6.0Actors: XBRL Reviewer dispatches; Client Approver decides via the magic-link portal. Pre-state: Filing atEXPORTEDwith at least one artifact (BizFinx or XBRL XML). Post-state:AuditEventcaptures the decision. Filing remains atEXPORTED. On revision request, filing back-routes toMAPPING_IN_PROGRESS.
0. Prerequisites
- Filing at
EXPORTED(see X-05). - Client has at least one active contact with
canApprove = true.
1. Steps
1.1 Dispatch the portal link
POST /ops/xbrl/filings/<filingId>/send-for-signoff
Content-Type: application/json
Authorization: Bearer <xbrl-reviewer-jwt>
{ "contactId": "<approverContactId>" }(Or click Send to client for sign-off on the filing detail page.)
The handler:
- Asserts
MANAGE_XBRL_FILING. - Creates a
PortalInvitationwith:resourceType = XBRL_FILINGtoken.product = XBRL_FILING- 30-day expiry
- SHA-256 token hash persisted
- Enqueues outbox event
xbrl.client.signoff_requested. - Emits
AuditEventxbrl.client.signoff_requested.
1.2 Outbound email
Subject: XBRL filing ready for sign-off: {Legal Name} ({period_start} → {period_end}). Body: filing period + UEN + entry point + validation-passed badge + single Open the sign-off portal button.
1.3 Client clicks the link
The portal route verifies the JWT and dispatches to the XBRL branch on token.product. The page renders:
- Filing header — company name, UEN, period, entry point, taxonomy version
- Statement-level previews — Balance Sheet totals + P&L totals
- A validation-passed indicator
- A UEN confirmation input — the approver must echo the captured UEN
- Two CTAs: Approve and Request revision
1.4 Client decides
Approve path:
POST /portal/xbrl/<token>/approve
Content-Type: application/json
{ "uenConfirmation": "<echoed UEN>" }Handler:
- Verifies the echoed UEN matches
Client.xbrlConfig.uen. - Captures the decision; emits
AuditEventxbrl.client.approved. - Sends a confirmation email to the firm.
Filing remains at EXPORTED. Reviewer proceeds to X-07.
Request revision path:
POST /portal/xbrl/<token>/request-revision
Content-Type: application/json
{ "comment": "<reason>" }Handler:
- Captures the comment.
- Emits
AuditEventxbrl.client.revision_requested. - Transitions filing
EXPORTED → MAPPING_IN_PROGRESS(allowed by the FSM).
Reviewer addresses the comment; iterates through validation and export again.
2. Verification
Database
SELECT id, token_hash IS NOT NULL AS has_hash, expires_at
FROM portal_invitations
WHERE resource_type = 'XBRL_FILING' AND resource_id = '<filingId>'
ORDER BY created_at DESC LIMIT 1;Mailpit
One email to the configured approver contact, subject as above.
Audit log
| Event type | Notes |
|---|---|
xbrl.client.signoff_requested | Dispatch |
xbrl.client.approved | Approve path; payload includes UEN-confirmation match flag |
xbrl.client.revision_requested | Revision path; comment in payload |
3. Negative & edge cases
- UEN mismatch — approve path rejects with
400 Bad Requestreason: "uen_mismatch". Filing stays atEXPORTED. No decision captured. - Magic link expired — portal returns
401 Unauthorized. Reviewer reissues; the old link is invalidated. - No approver contact — handler refuses with
400 Bad Requestandreason: "no_approver_contact". Admin must add one. - Filing already
FILED— sign-off endpoint refuses with409 Conflict. The filing has been submitted; revision now requires post-filing recovery. - Multiple signoff dispatches — each creates a fresh invitation; the latest decision wins. The activity log preserves all.
Next
Proceed to X-07 · Mark filed with ACRA.