Developer documentation56 documented operations

Build with the K2 platform

Scoped API keys, authentication, permissions, request fields, rate limits, cURL examples, and responses for K2Mailer email marketing, PushWave web push, and AI Chat Assistant.

Reference reviewed against the current K2Mailer API routes and API key scope model on August 13, 2026.

K2Mailer authentication

Create scoped API keys for each integration

K2Mailer API keys are tenant-scoped credentials. Create a separate key for production, Zapier, reporting, or another integration, then grant only the scopes that integration needs.

Quick start

  1. 1Open Settings → API, enable API access, and select Create API Key.
  2. 2Name the key, choose the minimum required scopes, choose an expiry, and create it. The full secret is shown once.
  3. 3Send the secret in the Authorization header as a Bearer credential. Keep it in server-side secret storage, not browser JavaScript or a public repository.
Authentication example
curl --request GET   --url 'https://app.k2mailer.com/api/v1/user'   --header 'Authorization: Bearer YOUR_API_KEY'   --header 'Accept: application/json'

Key lifecycle

  • • New secrets are stored as hashes and cannot be displayed again.
  • • Each key can have its own expiry and can be revoked independently.
  • • Last-used time and IP are recorded after successful authentication.
  • • Disabling tenant API access immediately blocks every key for that tenant.

Legacy keys

Existing pre-upgrade API tokens are migrated as Legacy API Key credentials with full-access scope for compatibility. Create a scoped replacement for each integration, switch the integration, then revoke the legacy key.

Scopes

Least-privilege permissions

Every K2Mailer REST endpoint below shows its required scope.

account.readAccount

Read basic account information for the tenant.

clients.writeClients

Create client accounts through approved provisioning workflows.

groups.readGroups

List and view groups.

groups.writeGroups

Create, update, and delete groups.

lists.readLists

List and view contact lists.

lists.writeLists

Create, update, and delete contact lists.

custom_fields.readCustom fields

List and view contact custom fields.

custom_fields.writeCustom fields

Create, update, and delete contact custom fields.

contacts.readContacts

List and view contacts.

contacts.writeContacts

Create, update, remove, and delete contacts.

campaigns.readCampaigns

List and view saved broadcasts/campaigns.

campaigns.writeCampaigns

Create, update, and delete saved broadcasts/campaigns.

401

Invalid, expired, or revoked key. The API intentionally returns the same generic unauthenticated response for each case.

403

A valid key lacks the endpoint scope, or tenant API access has been disabled.

422

Request validation failed. Review the response errors and required request fields.

429

The REST API is limited to 60 requests per minute by the application API middleware.

Collection pagination

Collection endpoints use start_from as a zero-based offset and return up to 500 records. They do not accept a limit parameter.

Response format

Most resources return a status and response JSON envelope. GET /user returns the user object directly. Some business-rule errors can still use HTTP 200 with JSON status: Error, so inspect both layers.

Response behavior

Read both HTTP and JSON status

K2Mailer business-rule errors can return HTTP 200 with a JSON status of Error. Authentication, authorization, validation, and rate-limit failures use 401, 403, 422, and 429.

PushWave key safety

Public and Secret keys are different

A pk_ Public Key is safe for browser integration endpoints. An sk_ Secret Key is private and required for every broadcast operation. Never expose the Secret Key in WordPress front-end JavaScript or a public page.

AI widget origin

Allow the production domain

Public chat session, lead, message, stream, rating, and escalation calls validate the Origin header against the bot’s allowed domains. Add every production domain before launch.

56 operations

Email marketing

K2Mailer REST API

Manage account data, audiences, contacts, custom fields, and saved broadcast content.

Authentication
Bearer API key
Settings → API. Enable API access, create a key, choose its scopes, and copy the secret when it is shown.
Base URL
https://app.k2mailer.com/api/v1
60 requests per minute

Account

2 operations
GET/user

Get the authenticated user

Return the user and workspace identity associated with the authenticated API key.

Authentication

K2Mailer Bearer API key

Required scope

account.read

Rate limit

60/min

Success

HTTP 200

Important

  • Unlike the other K2Mailer REST resources, this endpoint returns the user object without a status/response envelope.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/user' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "id": 7,
  "name": "API User",
  "email": "api@example.com",
  "language": "en",
  "time_zone": "UTC",
  "active": true,
  "is_client": false,
  "primary_product": "k2mailer"
}
POST/client/add

Provision a client account

Create an isolated client workspace linked to the authenticated parent account.

Authentication

K2Mailer Bearer API key

Required scope

clients.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
nameRequiredbodystringClient display name.
emailRequiredbodyemailUnique login email.
passwordRequiredbodystringPassword with at least 8 characters.
list_idbodyintegerOptional list owned by the authenticating account.
productbodystringOptional initial product assignment.
custom_fieldsbodyobjectOptional custom field values.

Important

  • This endpoint is for approved client-provisioning workflows, not ordinary contact creation.
  • The authenticated account must have permission to create clients.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/v1/client/add' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Acme Marketing",
  "email": "owner@acme.example",
  "password": "a-strong-password",
  "product": "K2Mailer"
}'
Example response
{
  "status": "Success",
  "response": {
    "email": "owner@acme.example",
    "login_token": "one-time-login-token"
  }
}

Groups

5 operations
GET/groups

List Groups

Return Groups belonging to the authenticated account.

Authentication

K2Mailer Bearer API key

Required scope

groups.read

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
type_idqueryintegerFilter by group type.
start_fromqueryintegerZero-based result offset. Defaults to 0.

Important

  • This endpoint returns up to 500 records after start_from. It does not accept a limit parameter.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/groups?type_id=1&start_from=0' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": [
    {
      "id": 42,
      "name": "Example group"
    }
  ]
}
GET/groups/{id}

Get a group

Return one tenant-owned group.

Authentication

K2Mailer Bearer API key

Required scope

groups.read

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned group ID.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/groups/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example group"
  }
}
POST/groups

Create a group

Create a new group in the authenticated account.

Authentication

K2Mailer Bearer API key

Required scope

groups.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
nameRequiredbodystringGroup name.
type_idRequiredbodyintegerGroup type: 1 lists, 2 broadcasts, or 3 sending servers.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/v1/groups' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Newsletter audiences",
  "type_id": 1
}'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example group"
  }
}
PATCH/groups/{id}

Update a group

Update fields on an existing tenant-owned group.

Authentication

K2Mailer Bearer API key

Required scope

