Skip to content

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 at EXPORTED with at least one artifact (BizFinx or XBRL XML). Post-state: AuditEvent captures the decision. Filing remains at EXPORTED. On revision request, filing back-routes to MAPPING_IN_PROGRESS.

0. Prerequisites

  • Filing at EXPORTED (see X-05).
  • Client has at least one active contact with canApprove = true.

1. Steps

http
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:

  1. Asserts MANAGE_XBRL_FILING.
  2. Creates a PortalInvitation with:
    • resourceType = XBRL_FILING
    • token.product = XBRL_FILING
    • 30-day expiry
    • SHA-256 token hash persisted
  3. Enqueues outbox event xbrl.client.signoff_requested.
  4. Emits AuditEvent xbrl.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.

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:

http
POST /portal/xbrl/<token>/approve
Content-Type: application/json

{ "uenConfirmation": "<echoed UEN>" }

Handler:

  1. Verifies the echoed UEN matches Client.xbrlConfig.uen.
  2. Captures the decision; emits AuditEvent xbrl.client.approved.
  3. Sends a confirmation email to the firm.

Filing remains at EXPORTED. Reviewer proceeds to X-07.

Request revision path:

http
POST /portal/xbrl/<token>/request-revision
Content-Type: application/json

{ "comment": "<reason>" }

Handler:

  1. Captures the comment.
  2. Emits AuditEvent xbrl.client.revision_requested.
  3. Transitions filing EXPORTED → MAPPING_IN_PROGRESS (allowed by the FSM).

Reviewer addresses the comment; iterates through validation and export again.

2. Verification

Database

sql
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 typeNotes
xbrl.client.signoff_requestedDispatch
xbrl.client.approvedApprove path; payload includes UEN-confirmation match flag
xbrl.client.revision_requestedRevision path; comment in payload

3. Negative & edge cases

  • UEN mismatch — approve path rejects with 400 Bad Request reason: "uen_mismatch". Filing stays at EXPORTED. 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 Request and reason: "no_approver_contact". Admin must add one.
  • Filing already FILED — sign-off endpoint refuses with 409 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.

Internal use only — BreezyCorp