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)
| Type | Payload | Behavior |
|---|---|---|
7punches:init | see below | Bootstraps company/theme/locale/auth. Sanitized via sanitizeInitPayload. |
7punches:destroy | none | Cancels 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)
| Type | Payload | When |
|---|---|---|
7punches:ready | none | Listener installed; handshake. |
7punches:session_token | { refreshToken } | After login and each token refresh. |
7punches:session_expired | none | Session unrecoverable; host should re-auth (if supported). |
7punches:punch_success | see below | Punch completed. |
7punches:punch_error | { category, message } | Punch failed. |
7punches:exit | none | Direct 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
} }Updated about 6 hours ago