groups.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned group ID.
namebodystringUpdated group name.
cURL request
curl --request PATCH \
  --url 'https://app.k2mailer.com/api/v1/groups/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Updated newsletter audiences"
}'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example group"
  }
}
DELETE/groups/{id}

Delete a group

Delete a tenant-owned group.

Authentication

K2Mailer Bearer API key

Required scope

groups.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned group ID.
cURL request
curl --request DELETE \
  --url 'https://app.k2mailer.com/api/v1/groups/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example group"
  }
}

Lists

5 operations
GET/lists

List Lists

Return Lists belonging to the authenticated account.

Authentication

K2Mailer Bearer API key

Required scope

lists.read

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
group_idqueryintegerFilter by tenant-owned list group.
start_fromqueryintegerZero-based result offset. Defaults to 0.

Important

  • This endpoint returns up to 500 records after start_from. It does not accept a limit parameter.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/lists?group_id=12&start_from=0' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": [
    {
      "id": 42,
      "name": "Example list"
    }
  ]
}
GET/lists/{id}

Get a list

Return one tenant-owned list.

Authentication

K2Mailer Bearer API key

Required scope

lists.read

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned list ID.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/lists/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example list"
  }
}
POST/lists

Create a list

Create a new list in the authenticated account.

Authentication

K2Mailer Bearer API key

Required scope

lists.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
nameRequiredbodystringList name.
group_idRequiredbodyintegerTenant-owned list group ID.
sending_server_idRequiredbodyintegerTenant-owned sending server ID.
custom_field_idbodyinteger[]Custom fields to associate with the list.
double_optinbodyYes | NoWhether confirmation is required.
welcome_emailbodyYes | NoWhether the list sends a welcome email.
unsub_emailbodyYes | NoWhether to send the list unsubscribe email.
notificationbodyEnabled | DisabledEnable list-owner subscription notifications.
notification_emailbodyemailDestination for enabled list notifications.
notification_criteriabodystringNotification rule stored with the list.

Important

  • Group, sending server, and custom field IDs must all belong to the authenticated account.
  • PATCH does not change the custom fields associated with a list.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/v1/lists' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Product newsletter",
  "group_id": 12,
  "sending_server_id": 4,
  "custom_field_id": [
    8,
    9
  ],
  "double_optin": "Yes",
  "welcome_email": "Yes"
}'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example list"
  }
}
PATCH/lists/{id}

Update a list

Update fields on an existing tenant-owned list.

Authentication

K2Mailer Bearer API key

Required scope

lists.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned list ID.
namebodystringUpdated list name.
group_idbodyintegerUpdated tenant-owned list group ID.
sending_server_idbodyintegerUpdated tenant-owned sending server ID.
double_optinbodyYes | NoWhether confirmation is required.
welcome_emailbodyYes | NoWhether the list sends a welcome email.
unsub_emailbodyYes | NoWhether to send the list unsubscribe email.
notificationbodyEnabled | DisabledEnable list-owner subscription notifications.
notification_emailbodyemailDestination for enabled list notifications.
notification_criteriabodystringNotification rule stored with the list.

Important

  • Group, sending server, and custom field IDs must all belong to the authenticated account.
  • PATCH does not change the custom fields associated with a list.
cURL request
curl --request PATCH \
  --url 'https://app.k2mailer.com/api/v1/lists/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Updated product newsletter",
  "group_id": 12,
  "sending_server_id": 4,
  "double_optin": "Yes"
}'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example list"
  }
}
DELETE/lists/{id}

Delete a list

Delete a tenant-owned list.

Authentication

K2Mailer Bearer API key

Required scope

lists.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned list ID.
cURL request
curl --request DELETE \
  --url 'https://app.k2mailer.com/api/v1/lists/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example list"
  }
}

Custom fields

5 operations
GET/custom-fields

List Custom fields

Return Custom fields belonging to the authenticated account.

Authentication

K2Mailer Bearer API key

Required scope

custom_fields.read

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
start_fromqueryintegerZero-based result offset. Defaults to 0.

Important

  • This endpoint returns up to 500 records after start_from. It does not accept a limit parameter.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/custom-fields?start_from=0' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": [
    {
      "id": 42,
      "name": "Example custom field"
    }
  ]
}
GET/custom-fields/{id}

Get a custom field

Return one tenant-owned custom field.

Authentication

K2Mailer Bearer API key

Required scope

custom_fields.read

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned custom field ID.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/custom-fields/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example custom field"
  }
}
POST/custom-fields

Create a custom field

Create a new custom field in the authenticated account.

Authentication

K2Mailer Bearer API key

Required scope

custom_fields.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
nameRequiredbodystringField label.
typebodystringField type. Defaults to text.
valuebodystring[]Options for radio, checkbox, or dropdown fields.
requiredbodybooleanWhether contacts must provide the field.

Important

  • The options property is named value, singular.
  • PATCH currently changes only the custom field name.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/v1/custom-fields' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Company size",
  "type": "dropdown",
  "value": [
    "1-10",
    "11-50",
    "51+"
  ],
  "required": false
}'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example custom field"
  }
}
PATCH/custom-fields/{id}

Update a custom field

Update fields on an existing tenant-owned custom field.

Authentication

K2Mailer Bearer API key

Required scope

custom_fields.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned custom field ID.
namebodystringUpdated field name.

Important

  • The options property is named value, singular.
  • PATCH currently changes only the custom field name.
cURL request
curl --request PATCH \
  --url 'https://app.k2mailer.com/api/v1/custom-fields/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Updated company size"
}'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example custom field"
  }
}
DELETE/custom-fields/{id}

Delete a custom field

Delete a tenant-owned custom field.

Authentication

K2Mailer Bearer API key

Required scope

custom_fields.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned custom field ID.
cURL request
curl --request DELETE \
  --url 'https://app.k2mailer.com/api/v1/custom-fields/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example custom field"
  }
}

Contacts

6 operations
GET/contacts

List contacts

Return contacts belonging to the authenticated account.

Authentication

K2Mailer Bearer API key

Required scope

contacts.read

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
list_idqueryintegerFilter by tenant-owned list.
start_fromqueryintegerZero-based result offset. Defaults to 0.

Important

  • This endpoint returns up to 500 records after start_from. It does not accept a limit parameter.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/contacts?list_id=15&start_from=0' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": [
    {
      "id": 81,
      "email": "reader@example.com",
      "list_id": 15
    }
  ]
}
GET/contacts/{id}

