Post Message Guide

for 7punches Embedded web terminal

Last Updated: Aug 25, 2026

PostMessage is the typed, origin-validated messaging protocol used to exchange session/auth and punch-flow events between the 7Punches web terminal and a native host app across the webview boundary.

All messages are namespaced with a 7punches: prefix and validated with type guards plus an origin allowlist for security. The bridge auto-detects the right outbound channel choosing from React Native WebView, iOS WKWebView handler, Android JS interface, or window.parent.postMessage.

Inbound messages (host → terminal)

TypePayloadBehavior
7punches:initsee belowBootstraps company/theme/locale/auth. Sanitized via sanitizeInitPayload.
7punches:destroynoneCancels token refresh, clears auth state.
7punches:set_external_user{ externalUserId }Re-targets external user without re-init; ignored outside idle phase.

Examples:

Init Payload Example

{ "type": "7punches:init", "payload": {
    "companyId": 12345,            // required, number
    "applicationName": "toast",    // required, non-empty string (integration name)
    "refreshToken": "def502...",   // optional — enables silent session resume
    "theme": "dark",               // optional — "light" | "dark"
    "lang": "fr-CA",               // optional — BCP-47 tag
    "externalUserId": "example-emp-987" // optional — skips PIN, auto-resolves user
} }

External User, Destroy

{ "type": "7punches:set_external_user", "payload": { "externalUserId": "toast-emp-987" } }
{ "type": "7punches:destroy" }

Outbound messages (terminal → host)

TypePayloadWhen
7punches:readynoneListener installed; handshake.
7punches:session_token{ refreshToken }After login and each token refresh.
7punches:session_expirednoneSession unrecoverable; host should re-auth (if supported).
7punches:punch_successsee belowPunch completed.
7punches:punch_error{ category, message }Punch failed.
7punches:exitnoneDirect user load-in mode done; host should close webview.

Outbound Examples (aside from Punch):

{ "type": "7punches:ready" }
{ "type": "7punches:session_token", "payload": { "refreshToken": "def502..." } }
{ "type": "7punches:session_expired" }
{ "type": "7punches:exit" }
{ "type": "7punches:punch_error", "payload": {
    "category": "validation",   // "network" | "auth" | "validation" | "server" | "unknown"
    "message": "Too early to clock in." } }

Online Punch Payloads

Punch Success Payload (Punch IN):

{ "type": "7punches:punch_success", "payload": {
    "punchType": "clock_in",               // clock_in | clock_out
    "punchId": 88123,                       // time_punch.id
    "userId": 4567,
    "externalUserId": "example-emp-987",
    "clockedIn": "2026-07-22T12:00:00.000Z",   // authoritative (server)
    "clockedOut": null,                        // null on clock_in
    "roleId": 5521,                         // internal 7shifts role_id
    "externalRoleId": "example-role-42",      // partner role id (planned, requires mapping)
    "shiftId": 99001,
    "departmentId": 310,
    "locationId": 88,
    "tips": null,                           // no tips at clock-in
    "breaks": []                            // no breaks yet
} }

Punch Success Payload (Punch OUT):

{ "type": "7punches:punch_success", "payload": {
    "punchType": "clock_out",              // clock_in | clock_out
    "punchId": 88123,                       // time_punch.id
    "userId": 4567,
    "externalUserId": "example-emp-987",
    "clockedIn": "2026-07-22T12:00:00.000Z",   // authoritative (server)
    "clockedOut": "2026-07-22T20:03:11.000Z",  // authoritative; null on clock_in
    "roleId": 5521,                         // internal 7shifts role_id
    "externalRoleId": "example-role-42",      // partner role id (planned, requires mapping)
    "shiftId": 99001,
    "departmentId": 310,
    "locationId": 88,
    "tips": 42.50,
    "breaks": [
      { "in": "2026-07-22T15:00:00-0400", "out": "2026-07-22T15:30:00-0400", "paid": false }
    ]
} }

Break Start Payload:

{ "type": "7punches:punch_success", "payload": {
    "punchType": "break_start",
    "punchId": 88123,                       // parent time_punch.id
    "userId": 4567,
    "externalUserId": "example-emp-987",
    "clockedIn": "2026-07-22T12:00:00.000Z",
    "clockedOut": null,                     // still on the clock during a break
    "roleId": 5521,
    "externalRoleId": "example-role-42",
    "shiftId": 99001,
    "departmentId": 310,
    "locationId": 88,
    "tips": null,
    "activeBreak": {                        // the break just opened
      "breakId": 77012,
      "in": "2026-07-22T15:00:00-0400",
      "out": null,
      "paid": false
    },
    "breaks": [
      { "breakId": 77012, "in": "2026-07-22T15:00:00-0400", "out": null, "paid": false }
    ]
} }

Break End Payload:

{ "type": "7punches:punch_success", "payload": {
    "punchType": "break_end",
    "punchId": 88123,
    "userId": 4567,
    "externalUserId": "example-emp-987",
    "clockedIn": "2026-07-22T12:00:00.000Z",
    "clockedOut": null,
    "roleId": 5521,
    "externalRoleId": "example-role-42",
    "shiftId": 99001,
    "departmentId": 310,
    "locationId": 88,
    "tips": null,
    "activeBreak": null,                    // break is now closed
    "lastBreakDurationInMinutes": 30,       // KMP `breakDurationInMinutes`
    "breaks": [
      { "breakId": 77012, "in": "2026-07-22T15:00:00-0400", "out": "2026-07-22T15:30:00-0400", "paid": false }
    ]
} }

Offline Punch Payload

{ "type": "7punches:punch_success", "payload": {
    "offline": true,                        // queued in IndexedDB; not yet synced to server
    "time": "2026-07-22T15:00:00-0400",     // DEVICE clock at punch — not server-authoritative
    "companyId": 12345,
    "locationHash": "a1b2c3d4...",          // offline queue keys on hash, not locationId
    "roleId": 5521,                         // null if no role was selected/cached
    "externalRoleId": "example-role-42",      // proposed, requires cache of external maps
    "departmentId": null,                   // null if not applicable
    "tips": null                            // tips are never captured offline
} }

Did this page help you?