{"openapi":"3.1.0","info":{"title":"Hook API","version":"1.0.0","description":"Read your organizations, phishing simulation results and training progress. Every count equals what Native Reports shows for the same scope. How v1 changes over time: https://api.hooksecurity.co/v1/docs#versioning"},"servers":[{"url":"https://api.hooksecurity.co"}],"security":[{"apiKey":[]}],"tags":[{"name":"Organizations","description":"The organizations a key covers."},{"name":"Campaigns","description":"Phishing simulation campaigns and per-person results."},{"name":"Training","description":"Enrollment batches and per-learner progress."},{"name":"Billing","description":"Billable users per Native Account and month."}],"externalDocs":{"description":"AI assistants: the MCP server card","url":"https://api.hooksecurity.co/mcp/server-card"},"paths":{"/v1/organizations":{"get":{"operationId":"listOrganizations","summary":"List organizations","tags":["Organizations"],"description":"Every organization the key covers: an MSP's whole tree, or the key's own organization.","parameters":[{"in":"query","name":"limit","schema":{"description":"Page size, 1 to 500. Defaults to 100.","type":"integer","minimum":1,"maximum":500},"description":"Page size, 1 to 500. Defaults to 100."},{"in":"query","name":"cursor","schema":{"description":"The next_cursor from the previous page.","type":"string","maxLength":512},"description":"The next_cursor from the previous page."}],"responses":{"200":{"description":"A page of organizations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationPage"}}}},"400":{"description":"The request is malformed: bad parameter, unsupported parameter, or invalid cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The organization that owns the key is suspended, or the key's organization tree is too large for one key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or outside the key's coverage. The two are indistinguishable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited at Hook's edge (per key and per IP). Retry after the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The query took too long (query_timeout) or the service is briefly unavailable. Narrow the request or retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{id}":{"get":{"operationId":"getOrganization","summary":"Get an organization","tags":["Organizations"],"parameters":[{"in":"path","name":"id","schema":{"type":"string","pattern":"^org_[1-9][0-9]*$","description":"Organization ID","example":"org_4521"},"required":true,"description":"Organization ID"}],"responses":{"200":{"description":"The organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}}},"400":{"description":"The request is malformed: bad parameter, unsupported parameter, or invalid cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The organization that owns the key is suspended, or the key's organization tree is too large for one key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or outside the key's coverage. The two are indistinguishable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited at Hook's edge (per key and per IP). Retry after the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The query took too long (query_timeout) or the service is briefly unavailable. Narrow the request or retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/campaigns":{"get":{"operationId":"listCampaigns","summary":"List campaigns","tags":["Campaigns"],"description":"Hook-native phishing simulation campaigns in the key's coverage. Counts equal the Native Reports campaign report from the campaign's first send to today (UTC).","parameters":[{"in":"query","name":"organization_id","schema":{"type":"string","pattern":"^org_[1-9][0-9]*$","description":"Only this organization's campaigns","example":"org_4521"}},{"in":"query","name":"limit","schema":{"description":"Page size, 1 to 100. Defaults to 100.","type":"integer","minimum":1,"maximum":100},"description":"Page size, 1 to 100. Defaults to 100."},{"in":"query","name":"cursor","schema":{"description":"The next_cursor from the previous page.","type":"string","maxLength":512},"description":"The next_cursor from the previous page."}],"responses":{"200":{"description":"A page of campaigns.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignPage"}}}},"400":{"description":"The request is malformed: bad parameter, unsupported parameter, or invalid cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The organization that owns the key is suspended, or the key's organization tree is too large for one key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or outside the key's coverage. The two are indistinguishable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited at Hook's edge (per key and per IP). Retry after the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The query took too long (query_timeout) or the service is briefly unavailable. Narrow the request or retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/campaigns/{id}":{"get":{"operationId":"getCampaign","summary":"Get a campaign","tags":["Campaigns"],"parameters":[{"in":"path","name":"id","schema":{"type":"string","pattern":"^cmp_[1-9][0-9]*$","description":"Campaign ID","example":"cmp_4521"},"required":true,"description":"Campaign ID"}],"responses":{"200":{"description":"The campaign.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"400":{"description":"The request is malformed: bad parameter, unsupported parameter, or invalid cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The organization that owns the key is suspended, or the key's organization tree is too large for one key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or outside the key's coverage. The two are indistinguishable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited at Hook's edge (per key and per IP). Retry after the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The query took too long (query_timeout) or the service is briefly unavailable. Narrow the request or retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/campaigns/{id}/results":{"get":{"operationId":"listCampaignResults","summary":"List a campaign's per-person results","tags":["Campaigns"],"description":"One row per recipient of the campaign's live runs. Re-read campaigns active in the last 30 days to pick up new outcomes; results have no change feed in v1.","parameters":[{"in":"path","name":"id","schema":{"type":"string","pattern":"^cmp_[1-9][0-9]*$","description":"Campaign ID","example":"cmp_4521"},"required":true,"description":"Campaign ID"},{"in":"query","name":"outcome","schema":{"description":"Only recipients with this outcome. delivered means the email was sent.","type":"string","enum":["clicked","reported","engaged","opened","delivered"]},"description":"Only recipients with this outcome. delivered means the email was sent."},{"in":"query","name":"limit","schema":{"description":"Page size, 1 to 500. Defaults to 100.","type":"integer","minimum":1,"maximum":500},"description":"Page size, 1 to 500. Defaults to 100."},{"in":"query","name":"cursor","schema":{"description":"The next_cursor from the previous page.","type":"string","maxLength":512},"description":"The next_cursor from the previous page."}],"responses":{"200":{"description":"A page of results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignResultPage"}}}},"400":{"description":"The request is malformed: bad parameter, unsupported parameter, or invalid cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The organization that owns the key is suspended, or the key's organization tree is too large for one key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or outside the key's coverage. The two are indistinguishable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited at Hook's edge (per key and per IP). Retry after the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The query took too long (query_timeout) or the service is briefly unavailable. Narrow the request or retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/enrollment-batches":{"get":{"operationId":"listEnrollmentBatches","summary":"List enrollment batches","tags":["Training"],"description":"Hook-native training assignments. Counts equal the Native Reports enrollment report from the batch's first assignment to today (UTC).","parameters":[{"in":"query","name":"organization_id","schema":{"type":"string","pattern":"^org_[1-9][0-9]*$","description":"Only this organization's batches","example":"org_4521"}},{"in":"query","name":"limit","schema":{"description":"Page size, 1 to 100. Defaults to 100.","type":"integer","minimum":1,"maximum":100},"description":"Page size, 1 to 100. Defaults to 100."},{"in":"query","name":"cursor","schema":{"description":"The next_cursor from the previous page.","type":"string","maxLength":512},"description":"The next_cursor from the previous page."}],"responses":{"200":{"description":"A page of enrollment batches.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollmentBatchPage"}}}},"400":{"description":"The request is malformed: bad parameter, unsupported parameter, or invalid cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The organization that owns the key is suspended, or the key's organization tree is too large for one key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or outside the key's coverage. The two are indistinguishable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited at Hook's edge (per key and per IP). Retry after the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The query took too long (query_timeout) or the service is briefly unavailable. Narrow the request or retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/enrollment-batches/{id}":{"get":{"operationId":"getEnrollmentBatch","summary":"Get an enrollment batch","tags":["Training"],"parameters":[{"in":"path","name":"id","schema":{"type":"string","pattern":"^eb_[1-9][0-9]*$","description":"Enrollment batch ID","example":"eb_4521"},"required":true,"description":"Enrollment batch ID"}],"responses":{"200":{"description":"The batch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollmentBatch"}}}},"400":{"description":"The request is malformed: bad parameter, unsupported parameter, or invalid cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The organization that owns the key is suspended, or the key's organization tree is too large for one key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or outside the key's coverage. The two are indistinguishable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited at Hook's edge (per key and per IP). Retry after the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The query took too long (query_timeout) or the service is briefly unavailable. Narrow the request or retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/enrollments":{"get":{"operationId":"listEnrollments","summary":"List enrollments","tags":["Training"],"description":"One row per learner per batch, including cancelled enrollments.","parameters":[{"in":"query","name":"organization_id","schema":{"type":"string","pattern":"^org_[1-9][0-9]*$","description":"Only this organization's enrollments","example":"org_4521"}},{"in":"query","name":"batch_id","schema":{"type":"string","pattern":"^eb_[1-9][0-9]*$","description":"Only this batch's enrollments","example":"eb_4521"}},{"in":"query","name":"status","schema":{"description":"Comma-separated statuses; overdue is also accepted. A row matching any listed value is returned.","type":"string","maxLength":200}},{"in":"query","name":"limit","schema":{"description":"Page size, 1 to 500. Defaults to 100.","type":"integer","minimum":1,"maximum":500},"description":"Page size, 1 to 500. Defaults to 100."},{"in":"query","name":"cursor","schema":{"description":"The next_cursor from the previous page.","type":"string","maxLength":512},"description":"The next_cursor from the previous page."}],"responses":{"200":{"description":"A page of enrollments.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollmentPage"}}}},"400":{"description":"The request is malformed: bad parameter, unsupported parameter, or invalid cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The organization that owns the key is suspended, or the key's organization tree is too large for one key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or outside the key's coverage. The two are indistinguishable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited at Hook's edge (per key and per IP). Retry after the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The query took too long (query_timeout) or the service is briefly unavailable. Narrow the request or retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/billing/seats":{"get":{"operationId":"listBillingSeats","summary":"List billable users per month","tags":["Billing"],"description":"Billable users for each Native Account the key covers, for one calendar month: the highest daily count of active people on a day the organization was active, the same rule Hook's monthly usage report uses. Organizations still on the legacy platform are not listed; organization.native.account tells them apart. A client deleted during or after the month is still listed for it (organization_state deleted). Clients are grouped by today's organization tree: a client moved to another MSP is listed under its current MSP only. Without month, the current month so far; a cursor belongs to its month.","parameters":[{"in":"query","name":"month","schema":{"description":"Calendar month, UTC (YYYY-MM). Defaults to the current month.","type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$","example":"2026-09"},"description":"Calendar month, UTC (YYYY-MM). Defaults to the current month."},{"in":"query","name":"limit","schema":{"description":"Page size, 1 to 500. Defaults to 100.","type":"integer","minimum":1,"maximum":500},"description":"Page size, 1 to 500. Defaults to 100."},{"in":"query","name":"cursor","schema":{"description":"The next_cursor from the previous page.","type":"string","maxLength":512},"description":"The next_cursor from the previous page."}],"responses":{"200":{"description":"A page of monthly billable users.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSeatsPage"}}}},"400":{"description":"The request is malformed: bad parameter, unsupported parameter, or invalid cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The organization that owns the key is suspended, or the key's organization tree is too large for one key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or outside the key's coverage. The two are indistinguishable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limited at Hook's edge (per key and per IP). Retry after the Retry-After header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The query took too long (query_timeout) or the service is briefly unavailable. Narrow the request or retry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"components":{"schemas":{"OrganizationPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Organization"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as cursor to read the next page; null on the last page."}},"required":["data","next_cursor"]},"Organization":{"type":"object","properties":{"id":{"type":"string","description":"Organization ID","example":"org_4521"},"name":{"type":"string"},"type":{"type":"string","x-extensible-enum":["msp","client"]},"parent_id":{"anyOf":[{"type":"string","description":"Parent organization, when the key covers it","example":"org_4521"},{"type":"null"}]},"state":{"type":"string","x-extensible-enum":["active","suspended"]},"active_user_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"People who are active, not deleted and receive simulations and training. Not the billing seat count."},"native":{"type":"object","properties":{"simulations":{"type":"boolean","description":"On Hook-native phishing simulations today. Campaigns it ran natively earlier are still listed when this is false."},"training":{"type":"boolean","description":"On Hook-native training today. Enrollments it ran natively earlier are still listed when this is false."},"account":{"type":"boolean","description":"A Native Account: created in Hook, not on the legacy platform. Only Native Accounts appear in billing seats."}},"required":["simulations","training","account"]},"created_at":{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},"updated_at":{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"}},"required":["id","name","type","parent_id","state","active_user_count","native","created_at","updated_at"]},"Problem":{"type":"object","properties":{"type":{"type":"string"},"title":{"type":"string"},"status":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"code":{"type":"string","description":"Stable machine-readable error code."},"detail":{"type":"string"},"instance":{"type":"string"},"request_id":{"type":"string"}},"required":["type","title","status","code","instance","request_id"],"description":"RFC 9457 problem details."},"CampaignPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Campaign"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as cursor to read the next page; null on the last page."}},"required":["data","next_cursor"]},"Campaign":{"type":"object","properties":{"id":{"type":"string","description":"Campaign ID","example":"cmp_4521"},"organization_id":{"type":"string","description":"Organization ID","example":"org_4521"},"name":{"type":"string"},"status":{"type":"string","x-extensible-enum":["scheduled","starting","sending","sent","unconfirmed","processing","active","completed","partially_sent","failed","canceled","archived"],"description":"Status of the campaign's most recent run, as on the Campaigns page."},"status_group":{"anyOf":[{"type":"string","x-extensible-enum":["active","scheduled","completed","failed"]},{"type":"null"}],"description":"The Campaigns page filter the status counts under; null for statuses only shown under All."},"launched_by":{"type":"string","x-extensible-enum":["msp","hook","org"],"description":"Who started it: the managing MSP, Hook (Campaign of the Month and Autopilot), or the organization itself."},"scheduled_for":{"type":"string","format":"date-time","description":"When the most recent run was set to start.","example":"2026-10-06T14:00:00.000Z"},"run_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Live runs. A relaunched campaign has more than one."},"recipients":{"anyOf":[{"type":"object","properties":{"sent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"failed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"excluded":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pending":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["sent","failed","excluded","pending"]},{"type":"null"}],"description":"Null when the campaign has over 50,000 recipient records, too many to count here; read its results instead."},"outcomes":{"anyOf":[{"type":"object","properties":{"opened":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"clicked":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"landed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"engaged":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Only counted where engagement_measured is true."},"reported":{"description":"Absent when reported is listed in withheld_outcomes.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["opened","clicked","landed","engaged"]},{"type":"null"}],"description":"Null when recipients is null."},"rates":{"anyOf":[{"type":"object","properties":{"click_rate":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}],"description":"Clicked over sent."},"engagement_rate":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}],"description":"Engaged over landed, among recipients whose teaching page measures engagement."}},"required":["click_rate","engagement_rate"]},{"type":"null"}],"description":"Null when recipients is null."},"engagement_measured":{"type":"boolean","description":"False when the campaign's teaching page predates engagement measurement."},"withheld_outcomes":{"type":"array","items":{"type":"string","x-extensible-enum":["reported"]},"description":"Outcomes hidden for this organization: reported, when reporting metrics are off or the report button is not live."},"created_at":{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},"updated_at":{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"}},"required":["id","organization_id","name","status","status_group","launched_by","scheduled_for","run_count","recipients","outcomes","rates","engagement_measured","withheld_outcomes","created_at","updated_at"]},"CampaignResultPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CampaignResult"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as cursor to read the next page; null on the last page."}},"required":["data","next_cursor"]},"CampaignResult":{"type":"object","properties":{"id":{"type":"string","description":"Recipient result ID","example":"rcp_4521"},"campaign_id":{"type":"string","description":"Campaign ID","example":"cmp_4521"},"organization_id":{"type":"string","description":"Organization ID","example":"org_4521"},"person":{"anyOf":[{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"required":["name","email"]},{"type":"null"}],"description":"Null when the person has since been removed."},"delivery_status":{"type":"string","x-extensible-enum":["scheduled","sent","failed","excluded"]},"sent_at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}]},"opened_at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}]},"clicked_at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}],"description":"First human click. Scanner and automated clicks are never credited."},"landed_at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}]},"engaged_at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}],"description":"Null when not engaged or when engagement_measured is false."},"reported_at":{"description":"Absent when reported is listed in withheld_outcomes.","anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}]},"engagement_measured":{"type":"boolean"},"withheld_outcomes":{"type":"array","items":{"type":"string","x-extensible-enum":["reported"]}},"updated_at":{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"}},"required":["id","campaign_id","organization_id","person","delivery_status","sent_at","opened_at","clicked_at","landed_at","engaged_at","engagement_measured","withheld_outcomes","updated_at"]},"EnrollmentBatchPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EnrollmentBatch"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as cursor to read the next page; null on the last page."}},"required":["data","next_cursor"]},"EnrollmentBatch":{"type":"object","properties":{"id":{"type":"string","description":"Enrollment batch ID","example":"eb_4521"},"organization_id":{"type":"string","description":"Organization ID","example":"org_4521"},"name":{"type":"string"},"course":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]},"launched_by":{"type":"string","x-extensible-enum":["msp","hook","org"],"description":"Who started it: the managing MSP, Hook (Campaign of the Month and Autopilot), or the organization itself."},"scheduled_for":{"anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}]},"due_at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}],"description":"The batch due date. Each enrollment carries its own due_at."},"counts":{"anyOf":[{"type":"object","properties":{"assigned":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"completed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"outstanding":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"overdue":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Not completed and past due. Cancelled enrollments are never counted."},"completion_rate":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}]}},"required":["assigned","completed","outstanding","overdue","completion_rate"]},{"type":"null"}],"description":"Null when the batch has over 50,000 enrollments, too many to count here; list its enrollments instead."},"created_at":{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},"updated_at":{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"}},"required":["id","organization_id","name","course","launched_by","scheduled_for","due_at","counts","created_at","updated_at"]},"EnrollmentPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Enrollment"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as cursor to read the next page; null on the last page."}},"required":["data","next_cursor"]},"Enrollment":{"type":"object","properties":{"id":{"type":"string","description":"Enrollment ID","example":"enr_4521"},"batch_id":{"type":"string","description":"Enrollment batch ID","example":"eb_4521"},"organization_id":{"type":"string","description":"Organization ID","example":"org_4521"},"course":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]},"person":{"anyOf":[{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"}},"required":["name","email"]},{"type":"null"}],"description":"Null when the learner has since been removed."},"status":{"type":"string","x-extensible-enum":["assigned","launched","in_progress","completed","failed","cancelled","expired"]},"overdue":{"type":"boolean"},"launched_by":{"type":"string","x-extensible-enum":["msp","hook","org"],"description":"Who started it: the managing MSP, Hook (Campaign of the Month and Autopilot), or the organization itself."},"assigned_at":{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},"due_at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}]},"started_at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}]},"completed_at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"},{"type":"null"}]},"updated_at":{"type":"string","format":"date-time","description":"ISO-8601 UTC timestamp","example":"2026-10-06T14:00:00.000Z"}},"required":["id","batch_id","organization_id","course","person","status","overdue","launched_by","assigned_at","due_at","started_at","completed_at","updated_at"]},"BillingSeatsPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BillingSeats"}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as cursor to read the next page; null on the last page."}},"required":["data","next_cursor"]},"BillingSeats":{"type":"object","properties":{"organization_id":{"type":"string","description":"Organization ID","example":"org_4521"},"organization_name":{"type":"string"},"parent_id":{"anyOf":[{"type":"string","description":"Parent organization, when the key covers it","example":"org_4521"},{"type":"null"}]},"organization_state":{"type":"string","x-extensible-enum":["active","suspended","deleted"],"description":"The organization today. A client deleted during or after the month still appears for that month, marked deleted."},"month":{"type":"string","description":"Calendar month, UTC (YYYY-MM)","example":"2026-09"},"month_complete":{"type":"boolean","description":"False for the current month: the figures so far, which can still rise."},"billable_users":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The highest active-user count recorded on any day of the month while the organization was active. Null when no day was recorded."},"peak_date":{"anyOf":[{"type":"string","format":"date","description":"Calendar date, UTC","example":"2026-09-14"},{"type":"null"}],"description":"The day billable_users was recorded (the latest day on a tie)."},"purchased_seats":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Seats the organization had on the peak day, when set."},"days_recorded":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days in the month with a recorded count while the organization was active."},"days_inactive":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Days in the month the organization was suspended or its account had expired. Recorded, but never billed."}},"required":["organization_id","organization_name","parent_id","organization_state","month","month_complete","billable_users","peak_date","purchased_seats","days_recorded","days_inactive"]}},"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Settings › API keys, sent as Authorization: Bearer hook_live_…"}}}}