Get a contact

Return one tenant-owned contact and its saved fields.

Authentication

K2Mailer Bearer API key

Required scope

contacts.read

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned contact ID.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/contacts/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": {
    "id": 81,
    "email": "reader@example.com",
    "list_id": 15,
    "active": "Yes"
  }
}
POST/contacts

Create a contact

Add a contact to a tenant-owned list.

Authentication

K2Mailer Bearer API key

Required scope

contacts.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
emailRequiredbodyemailContact email address.
list_idRequiredbodyintegerTenant-owned destination list.
formatbodyHTML | TextPreferred email format. Defaults to HTML.
activebodyYes | NoContact active state. Defaults to Yes.
confirmbodyYes | NoWhether to run the list confirmation or welcome-email flow. Defaults to Yes.
unsubscribedbodyYes | NoUnsubscribe state. Defaults to No.
custom_fieldsbodyobjectValues keyed by tenant-owned custom field ID.

Important

  • New contacts default to HTML format, active, confirmed, and not unsubscribed.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/v1/contacts' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "email": "reader@example.com",
  "list_id": 15,
  "custom_fields": {
    "8": "Acme",
    "9": "11-50"
  }
}'
Example response
{
  "status": "Success",
  "response": {
    "id": 81,
    "email": "reader@example.com",
    "list_id": 15
  }
}
POST/contacts/remove

Remove a contact by email

Remove an email address from a specific tenant-owned list.

Authentication

K2Mailer Bearer API key

Required scope

contacts.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
emailRequiredbodyemailContact email address.
list_idRequiredbodyintegerTenant-owned list ID.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/v1/contacts/remove' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "email": "reader@example.com",
  "list_id": 15
}'
Example response
{
  "status": "Success",
  "response": "Contact removed"
}
PATCH/contacts/{id}

Update a contact

Update the email address or replace custom field values for a contact.

Authentication

K2Mailer Bearer API key

Required scope

contacts.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned contact ID.
emailbodyemailUpdated email address.
list_idbodyintegerAccepted for validation but the contact remains on its current list.
formatbodyHTML | TextUpdated email format.
activebodyYes | NoUpdated active state.
unsubscribedbodyYes | NoUpdated unsubscribe state.
custom_fieldsbodyobjectReplacement values keyed by custom field ID.

Important

  • A PATCH cannot move the contact to another list.
  • When custom_fields is provided, it replaces the stored custom field values.
cURL request
curl --request PATCH \
  --url 'https://app.k2mailer.com/api/v1/contacts/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "email": "new-address@example.com",
  "custom_fields": {
    "8": "Updated company"
  }
}'
Example response
{
  "status": "Success",
  "response": {
    "id": 81,
    "email": "new-address@example.com"
  }
}
DELETE/contacts/{id}

Delete a contact

Delete a tenant-owned contact by ID.

Authentication

K2Mailer Bearer API key

Required scope

contacts.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned contact ID.
cURL request
curl --request DELETE \
  --url 'https://app.k2mailer.com/api/v1/contacts/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": "Contact deleted"
}

Broadcasts

5 operations
GET/broadcasts

List Broadcasts

Return Broadcasts belonging to the authenticated account.

Authentication

K2Mailer Bearer API key

Required scope

campaigns.read

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
group_idqueryintegerFilter by tenant-owned broadcast group.
start_fromqueryintegerZero-based result offset. Defaults to 0.

Important

  • This endpoint returns up to 500 records after start_from. It does not accept a limit parameter.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/broadcasts?group_id=12&start_from=0' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": [
    {
      "id": 42,
      "name": "Example broadcast"
    }
  ]
}
GET/broadcasts/{id}

Get a broadcast

Return one tenant-owned broadcast.

Authentication

K2Mailer Bearer API key

Required scope

campaigns.read

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned broadcast ID.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/broadcasts/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example broadcast"
  }
}
POST/broadcasts

Create a broadcast

Create a new broadcast in the authenticated account.

Authentication

K2Mailer Bearer API key

Required scope

campaigns.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
nameRequiredbodystringInternal broadcast name.
group_idRequiredbodyintegerTenant-owned broadcast group ID.
email_subjectRequiredbodystringEmail subject line.
htmlbodystringURL-encoded HTML content.
textbodystringURL-encoded plain-text content.

Important

  • This resource saves email content. It does not send or schedule a campaign.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/v1/broadcasts' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "April update",
  "group_id": 22,
  "email_subject": "What is new this month",
  "html": "%3Ch1%3EApril%20update%3C%2Fh1%3E",
  "text": "April%20update"
}'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example broadcast"
  }
}
PATCH/broadcasts/{id}

Update a broadcast

Update fields on an existing tenant-owned broadcast.

Authentication

K2Mailer Bearer API key

Required scope

campaigns.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned broadcast ID.
namebodystringInternal broadcast name.
group_idbodyintegerTenant-owned broadcast group ID.
email_subjectbodystringEmail subject line.
htmlbodystringURL-encoded HTML content.
textbodystringURL-encoded plain-text content.

Important

  • This resource saves email content. It does not send or schedule a campaign.
cURL request
curl --request PATCH \
  --url 'https://app.k2mailer.com/api/v1/broadcasts/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "April update",
  "group_id": 22,
  "email_subject": "What is new this month",
  "html": "%3Ch1%3EApril%20update%3C%2Fh1%3E",
  "text": "April%20update"
}'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example broadcast"
  }
}
DELETE/broadcasts/{id}

Delete a broadcast

Delete a tenant-owned broadcast.

Authentication

K2Mailer Bearer API key

Required scope

campaigns.write

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
idRequiredpathintegerThe tenant-owned broadcast ID.
cURL request
curl --request DELETE \
  --url 'https://app.k2mailer.com/api/v1/broadcasts/42' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Accept: application/json'
Example response
{
  "status": "Success",
  "response": {
    "id": 42,
    "name": "Example broadcast"
  }
}

Web push

PushWave API

Register browser subscriptions, record events, and manage push broadcasts.

Authentication
Public or Secret Key
PushWave → Sites. Use the key required by each endpoint group.
Base URL
https://app.k2mailer.com
10–120 requests per minute, depending on the endpoint

Browser integration

2 operations
GET/api/pushwave/config

Get site configuration

Return public widget, prompt, and site settings for the Public Key.

Authentication

PushWave Public Key

Rate limit

