The 360Onboard V2 API gives your server structured access to published flows, clients, onboarding progress, completed responses, and — when your account includes Client Portal — the complete post-onboarding workspace. Webhook V2 sends rich snapshots when a flow is published, a client is created, or a client completes onboarding.
Existing V1 integrations continue to work unchanged. New integrations should use V2. You do not need to migrate an existing integration until you are ready.
Choose MCP, the API, or Webhooks
360Onboard supports three complementary ways to automate your workspace:
- MCP is for working through an AI assistant such as Claude, ChatGPT, Codex, or Cursor. Connect once with OAuth, then ask the assistant to build flows, inspect responses, manage Client Portal work, configure custom domains, and handle other supported workspace operations in plain English. Start with Connect 360Onboard to Claude & ChatGPT with MCP.
- The V2 API is for software you are building. Use it when your server, application, or automation needs to read or change 360Onboard data directly and predictably.
- Webhooks V2 are for receiving events from 360Onboard. Use them when your system should react as soon as a flow is published, a client is created, or onboarding is completed.
These options are designed to work together. For example, your application can receive completion events through webhooks, your backend can retrieve the full response through the API, and your team can use MCP to investigate the result or create the next piece of Client Portal work.
MCP does not use the API key described below. It uses OAuth and the permissions of the connected 360Onboard account. The rest of this article documents the public V2 API and webhook contracts; the dedicated MCP guide covers connection and everyday use.
Authentication
Generate an API key in Settings → APIs, Webhooks & MCP → API Keys. Public V2 requests pass that key in the apiKey query parameter.
GET https://api.360onboard.com/v2/workspaces?apiKey=sk_live_your_key_here
Base URL: https://api.360onboard.com/v2
Keep API keys on your server. Do not embed them in browser JavaScript, mobile applications, or public repositories.
API keys are account-level. After authenticating, list workspaces to find the workspaceId used by the remaining resource routes.
Discover Your Workspace
curl "https://api.360onboard.com/v2/workspaces?apiKey=sk_live_your_key_here"
{
"object": "list",
"data": [
{
"id": "workspace_uuid",
"object": "workspace",
"name": "Acme Agency",
"slug": "acme-agency",
"api_url": "https://api.360onboard.com/v2/workspaces/workspace_uuid"
}
]
}
Every workspace resource is scoped to the API-key owner. A workspace that does not belong to the key returns 404 rather than exposing whether another account owns it.
API Endpoints
Append ?apiKey=sk_live_your_key_here to every request. When a URL already has query parameters, append the key with &apiKey=....
Flows
| Method | Endpoint | Purpose |
|---|---|---|
GET | /workspaces/{workspaceId}/flows | List published V2 flows. Supports page and limit up to 100. |
GET | /workspaces/{workspaceId}/flows/{flowId} | Get a complete sanitized flow, including steps, design, and public URL. |
GET | /workspaces/{workspaceId}/flows/{flowId}/stats | Get total, completed, in-progress, and not-started clients plus completion rate. |
A rich flow contains its name, type, schema version, published status, design configuration, public-link settings, final public URL, and sanitized step definitions. API headers, webhook secrets, outbound URLs, static recipients, and private integration target IDs are not exposed.
{
"data": {
"id": "flow_uuid",
"object": "flow",
"name": "Client Onboarding",
"type": "client",
"status": "published",
"schema_version": 2,
"public_url": "https://onboard.acme.com/start",
"step_count": 4,
"steps": [
{
"id": "step-1",
"object": "flow_step",
"position": 0,
"type": "questionnaire",
"title": "Company details",
"fields": []
}
]
}
}
Clients
| Method | Endpoint | Purpose |
|---|---|---|
GET | /workspaces/{workspaceId}/clients | List V2 clients. Supports flow_id, page, and limit. |
POST | /workspaces/{workspaceId}/clients | Create a client in a published V2 flow and optionally send the invitation. |
GET | /workspaces/{workspaceId}/clients/{clientId} | Get the client, progress, complete flow, and final onboarding URL. |
PATCH | /workspaces/{workspaceId}/clients/{clientId} | Update supplied identity fields. |
DELETE | /workspaces/{workspaceId}/clients/{clientId} | Soft-delete the client onboarding record. |
GET | /workspaces/{workspaceId}/clients/{clientId}/progress | Get completion percentage, step counts, response ID, and activity timestamps. |
POST | /workspaces/{workspaceId}/clients/{clientId}/remind | Send an invitation reminder unless the client is already complete. |
Create a client:
curl -X POST \
"https://api.360onboard.com/v2/workspaces/workspace_uuid/clients?apiKey=sk_live_your_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: acme-client-2026-08-27" \
-d '{
"flow_id": "flow_uuid",
"email": "jane@acme.com",
"first_name": "Jane",
"last_name": "Smith",
"company_name": "Acme Corp",
"phone": "+15551234567",
"send_invitation": true
}'
flow_id and email are required. send_invitation defaults to true.
The returned client includes the complete associated flow and the final onboarding URL. When the workspace has an appropriate verified custom domain, that custom-domain URL is returned.
Safe Retries with Idempotency-Key
Client creation supports an optional Idempotency-Key header for 24 hours.
- Retrying the same request with the same key returns the original response without creating another client.
- Reusing the key with different request data returns a conflict instead of performing an ambiguous action.
- Use a unique key for every logical client-creation operation.
This is especially important for automation platforms that retry after timeouts and cannot tell whether the first request succeeded.
Responses
| Method | Endpoint | Purpose |
|---|---|---|
GET | /workspaces/{workspaceId}/responses | List rich V2 responses. Supports flow_id, client_id, and status. |
GET | /workspaces/{workspaceId}/responses/{responseId} | Retrieve one response with its related client, flow, and typed step results. |
Response steps preserve their real type instead of flattening everything into generic key/value data. Depending on the flow, a response can include:
- Questionnaire answers and uploaded files
- E-signature signers, signed-document URL, audit events, timestamps, and PDF hash
- Payment amount, currency, provider, and payment time
- Platform access grants and completed guided-access steps
- Scheduling results
- AI and API outputs
- CRM, export, email, SMS, webhook, and client-portal action results
Ephemeral-field values and outbound integration secrets are never included.
{
"id": "provider-file-id",
"object": "file",
"name": "brand-assets.zip",
"size_bytes": 824193,
"mime_type": "application/zip",
"storage_provider": "google_drive",
"url": "https://drive.google.com/file/d/final-file-id",
"uploaded_at": "2026-08-27T18:00:00Z"
}
Client Portal API
Accounts that include Client Portal receive an additional API surface for managing the work that happens after onboarding. It covers client accounts, tasks, milestones, conversations, files, invoices, members, internal notes, templates, activity, portal customization, and agency-profile data.
Client Portal routes use the same API key and workspace scoping as the rest of V2. Their base path is:
https://api.360onboard.com/v2/workspaces/{workspaceId}/portal
Append ?apiKey=sk_live_your_key_here to every request. Send an Idempotency-Key header with POST, PATCH, PUT, and DELETE requests that may be retried.
Availability and Discovery
Start with the discovery endpoint instead of hard-coding the route catalog:
curl "https://api.360onboard.com/v2/workspaces/workspace_uuid/portal?apiKey=sk_live_your_key_here"
The response includes the workspace-specific Client Portal base URL, authentication and idempotency guidance, all supported routes, their HTTP methods, and payload contracts.
If Client Portal is not included for the account, every Client Portal API and MCP operation returns HTTP 403 with a stable error:
{
"error": {
"code": "client_portal_unavailable",
"message": "Client portal not available"
}
}
Portal Overview and Configuration
The endpoints below are relative to /workspaces/{workspaceId}/portal.
| Method | Endpoint | Purpose |
|---|---|---|
GET | /summary | Return dashboard totals, revenue, client health, upcoming tasks, and recent activity. |
GET | /conversations | List account conversations for the workspace. |
GET | /flows | List flows that can be attached to portal work. |
GET | /ledger | Return the workspace payment ledger. |
GET | /link-preview?url={url} | Retrieve safe metadata for a link preview. |
GET, PUT | /config | Read or update the workspace-default portal sidebar, home, theme, and login configuration. |
GET, PUT | /profile | Read or update the client-facing agency profile and team presentation. |
GET | /artifacts/{responseId}/{stepId} | Retrieve a portal-visible onboarding artifact for a completed response step. |
Client Accounts and Account Activity
Portal accounts are the companies or client workspaces shown in Client Portal. They are separate from an onboarding client submission and can contain multiple portal members.
| Method | Endpoint | Purpose |
|---|---|---|
GET, POST | /accounts | List portal accounts or create one. |
GET, PATCH, DELETE | /accounts/{accountId} | Read, update, or soft-delete an account. |
GET | /accounts/{accountId}/activity | Read the account activity timeline. |
GET | /accounts/{accountId}/engagement | Read the account engagement history. |
GET, POST, PATCH | /accounts/{accountId}/comments | List, create, resolve, or reopen comments for an account resource. |
GET, PUT | /accounts/{accountId}/config | Read or update this account's portal customization overrides. |
Create an account without sending an invitation:
curl -X POST \
"https://api.360onboard.com/v2/workspaces/workspace_uuid/portal/accounts?apiKey=sk_live_your_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: acme-portal-account-001" \
-d '{
"companyName": "Acme Corp",
"slug": "acme-corp",
"status": "onboarding"
}'
Include primaryContact when you also want to provision the first portal member. Member payloads support email, name fields, company name, role, sendInvite, and resendExisting.
Tasks and Milestones
Account tasks belong to a client account. Workspace tasks are unassigned work that can be organized before it is associated with a client.
| Method | Endpoint | Purpose |
|---|---|---|
GET, POST | /accounts/{accountId}/tasks | List or create account tasks. |
GET, PATCH, DELETE | /accounts/{accountId}/tasks/{taskId} | Read, update, or soft-delete an account task. |
POST | /accounts/{accountId}/tasks/{taskId}/move | Move a task to another account in the same workspace using targetAccountId. |
GET, POST | /tasks | List or create unassigned workspace tasks. |
GET, PATCH, DELETE | /tasks/{taskId} | Read, update, or soft-delete a workspace task. |
GET, POST, PATCH | /tasks/{taskId}/comments | List, create, resolve, or reopen internal task comments. |
GET, POST | /accounts/{accountId}/milestones | List milestones or create a draft milestone. |
GET, PATCH, DELETE | /accounts/{accountId}/milestones/{milestoneId} | Read a milestone or stage edits/removal. |
POST | /accounts/{accountId}/milestones/{milestoneId}/transition | Start, submit, approve, request changes, or reopen a milestone. |
POST | /accounts/{accountId}/milestones/{milestoneId}/publish | Publish the held changes for one milestone. |
POST | /accounts/{accountId}/milestones/publish | Publish all held milestone changes for an account. |
POST | /accounts/{accountId}/milestones/{milestoneId}/remind | Email the client when a published milestone is waiting for client approval. |
Task payloads support status, priority, milestone and parent relationships, assignees, dates, dependencies, recurrence, flow attachments, file attachments, and ordering. Milestone payloads support approval rules, automatic start/finish rules, dates, descriptions, covers, and ordering.
Messages and Attachments
| Method | Endpoint | Purpose |
|---|---|---|
GET, POST | /accounts/{accountId}/messages | List messages or send a client message/internal note. |
PATCH, DELETE | /accounts/{accountId}/messages/{messageId} | Mark a message as read or soft-delete it. |
POST | /accounts/{accountId}/messages/attachments | Upload a message attachment with multipart/form-data. |
GET | /accounts/{accountId}/messages/{messageId}/attachments/{attachmentIndex} | Retrieve an attachment associated with a message. |
Set kind to message for client-visible communication or note for an internal agency note. A message must include a non-empty body or at least one attachment. Creating a client-visible message can notify the client.
Files, File Groups, Comments, and Annotations
Account paths manage files belonging to one client. Top-level /files and /file-groups paths manage reusable workspace-library resources.
| Method | Endpoint | Purpose |
|---|---|---|
GET, POST | /accounts/{accountId}/file-groups | List or create account file groups. |
PATCH, DELETE | /accounts/{accountId}/file-groups/{groupId} | Update or soft-delete an account file group. |
GET, POST | /accounts/{accountId}/files | List or create account file records. |
GET, PATCH, DELETE | /accounts/{accountId}/files/{fileId} | Read, update, or soft-delete an account file. |
GET, POST | /file-groups | List or create workspace-library groups. |
PATCH, DELETE | /file-groups/{groupId} | Update or soft-delete a workspace-library group. |
GET, POST | /files | List or create workspace-library files. |
PATCH, DELETE | /files/{fileId} | Update or soft-delete a workspace-library file. |
GET, POST, PATCH | /files/{fileId}/comments | List, create, resolve, or reopen internal workspace-file comments. |
GET, POST | /files/{fileId}/annotations | List visual annotations or add a pin/reply. |
PATCH, DELETE | /files/{fileId}/annotations/{annotationId} | Edit, resolve, or soft-delete an annotation. |
File records support purpose, format, kind, storage path or URL, description, approval and contract state, access rules, versions, and display order. Creating a file record does not upload binary content unless the specific endpoint uses multipart/form-data.
Invoices, Members, and Internal Notes
| Method | Endpoint | Purpose |
|---|---|---|
GET, POST | /accounts/{accountId}/invoices | List invoices or create one. |
GET, PATCH, DELETE | /accounts/{accountId}/invoices/{invoiceId} | Read, update, or soft-delete an invoice. |
GET | /accounts/{accountId}/invoices/{invoiceId}/pdf | Generate and download the invoice PDF. |
GET, POST | /accounts/{accountId}/members | List members or provision/invite a member. |
DELETE | /accounts/{accountId}/members/{memberId} | Remove a member from the account. |
POST | /accounts/{accountId}/members/{memberId}/password | Set the member's portal password. |
POST, DELETE | /accounts/{accountId}/members/{memberId}/photo | Upload or remove the member's profile photo. |
GET, POST | /accounts/{accountId}/notes | List or create agency-only account notes. |
PATCH, DELETE | /accounts/{accountId}/notes/{noteId} | Update or soft-delete an internal note. |
POST | /accounts/{accountId}/meet | Create a Google Meet and send the join link to the client. |
Invoice creation requires amount and supports currency, status, dates, memo, billing details, line items, payment references, and recurring-billing metadata. Member invitations, milestone reminders, client-visible messages, and meeting creation can send external notifications.
Portal Templates and Saved Replies
| Method | Endpoint | Purpose |
|---|---|---|
GET, POST | /home-templates | List or create reusable portal-home layouts. |
PATCH, DELETE | /home-templates/{templateId} | Update or soft-delete a home template. |
GET, POST | /milestone-library | List templates/playbooks or create one. |
PATCH, DELETE | /milestone-library/{kind}/{id} | Update a milestone template or delete a template/playbook. |
POST | /milestone-library/copy | Copy selected milestone templates and their tasks to an account. |
GET, POST | /saved-replies | List or create message-composer saved replies. |
DELETE | /saved-replies/{id} | Soft-delete a saved reply. |
Using Client Portal Through MCP
The same Client Portal capabilities are available to connected AI agents through the 360Onboard MCP server at https://app.360onboard.com/api/mcp. MCP uses OAuth instead of an API key. See Connect 360Onboard to Claude & ChatGPT with MCP for setup instructions.
The MCP exposes these Client Portal tools:
portal_capabilities_list— discover every available portal route and payload contract.portal_accounts_manage— accounts, activity, engagement, comments, and account customization.portal_tasks_manage— account tasks, workspace tasks, moves, and internal comments.portal_milestones_manage— milestones, transitions, publishing, and reminders.portal_messages_manage— messages, internal notes, attachments, read state, and deletion.portal_files_manageandportal_file_groups_manage— account and workspace-library resources, comments, and annotations.portal_invoices_manage,portal_members_manage, andportal_notes_manage— client billing records, portal access, and agency notes.portal_workspace_manage— summaries, conversations, flow choices, ledger, configuration, profile, link previews, and meetings.portal_templates_manage— home templates, milestone templates/playbooks, account copies, and saved replies.portal_request— an escape hatch for routes returned byportal_capabilities_listwhen no specialized tool covers the operation.
High-impact MCP actions default to a dry run. Deletes, milestone publishing and reminders, portal-password changes, profile-photo removal, and meeting creation require the agent to repeat the operation with dry_run=false and confirm=true. This protection is specific to MCP; REST API callers are responsible for implementing their own confirmation and authorization controls.
Webhooks V2
Webhooks let 360Onboard notify your server instead of requiring you to poll the API. V2 intentionally supports three high-value events:
| Event | When it fires | Included data |
|---|---|---|
flow.created | A V2 flow is published for the first time. | Complete sanitized flow, steps, design, timestamps, and public URL. |
client.created | A client onboarding is created. | Client identity, progress, complete flow, and final onboarding URL. |
client.completed | A client completes the flow and final storage processing settles. | Client, flow, typed response steps, uploads, signed documents, payments, grants, and audit information. |
Configure an endpoint in Settings → APIs, Webhooks & MCP → Webhooks, or use the webhook-endpoint API described below. New V2 endpoint URLs must use public HTTPS addresses.
Event Envelope and Headers
Every webhook is an HTTPS POST containing an immutable event snapshot.
POST https://your-server.com/360onboard/webhook
Content-Type: application/json
User-Agent: 360Onboard-Webhooks/2.0
X-360Onboard-Webhook-Id: event_uuid
X-360Onboard-Event: client.completed
X-360Onboard-Timestamp: 1787853600
X-360Onboard-Signature: t=1787853600,v1=a1b2c3...
X-360Onboard-Delivery-Attempt: 1
{
"id": "event_uuid",
"object": "event",
"api_version": "v2",
"type": "client.completed",
"workspace_id": "workspace_uuid",
"created_at": "2026-08-27T18:00:00Z",
"data": {
"client": {},
"flow": {},
"response": {}
}
}
flow.created
{
"type": "flow.created",
"data": {
"flow": {
"id": "flow_uuid",
"name": "Q3 Client Onboarding",
"status": "published",
"public_url": "https://onboard.acme.com/q3-onboarding",
"steps": []
}
}
}
client.created
{
"type": "client.created",
"data": {
"client": {
"id": "client_uuid",
"full_name": "Jane Smith",
"email": "jane@acme.com",
"company_name": "Acme Corp",
"flow_id": "flow_uuid",
"flow_name": "Q3 Client Onboarding",
"url": "https://onboard.acme.com/jane-smith",
"progress": {},
"flow": {}
}
}
}
client.completed
{
"type": "client.completed",
"data": {
"client": {},
"flow": {},
"response": {
"id": "response_uuid",
"status": "completed",
"completed_at": "2026-08-27T18:00:00Z",
"steps": [
{
"id": "upload-step",
"type": "questionnaire",
"status": "completed",
"fields": [
{
"id": "brand_assets",
"label": "Upload your brand assets",
"type": "file",
"value": {
"object": "file",
"name": "brand-assets.zip",
"storage_provider": "google_drive",
"url": "https://drive.google.com/file/d/final-file-id"
}
}
]
},
{
"id": "contract-step",
"type": "esignature",
"status": "completed",
"signers": [],
"document": {
"status": "completed",
"url": "https://drive.google.com/file/d/final-contract-id",
"storage_provider": "google_drive",
"sha256": "document_hash",
"finalized_at": "2026-08-27T17:59:58Z"
},
"audit_events": []
}
]
}
}
}
Final Cloud-Storage URLs
For client.completed, 360Onboard waits while queued or active cloud-storage transfers settle. When mirroring succeeds, uploaded files and signed documents use the final connected-provider URL. If no provider is connected or a final provider URL is unavailable, the payload retains a durable 360Onboard URL.
Delivery History and Replay
V2 records every delivery attempt, including its status, attempt count, next retry time, HTTP response status/body, error, timestamps, and original event payload.
In 360Onboard, open Settings → Developer → Webhooks. Each V2 endpoint shows its ten most recent deliveries with the event type, success or failure, HTTP response code, and delivery time. Select a delivery to expand its attempts, request payload, response body, and error. Select Replay to send the original immutable event again.
Delivery history and replay are also available through the V2 API. The log lives in 360Onboard; it is not installed on the receiving website. A receiving application can maintain its own inbound log if desired.
GET /workspaces/{workspaceId}/webhook-endpoints/{endpointId}/deliveries
Use limit to request up to 100 recent deliveries.
{
"object": "list",
"data": [
{
"id": "delivery_uuid",
"object": "webhook_delivery",
"event_id": "event_uuid",
"event_type": "client.completed",
"status": "succeeded",
"attempts": 1,
"response_status": 200,
"response_body": "ok",
"last_error": null,
"delivered_at": "2026-08-27T18:00:02Z",
"payload": {}
}
]
}
Replay a delivery:
POST /workspaces/{workspaceId}/webhook-endpoints/{endpointId}/deliveries/{deliveryId}/replay
Replay resets the delivery attempt and sends the original immutable event payload again. It does not rebuild the event from current database values.
Design receivers to be idempotent by storing X-360Onboard-Webhook-Id. If that event ID has already been processed, return a successful response without performing the downstream action twice.
Automatic Retries
Network errors and non-2xx responses are retried up to eight total attempts. The retry schedule is approximately:
- 1 minute
- 5 minutes
- 15 minutes
- 1 hour
- 4 hours
- 12 hours
- 24 hours
Return a 2xx response promptly after safely accepting the event. Perform slow downstream work asynchronously on your own system.
Verifying Webhook Signatures
Webhook V2 signatures are always enabled. Save the whsec_... secret shown when the endpoint is created.
To verify a request:
- Read the raw request body without parsing or reformatting it.
- Read
X-360Onboard-Timestampand reject stale timestamps. - Build the signed value as
timestamp.rawBody. - Calculate HMAC-SHA256 with the endpoint secret.
- Compare the hexadecimal result with the
v1value inX-360Onboard-Signatureusing a timing-safe comparison.
import crypto from "node:crypto";
export function verify360OnboardWebhook(rawBody, headers, secret) {
const timestamp = headers.get("X-360Onboard-Timestamp");
const signatureHeader = headers.get("X-360Onboard-Signature") || "";
const received = signatureHeader
.split(",")
.find((part) => part.startsWith("v1="))
?.slice(3);
if (!timestamp || !received) return false;
const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!Number.isFinite(ageSeconds) || ageSeconds > 300) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const receivedBuffer = Buffer.from(received, "hex");
const expectedBuffer = Buffer.from(expected, "hex");
return receivedBuffer.length === expectedBuffer.length &&
crypto.timingSafeEqual(receivedBuffer, expectedBuffer);
}
Managing Webhook Endpoints Through the API
| Method | Endpoint | Purpose |
|---|---|---|
GET | /workspaces/{workspaceId}/webhook-endpoints | List V2 endpoints. |
POST | /workspaces/{workspaceId}/webhook-endpoints | Register an endpoint. The signing secret is returned once. |
PATCH | /workspaces/{workspaceId}/webhook-endpoints/{endpointId} | Update URL, subscribed events, or active state. |
DELETE | /workspaces/{workspaceId}/webhook-endpoints/{endpointId} | Delete an endpoint and its delivery history. |
POST | /workspaces/{workspaceId}/webhook-endpoints/{endpointId}/rotate-secret | Rotate the signing secret and return the replacement once. |
GET | /workspaces/{workspaceId}/webhook-endpoints/{endpointId}/deliveries | Inspect delivery attempts and original payloads. |
POST | /workspaces/{workspaceId}/webhook-endpoints/{endpointId}/deliveries/{deliveryId}/replay | Replay the original event. |
Create an endpoint:
curl -X POST \
"https://api.360onboard.com/v2/workspaces/workspace_uuid/webhook-endpoints?apiKey=sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/360onboard/webhook",
"events": ["flow.created", "client.created", "client.completed"]
}'
The response contains the endpoint plus its signing secret. Store the secret immediately because it is not shown again. If it is lost, use the rotate-secret endpoint.
Errors and Rate Limits
Errors use a stable machine-readable code and human-readable message:
{
"error": {
"code": "response_not_found",
"message": "Response not found."
}
}
Common status codes:
200— successful read or action201— resource created400— invalid request401— missing or invalid API key403— API or Client Portal capability is not included for the account404— workspace or resource not found409— idempotency conflict429— rate limit exceeded500— server error
The public API allows 100 requests per minute per API key. Responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. A 429 response also includes Retry-After.
Use webhooks for real-time updates instead of repeatedly polling responses.
Support: david@360onboard.com