30/min/IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe browser-safe site Public Key beginning with pk_.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/pushwave/config' \
  --header 'X-Api-Key: pk_YOUR_PUBLIC_KEY' \
  --header 'Accept: application/json'
Example response
{
  "soft_prompt_enabled": true,
  "vapid_public_key": "BExampleVapidPublicKey",
  "site_name": "Example site",
  "site_icon": "https://app.k2mailer.com/storage/site-icon.png",
  "data_notice_text": "We use notifications to send important updates.",
  "privacy_policy_url": "https://example.com/privacy",
  "unsubscribe_url": "https://app.k2mailer.com/pushwave/unsubscribed/11",
  "resubscribe_url": "https://example.com/?pushwave_resubscribe=1"
}
GET/api/pushwave/vapid-key

Get VAPID public key

Return the VAPID public key used by the browser Push API.

Authentication

PushWave Public Key

Rate limit

30/min/IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe browser-safe site Public Key beginning with pk_.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/pushwave/vapid-key' \
  --header 'X-Api-Key: pk_YOUR_PUBLIC_KEY' \
  --header 'Accept: application/json'
Example response
{
  "vapid_public_key": "BExampleVapidPublicKey"
}

Subscriptions

3 operations
POST/api/pushwave/subscribe

Subscribe a browser

Register a browser push subscription with the site.

Authentication

PushWave Public Key

Rate limit

10/min/IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe browser-safe site Public Key beginning with pk_.
endpointRequiredbodyURLPush service endpoint, maximum 2,000 characters.
p256dh_keyRequiredbodystringBrowser subscription p256dh key.
auth_keyRequiredbodystringBrowser subscription auth secret.
browserbodystringBrowser name, maximum 100 characters.
browser_versionbodystringBrowser version, maximum 50 characters.
device_typebodydesktop | mobile | tabletDetected device category.
osbodystringOperating system, maximum 100 characters.
timezonebodystringBrowser timezone, maximum 100 characters.
languagebodystringBrowser language, maximum 20 characters.
subscribed_page_urlbodystringPage on which subscription occurred, maximum 2,000 characters.
user_agentbodystringBrowser user agent, maximum 512 characters.
consent_methodbodysoft_prompt | native_prompt | apiHow consent was collected.
consent_page_urlbodystringPage on which consent was granted, maximum 2,000 characters.
confirm_resubscribebodybooleanConfirm a user-requested resubscribe after prior suppression.

Important

  • An endpoint registered to another site returns 409 endpoint_already_registered.
  • A previously suppressed endpoint returns 409 subscriber_suppressed until the user confirms and the request is retried with confirm_resubscribe.
  • A plan limit returns 429 subscriber_limit_reached.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/pushwave/subscribe' \
  --header 'X-Api-Key: pk_YOUR_PUBLIC_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "endpoint": "https://push.example.net/subscription/abc",
  "p256dh_key": "browser-p256dh-key",
  "auth_key": "browser-auth-key",
  "browser": "Chrome",
  "browser_version": "126",
  "device_type": "desktop",
  "os": "Windows",
  "timezone": "Asia/Karachi",
  "language": "en",
  "subscribed_page_url": "https://example.com/pricing",
  "consent_method": "native_prompt"
}'
Example response
{
  "subscriber_id": 9021,
  "status": "subscribed"
}
POST/api/pushwave/unsubscribe

Unsubscribe a browser

Mark the matching browser endpoint as unsubscribed for the site.

Authentication

PushWave Public Key

Rate limit

20/min/IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe browser-safe site Public Key beginning with pk_.
endpointRequiredbodyURLThe browser push service endpoint.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/pushwave/unsubscribe' \
  --header 'X-Api-Key: pk_YOUR_PUBLIC_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "endpoint": "https://push.example.net/subscription/abc"
}'
Example response
{
  "ok": true,
  "affected": 1
}
POST/api/pushwave/prompt-event

Record a prompt event

Record an opt-in prompt impression or outcome.

Authentication

PushWave Public Key

Rate limit

60/min/IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe browser-safe site Public Key beginning with pk_.
eventRequiredbodyimpression | allowed | dismissed | blockedPrompt event. type is accepted as an alias.
urlbodyURLPage on which the event occurred. page_url is accepted as an alias.
ipbodystringOptional visitor IP. Defaults to the request IP.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/pushwave/prompt-event' \
  --header 'X-Api-Key: pk_YOUR_PUBLIC_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "event": "allowed",
  "url": "https://example.com/"
}'
Example response
{
  "success": true
}

Tracking

3 operations
POST/api/track/view

Record a notification view

Record that a subscriber viewed a PushWave broadcast.

Authentication

PushWave Public Key

Rate limit

120/min/IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe browser-safe site Public Key beginning with pk_.
broadcast_idRequiredbodyintegerBroadcast ID for this site.
subscriber_idRequiredbodyintegerSubscriber ID for this site.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/track/view' \
  --header 'X-Api-Key: pk_YOUR_PUBLIC_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "broadcast_id": 314,
  "subscriber_id": 9021
}'
Example response
{
  "ok": true
}
POST/api/track/dismissed

Record prompt dismissal

Record that the site opt-in prompt was dismissed.

Authentication

PushWave Public Key

Rate limit

120/min/IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe browser-safe site Public Key beginning with pk_.
urlbodyURLPage on which the prompt was dismissed.
ipbodystringOptional visitor IP. Defaults to the request IP.

Important

  • This tracks opt-in prompt dismissal, not the closing of a delivered notification.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/track/dismissed' \
  --header 'X-Api-Key: pk_YOUR_PUBLIC_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "url": "https://example.com/pricing"
}'
Example response
{
  "ok": true
}
POST/api/track/pageview

Record a subscriber page view

Associate a page view with an existing PushWave subscriber.

Authentication

PushWave Public Key

Rate limit

120/min/IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe browser-safe site Public Key beginning with pk_.
subscriber_idRequiredbodyintegerSubscriber ID for this site.
urlRequiredbodyURLViewed page URL.
referrerbodyURLOptional referring page.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/track/pageview' \
  --header 'X-Api-Key: pk_YOUR_PUBLIC_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "subscriber_id": 9021,
  "url": "https://example.com/features",
  "referrer": "https://example.com/"
}'
Example response
{
  "ok": true
}

Broadcasts

5 operations
GET/api/pushwave/broadcasts

List broadcasts

Return paginated broadcasts for the site identified by the Secret Key.

Authentication

PushWave Secret Key

Rate limit

60/min/credential

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe private site Secret Key beginning with sk_.
searchquerystringSearch broadcast title or message.
statusquerystringFilter by broadcast status.
fromquerydateCreated-at lower bound.
toquerydateCreated-at upper bound.
per_pagequeryintegerResults per page, maximum 100.
pagequeryintegerPage number.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/pushwave/broadcasts?status=draft&per_page=25&page=1' \
  --header 'X-Api-Key: sk_YOUR_SECRET_KEY' \
  --header 'Accept: application/json'
Example response
{
  "data": [
    {
      "id": 314,
      "title": "New feature available",
      "message": "See what changed in your dashboard.",
      "url": "https://example.com/product-update",
      "status": "draft",
      "audience_count": 0,
      "stats": {
        "sent": 0,
        "views": 0,
        "clicks": 0,
        "view_rate": 0,
        "click_rate": 0
      },
      "created_at": "2026-07-24T09:30:00Z",
      "updated_at": "2026-07-24T09:30:00Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 25,
    "total": 1,
    "from": 1,
    "to": 1
  },
  "links": {
    "first": "https://app.k2mailer.com/api/pushwave/broadcasts?page=1",
    "last": "https://app.k2mailer.com/api/pushwave/broadcasts?page=1",
    "prev": null,
    "next": null
  }
}
POST/api/pushwave/broadcasts

Create a broadcast

Create a draft, send immediately, or schedule a site broadcast.

Authentication

PushWave Secret Key

Rate limit

60/min/credential

Success

HTTP 201

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe private site Secret Key beginning with sk_.
titleRequiredbodystringNotification title, maximum 255 characters.
messageRequiredbodystringNotification message, maximum 2,000 characters.
urlRequiredbodyURLClick-through URL, maximum 2,000 characters.
actionbodydraft | send_now | scheduleCreate behavior. Defaults to draft.
scheduled_atbodydate-timeFuture send time. Required when action is schedule.
schedule_timezonebodytimezoneIANA timezone used for the scheduled time.
segment_idbodyintegerTenant-owned PushWave segment ID.
audience_filtersbodyobjectCountry, browser, device, and subscribed-page filters.
audience_filters.country_codesbodystring[]Two-letter country codes.
audience_filters.browsersbodystring[]Browser names, each up to 100 characters.
audience_filters.device_typesbodystring[]desktop, mobile, or tablet values.
audience_filters.subscribed_pagebodystringSubscribed-page filter, maximum 2,000 characters.
icon_urlbodyURLNotification icon.
show_large_imagebodybooleanEnable the large image.
large_image_urlbodyURLLarge image URL.
multi_action_enabledbodybooleanEnable notification action buttons.
actionsbodyobject[]Up to two action buttons with title and URL.
utm_enabledbodybooleanAppend UTM parameters to click URLs.
utm_sourcebodystringUTM source, maximum 255 characters.
utm_mediumbodystringUTM medium, maximum 255 characters.
utm_campaignbodystringUTM campaign, maximum 255 characters.
utm_termbodystringUTM term, maximum 255 characters.
utm_contentbodystringUTM content, maximum 255 characters.
notification_duration_enabledbodybooleanEnable the custom notification duration.
duration_daysbodyintegerDuration days from 0 to 28.
duration_hoursbodyintegerDuration hours from 0 to 23.
duration_minutesbodyintegerDuration minutes from 0 to 59.
notif_tagbodystringNotification replacement tag, maximum 128 characters.
renotifybodybooleanNotify again when replacing the same tag.
silentbodybooleanRequest silent delivery.
require_interactionbodybooleanKeep the notification visible until interaction.
badge_urlbodyURLNotification badge image.
vibrate_patternbodyinteger[] | stringPositive vibration durations as an array or comma-separated string.
notif_timestampbodyinteger | date-timeNotification timestamp as milliseconds or a parseable date.
notif_dirbodyauto | ltr | rtlNotification text direction.

Important

  • send_now returns HTTP 202. draft and schedule return HTTP 201.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/pushwave/broadcasts' \
  --header 'X-Api-Key: sk_YOUR_SECRET_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "title": "New feature available",
  "message": "See what changed in your dashboard.",
  "url": "https://example.com/product-update",
  "action": "draft",
  "icon_url": "https://example.com/icon.png",
  "audience_filters": {
    "country_codes": [
      "PK",
      "US"
    ],
    "device_types": [
      "desktop",
      "mobile"
    ]
  },
  "utm_enabled": true,
  "utm_source": "pushwave",
  "utm_medium": "web-push",
  "utm_campaign": "product-update"
}'
Example response
{
  "message": "Broadcast created as draft.",
  "data": {
    "id": 314,
    "title": "New feature available",
    "message": "See what changed in your dashboard.",
    "url": "https://example.com/product-update",
    "status": "draft",
    "audience_count": 0,
    "stats": {
      "sent": 0,
      "views": 0,
      "clicks": 0,
      "view_rate": 0,
      "click_rate": 0
    },
    "created_at": "2026-07-24T09:30:00Z",
    "updated_at": "2026-07-24T09:30:00Z"
  }
}
GET/api/pushwave/broadcasts/{broadcast}

Get a broadcast

Return a broadcast owned by the site resolved from the Secret Key.

Authentication

PushWave Secret Key

Rate limit

60/min/credential

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe private site Secret Key beginning with sk_.
broadcastRequiredpathintegerA broadcast belonging to the site resolved from the Secret Key.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/pushwave/broadcasts/314' \
  --header 'X-Api-Key: sk_YOUR_SECRET_KEY' \
  --header 'Accept: application/json'
Example response
{
  "data": {
    "id": 314,
    "title": "New feature available",
    "message": "See what changed in your dashboard.",
    "url": "https://example.com/product-update",
    "status": "draft",
    "audience_count": 0,
    "stats": {
      "sent": 0,
      "views": 0,
      "clicks": 0,
      "view_rate": 0,
      "click_rate": 0
    },
    "created_at": "2026-07-24T09:30:00Z",
    "updated_at": "2026-07-24T09:30:00Z"
  }
}
PATCH/api/pushwave/broadcasts/{broadcast}

Update a broadcast

Update a mutable site broadcast. PUT is also accepted.

Authentication

PushWave Secret Key

Rate limit

60/min/credential

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe private site Secret Key beginning with sk_.
broadcastRequiredpathintegerA broadcast belonging to the site resolved from the Secret Key.
titlebodystringNotification title, maximum 255 characters.
messagebodystringNotification message, maximum 2,000 characters.
urlbodyURLClick-through URL, maximum 2,000 characters.
actionbodydraft | send_now | scheduleCreate behavior. Defaults to draft.
scheduled_atbodydate-timeFuture send time. Required when action is schedule.
schedule_timezonebodytimezoneIANA timezone used for the scheduled time.
segment_idbodyintegerTenant-owned PushWave segment ID.
audience_filtersbodyobjectCountry, browser, device, and subscribed-page filters.
audience_filters.country_codesbodystring[]Two-letter country codes.
audience_filters.browsersbodystring[]Browser names, each up to 100 characters.
audience_filters.device_typesbodystring[]desktop, mobile, or tablet values.
audience_filters.subscribed_pagebodystringSubscribed-page filter, maximum 2,000 characters.
icon_urlbodyURLNotification icon.
show_large_imagebodybooleanEnable the large image.
large_image_urlbodyURLLarge image URL.
multi_action_enabledbodybooleanEnable notification action buttons.
actionsbodyobject[]Up to two action buttons with title and URL.
utm_enabledbodybooleanAppend UTM parameters to click URLs.
utm_sourcebodystringUTM source, maximum 255 characters.
utm_mediumbodystringUTM medium, maximum 255 characters.
utm_campaignbodystringUTM campaign, maximum 255 characters.
utm_termbodystringUTM term, maximum 255 characters.
utm_contentbodystringUTM content, maximum 255 characters.
notification_duration_enabledbodybooleanEnable the custom notification duration.
duration_daysbodyintegerDuration days from 0 to 28.
duration_hoursbodyintegerDuration hours from 0 to 23.
duration_minutesbodyintegerDuration minutes from 0 to 59.
notif_tagbodystringNotification replacement tag, maximum 128 characters.
renotifybodybooleanNotify again when replacing the same tag.
silentbodybooleanRequest silent delivery.
require_interactionbodybooleanKeep the notification visible until interaction.
badge_urlbodyURLNotification badge image.
vibrate_patternbodyinteger[] | stringPositive vibration durations as an array or comma-separated string.
notif_timestampbodyinteger | date-timeNotification timestamp as milliseconds or a parseable date.
notif_dirbodyauto | ltr | rtlNotification text direction.

Important

  • Only draft, scheduled, failed, and failed_partial broadcasts can be changed. Other states return HTTP 409.
cURL request
curl --request PATCH \
  --url 'https://app.k2mailer.com/api/pushwave/broadcasts/314' \
  --header 'X-Api-Key: sk_YOUR_SECRET_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "title": "Updated feature announcement",
  "message": "The launch date has changed."
}'
Example response
{
  "message": "Broadcast updated.",
  "data": {
    "id": 314,
    "title": "New feature available",
    "message": "See what changed in your dashboard.",
    "url": "https://example.com/product-update",
    "status": "draft",
    "audience_count": 0,
    "stats": {
      "sent": 0,
      "views": 0,
      "clicks": 0,
      "view_rate": 0,
      "click_rate": 0
    },
    "created_at": "2026-07-24T09:30:00Z",
    "updated_at": "2026-07-24T09:30:00Z"
  }
}
DELETE/api/pushwave/broadcasts/{broadcast}

Delete a broadcast

Delete a mutable broadcast belonging to the authenticated site.

Authentication

PushWave Secret Key

Rate limit

60/min/credential

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe private site Secret Key beginning with sk_.
broadcastRequiredpathintegerA broadcast belonging to the site resolved from the Secret Key.

Important

  • Only draft, scheduled, failed, and failed_partial broadcasts can be deleted.
cURL request
curl --request DELETE \
  --url 'https://app.k2mailer.com/api/pushwave/broadcasts/314' \
  --header 'X-Api-Key: sk_YOUR_SECRET_KEY' \
  --header 'Accept: application/json'
Example response
{
  "message": "Broadcast deleted."
}

Broadcast actions

3 operations
POST/api/pushwave/broadcasts/{broadcast}/schedule

Schedule a broadcast

Schedule a mutable broadcast for a future time.

Authentication

PushWave Secret Key

Rate limit

60/min/credential

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe private site Secret Key beginning with sk_.
broadcastRequiredpathintegerA broadcast belonging to the site resolved from the Secret Key.
scheduled_atRequiredbodydate-timeA future date and time.
schedule_timezonebodytimezoneIANA timezone for the schedule.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/pushwave/broadcasts/314/schedule' \
  --header 'X-Api-Key: sk_YOUR_SECRET_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "scheduled_at": "2026-08-01 10:00:00",
  "schedule_timezone": "Asia/Karachi"
}'
Example response
{
  "message": "Broadcast scheduled.",
  "data": {
    "id": 314,
    "title": "New feature available",
    "message": "See what changed in your dashboard.",
    "url": "https://example.com/product-update",
    "status": "scheduled",
    "audience_count": 0,
    "stats": {
      "sent": 0,
      "views": 0,
      "clicks": 0,
      "view_rate": 0,
      "click_rate": 0
    },
    "created_at": "2026-07-24T09:30:00Z",
    "updated_at": "2026-07-24T09:30:00Z",
    "scheduled_at": "2026-08-01T05:00:00Z"
  }
}
POST/api/pushwave/broadcasts/{broadcast}/send

Send a broadcast

Queue a mutable broadcast for immediate delivery.

Authentication

PushWave Secret Key

Rate limit

60/min/credential

Success

HTTP 202

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe private site Secret Key beginning with sk_.
broadcastRequiredpathintegerA broadcast belonging to the site resolved from the Secret Key.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/pushwave/broadcasts/314/send' \
  --header 'X-Api-Key: sk_YOUR_SECRET_KEY' \
  --header 'Accept: application/json'
Example response
{
  "message": "Broadcast queued for sending.",
  "data": {
    "id": 314,
    "title": "New feature available",
    "message": "See what changed in your dashboard.",
    "url": "https://example.com/product-update",
    "status": "sending",
    "audience_count": 0,
    "stats": {
      "sent": 0,
      "views": 0,
      "clicks": 0,
      "view_rate": 0,
      "click_rate": 0
    },
    "created_at": "2026-07-24T09:30:00Z",
    "updated_at": "2026-07-24T09:30:00Z"
  }
}
POST/api/pushwave/broadcasts/{broadcast}/test

Send a test notification

Send a test to one site subscriber without starting the broadcast.

Authentication

PushWave Secret Key

Rate limit

60/min/credential

Success

HTTP 200

Parameters

NameLocationTypeDescription
X-Api-KeyRequiredheaderstringThe private site Secret Key beginning with sk_.
broadcastRequiredpathintegerA broadcast belonging to the site resolved from the Secret Key.
subscriber_idbodyintegerTarget tenant-owned subscriber ID.
endpointbodyURLTarget tenant-owned browser endpoint.

Important

  • Provide either subscriber_id or endpoint. A target outside the current site is rejected.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/pushwave/broadcasts/314/test' \
  --header 'X-Api-Key: sk_YOUR_SECRET_KEY' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "subscriber_id": 9021
}'
Example response
{
  "message": "Test notification sent.",
  "data": {
    "broadcast": {
      "id": 314,
      "title": "New feature available",
      "message": "See what changed in your dashboard.",
      "url": "https://example.com/product-update",
      "status": "draft",
      "audience_count": 0,
      "stats": {
        "sent": 0,
        "views": 0,
        "clicks": 0,
        "view_rate": 0,
        "click_rate": 0
      },
      "created_at": "2026-07-24T09:30:00Z",
      "updated_at": "2026-07-24T09:30:00Z"
    },
    "subscriber_id": 9021,
    "success": true
  }
}

AI conversations

AI Chat Assistant API

Power the public chat widget or export bots, conversations, messages, and leads.

Authentication
Public bot token or Bearer personal token
Bot installation settings or AI Chat Assistant → API Tokens.
Base URL
https://app.k2mailer.com
6–60 requests per minute, depending on the endpoint

Public widget

7 operations
GET/api/chat/{public_token}/config

Get widget configuration

Return public visual, copy, launcher, and escalation settings for a bot.

Authentication

AI bot public token

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
public_tokenRequiredpathstringThe bot public token from its installation settings.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/chat/BOT_PUBLIC_TOKEN/config' \
  --header 'Accept: application/json'
Example response
{
  "status": "active",
  "bot_display_name": "K2 Assistant",
  "greeting_message": "How can I help?",
  "primary_color": "#0052CC",
  "position": "right",
  "show_branding": true,
  "fallback_message": "I could not find that in the knowledge base.",
  "lead_capture_after": 3,
  "suggested_questions": [
    "How do I start?",
    "What plans are available?"
  ],
  "show_escalation": true
}
POST/api/chat/{public_token}/session/start

Start or resume a session

Start a chat session or validate and resume an existing session token.

Authentication

AI bot public token

Rate limit

20/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
public_tokenRequiredpathstringThe bot public token from its installation settings.
OriginRequiredheaderURLA domain allowed in the bot settings.
existing_session_tokenbodystringA prior 64-character session token to resume.

Important

  • A resumed session must belong to the same bot and match its stored visitor IP and origin context.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/chat/BOT_PUBLIC_TOKEN/session/start' \
  --header 'Origin: https://your-site.example' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{}'
Example response
{
  "session_token": "f3d9b46b973f7ac76dd8f916c1c4e3a3524f975ad142df6c47c0d8191a0c227b"
}
POST/api/chat/{public_token}/lead

Capture a lead

Attach a verified-format email address to an active chat session.

Authentication

AI bot public token

Rate limit

6/min

Success

HTTP 201

Parameters

NameLocationTypeDescription
public_tokenRequiredpathstringThe bot public token from its installation settings.
OriginRequiredheaderURLA domain allowed in the bot settings.
session_tokenRequiredbodystringThe 64-character lowercase hexadecimal chat session token.
emailRequiredbodyemailLead email address with a valid DNS domain.
websitebodystringSpam honeypot. Legitimate clients must leave this empty.

Important

  • The website field is a spam honeypot and should not be populated by legitimate clients.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/chat/BOT_PUBLIC_TOKEN/lead' \
  --header 'Origin: https://your-site.example' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "session_token": "f3d9b46b973f7ac76dd8f916c1c4e3a3524f975ad142df6c47c0d8191a0c227b",
  "email": "visitor@example.com"
}'
Example response
{
  "captured": true,
  "email": "visitor@example.com"
}
POST/api/chat/{public_token}/message

Send a message

Send a visitor message and receive the assistant reply as JSON.

Authentication

AI bot public token

Rate limit

20/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
public_tokenRequiredpathstringThe bot public token from its installation settings.
OriginRequiredheaderURLA domain allowed in the bot settings.
session_tokenRequiredbodystringThe 64-character lowercase hexadecimal chat session token.
messageRequiredbodystringVisitor message, maximum 2,000 characters.

Important

  • The workspace monthly message limit can return HTTP 429.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/chat/BOT_PUBLIC_TOKEN/message' \
  --header 'Origin: https://your-site.example' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "session_token": "f3d9b46b973f7ac76dd8f916c1c4e3a3524f975ad142df6c47c0d8191a0c227b",
  "message": "How do I create a campaign?"
}'
Example response
{
  "message": "Open Campaigns, choose Create campaign, then select your list.",
  "message_id": 712,
  "citations": [],
  "lead_capture_required": false,
  "lead_prompt_message": null,
  "awaiting_lead_email": false
}
GET/api/chat/{public_token}/stream

Stream an assistant reply

Receive the assistant reply incrementally as a Server-Sent Events stream.

Authentication

AI bot public token

Rate limit

20/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
public_tokenRequiredpathstringThe bot public token from its installation settings.
OriginRequiredheaderURLA domain allowed in the bot settings.
session_tokenRequiredquerystringThe 64-character lowercase hexadecimal chat session token.
messageRequiredquerystringURL-encoded visitor message, maximum 2,000 characters.

Important

  • Set Accept: text/event-stream and process each data event until done is true.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/chat/BOT_PUBLIC_TOKEN/stream?session_token=f3d9b46b973f7ac76dd8f916c1c4e3a3524f975ad142df6c47c0d8191a0c227b&message=How+do+I+create+a+campaign%3F' \
  --header 'Origin: https://your-site.example' \
  --header 'Accept: text/event-stream'
Example response
data: {"chunk":"Open "}

data: {"chunk":"Campaigns"}

data: {"done":true,"citations":[],"message_id":712,"session_token":"f3d9…227b","origin_domain":"your-site.example"}

POST/api/chat/{public_token}/rate

Rate an assistant message

Save positive or negative feedback for an assistant response.

Authentication

AI bot public token

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
public_tokenRequiredpathstringThe bot public token from its installation settings.
OriginRequiredheaderURLA domain allowed in the bot settings.
session_tokenRequiredbodystringThe 64-character lowercase hexadecimal chat session token.
message_idRequiredbodyintegerAssistant message in this session.
ratingRequiredbody-1 | 1Negative or positive rating.
commentbodystringOptional feedback, maximum 500 characters.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/chat/BOT_PUBLIC_TOKEN/rate' \
  --header 'Origin: https://your-site.example' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "session_token": "f3d9b46b973f7ac76dd8f916c1c4e3a3524f975ad142df6c47c0d8191a0c227b",
  "message_id": 712,
  "rating": 1,
  "comment": "This answered my question."
}'
Example response
{
  "rated": true,
  "rating": 1
}
POST/api/chat/{public_token}/escalate

Request escalation

Mark a conversation as requiring human follow-up.

Authentication

AI bot public token

Rate limit

60/min

Success

HTTP 200

Parameters

NameLocationTypeDescription
public_tokenRequiredpathstringThe bot public token from its installation settings.
OriginRequiredheaderURLA domain allowed in the bot settings.
session_tokenRequiredbodystringThe 64-character lowercase hexadecimal chat session token.
cURL request
curl --request POST \
  --url 'https://app.k2mailer.com/api/chat/BOT_PUBLIC_TOKEN/escalate' \
  --header 'Origin: https://your-site.example' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "session_token": "f3d9b46b973f7ac76dd8f916c1c4e3a3524f975ad142df6c47c0d8191a0c227b"
}'
Example response
{
  "escalated": true
}

Developer exports

5 operations
GET/api/v1/bots

List bots

Return bots visible to a personal API token with bots:read permission.

Authentication

AI personal Bearer token

Rate limit

60/min/token+IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
AuthorizationRequiredheaderBearer k2chat_*A private AI Chat Assistant personal API token.
pagequeryintegerPage number. Returns 20 bots per page.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/bots?page=1' \
  --header 'Authorization: Bearer k2chat_YOUR_TOKEN' \
  --header 'Accept: application/json'
Example response
{
  "current_page": 1,
  "data": [
    {
      "id": 33,
      "name": "Support bot",
      "status": "active",
      "default_locale": "en",
      "monthly_message_count": 125,
      "sessions_count": 18,
      "leads_count": 4,
      "documents_count": 7
    }
  ],
  "per_page": 20,
  "total": 1
}
GET/api/v1/bots/{botId}/sessions

List bot sessions

Return conversations for a bot using conversations:read permission.

Authentication

AI personal Bearer token

Rate limit

60/min/token+IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
AuthorizationRequiredheaderBearer k2chat_*A private AI Chat Assistant personal API token.
botIdRequiredpathintegerA bot belonging to the personal API token account.
sincequerydate-timeOnly sessions updated after this time.
pagequeryintegerPage number. Returns 50 sessions per page.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/bots/33/sessions?since=2026-07-01T00%3A00%3A00Z&page=1' \
  --header 'Authorization: Bearer k2chat_YOUR_TOKEN' \
  --header 'Accept: application/json'
Example response
{
  "current_page": 1,
  "data": [
    {
      "id": 501,
      "bot_id": 33,
      "visitor_email": "visitor@example.com",
      "origin_domain": "your-site.example",
      "locale": "en",
      "message_count": 4,
      "lead_captured": true,
      "escalated": false,
      "started_at": "2026-07-24T09:00:00Z",
      "last_activity_at": "2026-07-24T09:03:00Z"
    }
  ],
  "per_page": 50,
  "total": 1
}
GET/api/v1/bots/{botId}/sessions/{sessionId}/messages

List session messages

Return messages in a bot session using conversations:read permission.

Authentication

AI personal Bearer token

Rate limit

60/min/token+IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
AuthorizationRequiredheaderBearer k2chat_*A private AI Chat Assistant personal API token.
botIdRequiredpathintegerA bot belonging to the personal API token account.
sessionIdRequiredpathintegerSession belonging to the requested bot.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/bots/33/sessions/501/messages' \
  --header 'Authorization: Bearer k2chat_YOUR_TOKEN' \
  --header 'Accept: application/json'
Example response
[
  {
    "id": 711,
    "role": "user",
    "content": "How do I create a campaign?",
    "created_at": "2026-07-24T09:01:00Z"
  },
  {
    "id": 712,
    "role": "assistant",
    "content": "Open Campaigns and choose Create campaign.",
    "created_at": "2026-07-24T09:01:02Z"
  }
]
GET/api/v1/bots/{botId}/sessions/{sessionId}/export

Export a session

Return a conversation export containing session metadata and messages.

Authentication

AI personal Bearer token

Rate limit

60/min/token+IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
AuthorizationRequiredheaderBearer k2chat_*A private AI Chat Assistant personal API token.
botIdRequiredpathintegerA bot belonging to the personal API token account.
sessionIdRequiredpathintegerSession belonging to the requested bot.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/bots/33/sessions/501/export' \
  --header 'Authorization: Bearer k2chat_YOUR_TOKEN' \
  --header 'Accept: application/json'
Example response
{
  "session": {
    "id": 501,
    "started_at": "2026-07-24T09:00:00Z"
  },
  "messages": [
    {
      "role": "user",
      "content": "How do I create a campaign?"
    },
    {
      "role": "assistant",
      "content": "Open Campaigns and choose Create campaign."
    }
  ]
}
GET/api/v1/bots/{botId}/leads

List bot leads

Return captured leads using leads:read permission.

Authentication

AI personal Bearer token

Rate limit

60/min/token+IP

Success

HTTP 200

Parameters

NameLocationTypeDescription
AuthorizationRequiredheaderBearer k2chat_*A private AI Chat Assistant personal API token.
botIdRequiredpathintegerA bot belonging to the personal API token account.
sincequerydate-timeOnly leads created after this time.
pagequeryintegerPage number. Returns 100 leads per page.
cURL request
curl --request GET \
  --url 'https://app.k2mailer.com/api/v1/bots/33/leads?since=2026-07-01T00%3A00%3A00Z&page=1' \
  --header 'Authorization: Bearer k2chat_YOUR_TOKEN' \
  --header 'Accept: application/json'
Example response
{
  "current_page": 1,
  "data": [
    {
      "id": 185,
      "bot_id": 33,
      "session_id": 501,
      "email": "visitor@example.com",
      "captured_at": "2026-07-24T09:03:00Z"
    }
  ],
  "per_page": 100,
  "total": 1
}