Produktly API (1.0.0)
Download OpenAPI specification:Download
The Produktly REST API gives you programmatic access to your product tours, checklists, smart tips, announcements, micro surveys, changelogs, roadmaps, feedback widgets, NPS widgets, tags, analytics, and identified users. It exposes the same functionality as the MCP server, over plain HTTP.
All endpoints (except GET /openapi.json) require an API key passed as a Bearer token.
- Sign in to Produktly and open Settings → API keys.
- Generate a new key and copy it. The same key works for both REST and MCP.
- Send it on every request:
GET /api/v1/changelogs HTTP/1.1
Host: api.produktly.com
Authorization: Bearer <your-api-key>
Missing or invalid keys return 401 unauthorized. Treat the key like a password — anyone with it can read and write your Produktly data.
List endpoints return an envelope with a data array and pagination metadata:
{
"data": [ { "id": 1, "name": "..." } ],
"pagination": { "total": 42, "limit": 50, "offset": 0 }
}
Single-resource endpoints (e.g. GET /roadmaps/{id}) return the resource directly, no envelope.
List endpoints accept limit (default 50, max 100) and offset (default 0) query parameters. Date filters (startDate, endDate) compose with pagination where supported.
Errors come back in a consistent shape:
{
"error": {
"code": "invalid_tag_names",
"message": "Unknown tag names: foo. Use GET /v1/tags to see available tags.",
"details": { "missing": ["foo"] }
}
}
Common codes:
unauthorized(401) — missing or invalid API keynot_found(404) — resource doesn't exist or doesn't belong to your companyvalidation_error(400) — invalid request parametersrate_limit_exceeded(429) — see Rate limits belowinternal_error(500) — server-side problem; safe to retry
Every API key is limited to 60 requests per minute. The /users endpoints have a separate bucket of 300 requests per minute to support backend syncs. Every response includes:
X-RateLimit-Limit: total allowed in the windowX-RateLimit-Remaining: remaining quotaX-RateLimit-Reset: Unix seconds when the bucket refills
When you exceed the limit you'll get 429 rate_limit_exceeded with a Retry-After header. Back off and retry.
List posts in a changelog
Authorizations:
path Parameters
| changelogId required | integer > 0 |
query Parameters
| limit | integer ( 0 .. 100 ] Default: 50 |
| offset | integer or null >= 0 Default: 0 |
| startDate | string |
| endDate | string |
Responses
Response samples
- 200
- 401
- 404
- 429
{- "data": [
- {
- "id": 0,
- "title": "string",
- "description": "string",
- "date": "2019-08-24T14:15:22Z",
- "active": true,
- "tags": [
- {
- "id": 0,
- "name": "string",
- "backgroundColor": "string",
- "textColor": "string"
}
]
}
], - "pagination": {
- "total": 0,
- "limit": 0,
- "offset": 0
}
}Create a changelog post
Authorizations:
path Parameters
| changelogId required | integer > 0 |
Request Body schema: application/json
| title required | string non-empty |
| description | string |
| date | string |
| active | boolean |
| tagNames | Array of strings |
Responses
Request samples
- Payload
{- "title": "string",
- "description": "string",
- "date": "string",
- "active": true,
- "tagNames": [
- "string"
]
}Response samples
- 201
- 400
- 401
- 404
- 429
{- "id": 0,
- "title": "string",
- "description": "string",
- "date": "2019-08-24T14:15:22Z",
- "active": true,
- "tags": [
- {
- "id": 0,
- "name": "string",
- "backgroundColor": "string",
- "textColor": "string"
}
]
}Update a changelog post
Authorizations:
path Parameters
| changelogId required | integer > 0 |
| postId required | integer > 0 |
Request Body schema: application/json
| title | string |
| description | string |
| date | string |
| active | boolean |
| tagNames | Array of strings |
Responses
Request samples
- Payload
{- "title": "string",
- "description": "string",
- "date": "string",
- "active": true,
- "tagNames": [
- "string"
]
}Response samples
- 200
- 400
- 401
- 404
- 429
{- "id": 0,
- "title": "string",
- "description": "string",
- "date": "2019-08-24T14:15:22Z",
- "active": true,
- "tags": [
- {
- "id": 0,
- "name": "string",
- "backgroundColor": "string",
- "textColor": "string"
}
]
}Create a feedback widget
Creates the widget as a draft. Pass active: true to publish immediately — the widget must have at least one option and targeting set, otherwise it stays a draft and the response note explains why. Supply options directly or use optionsPreset for a built-in set. The response includes a dashboardUrl for reviewing the widget in the builder.
Authorizations:
Request Body schema: application/json
| name required | string non-empty |
Array of objects The answer options users pick from | |
| optionsPreset | integer [ 1 .. 6 ] Use a built-in option set instead of listing options: 1 issue/idea/other, 2 three emojis, 3 five emojis, 4 four emojis, 5 feelings, 6 issue/idea/happy/angry/other |
object | |
object or object or object | |
| themeId | integer > 0 |
| folderId | integer > 0 |
| active | boolean Publish immediately. Needs at least one option and targeting |
Responses
Request samples
- Payload
{- "name": "string",
- "options": [
- {
- "name": "string",
- "emoji": "string",
- "type": "string"
}
], - "optionsPreset": 1,
- "settings": {
- "question": "string",
- "showBeacon": true,
- "collapsedText": "string",
- "beaconLocation": "mid-right",
- "collectEmail": true,
- "emailOptional": true,
- "shouldValidateEmail": true,
- "allowScreenshots": true,
- "autoCaptureScreenshot": true,
- "requireScreenshot": true,
- "themeColor": "string",
- "textColor": "string",
- "emailLabelText": "string",
- "emailOptionalLabelText": "string",
- "messageLabelText": "string",
- "submitBtnText": "string",
- "submitAnotherBtnText": "string",
- "feedbackSuccessText": "string",
- "feedbackErrorText": "string",
- "emailMissingErrorText": "string",
- "emailInvalidErrorText": "string",
- "messageMissingErrorText": "string",
- "screenshotLabelText": "string",
- "screenshotMissingErrorText": "string",
- "slackWebhookUrl": "string",
- "discordWebhookUrl": "string",
- "zapierWebhookUrl": "string",
- "customWebhookUrl": "string",
- "notificationEmails": "string"
}, - "targeting": {
- "showOnAllPages": true
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 201
- 400
- 401
- 403
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "options": [
- {
- "property1": null,
- "property2": null
}
], - "dashboardUrl": "string",
- "note": "string"
}List feedback responses
Authorizations:
path Parameters
| widgetId required | integer > 0 |
query Parameters
| limit | integer ( 0 .. 100 ] Default: 50 |
| offset | integer or null >= 0 Default: 0 |
| startDate | string |
| endDate | string |
Responses
Response samples
- 200
- 401
- 429
{- "data": [
- {
- "id": 0,
- "type": "string",
- "optionName": "string",
- "emoji": "string",
- "message": "string",
- "email": "string",
- "name": "string",
- "fromUrl": "string",
- "createdAt": "2019-08-24T14:15:22Z"
}
], - "pagination": {
- "total": 0,
- "limit": 0,
- "offset": 0
}
}Response samples
- 200
- 401
- 404
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "options": [
- {
- "property1": null,
- "property2": null
}
], - "dashboardUrl": "string",
- "note": "string"
}Update a feedback widget
Only the fields you send are changed. settings are merged over stored settings; targeting and options (whether passed directly or via optionsPreset) replace wholesale. Live widgets can be edited, but an update that would leave one unpublishable (no options or no targeting) returns 400; set active: false first to take it down.
Authorizations:
path Parameters
| widgetId required | integer > 0 |
Request Body schema: application/json
| name | string non-empty |
Array of objects Replaces all options | |
| optionsPreset | integer [ 1 .. 6 ] |
object Merged over existing settings | |
object or object or object | |
| themeId | integer or null > 0 |
| folderId | integer or null > 0 |
| active | boolean |
Responses
Request samples
- Payload
{- "name": "string",
- "options": [
- {
- "name": "string",
- "emoji": "string",
- "type": "string"
}
], - "optionsPreset": 1,
- "settings": {
- "question": "string",
- "showBeacon": true,
- "collapsedText": "string",
- "beaconLocation": "mid-right",
- "collectEmail": true,
- "emailOptional": true,
- "shouldValidateEmail": true,
- "allowScreenshots": true,
- "autoCaptureScreenshot": true,
- "requireScreenshot": true,
- "themeColor": "string",
- "textColor": "string",
- "emailLabelText": "string",
- "emailOptionalLabelText": "string",
- "messageLabelText": "string",
- "submitBtnText": "string",
- "submitAnotherBtnText": "string",
- "feedbackSuccessText": "string",
- "feedbackErrorText": "string",
- "emailMissingErrorText": "string",
- "emailInvalidErrorText": "string",
- "messageMissingErrorText": "string",
- "screenshotLabelText": "string",
- "screenshotMissingErrorText": "string",
- "slackWebhookUrl": "string",
- "discordWebhookUrl": "string",
- "zapierWebhookUrl": "string",
- "customWebhookUrl": "string",
- "notificationEmails": "string"
}, - "targeting": {
- "showOnAllPages": true
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 200
- 400
- 401
- 404
- 409
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "options": [
- {
- "property1": null,
- "property2": null
}
], - "dashboardUrl": "string",
- "note": "string"
}Response samples
- 200
- 401
- 429
{- "data": [
- {
- "id": 0,
- "name": "string",
- "publicName": "string",
- "active": true,
- "customDomain": "string",
- "publicId": "string",
- "sections": [
- {
- "id": 0,
- "name": "string",
- "items": [
- {
- "id": 0,
- "name": "string",
- "description": "string",
- "votesCount": 0,
- "updatedAt": "2019-08-24T14:15:22Z",
- "tag": {
- "id": 0,
- "name": "string",
- "backgroundColor": "string",
- "textColor": "string"
}
}
]
}
]
}
], - "pagination": {
- "total": 0,
- "limit": 0,
- "offset": 0
}
}Get roadmap with sections and items
Authorizations:
path Parameters
| roadmapId required | integer > 0 |
query Parameters
| startDate | string |
| endDate | string |
| latestCount | integer ( 0 .. 100 ] |
| limit | integer ( 0 .. 100 ] |
| query | string |
| tagNames | Array of strings |
Responses
Response samples
- 200
- 401
- 404
- 429
{- "id": 0,
- "name": "string",
- "publicName": "string",
- "active": true,
- "customDomain": "string",
- "publicId": "string",
- "sections": [
- {
- "id": 0,
- "name": "string",
- "items": [
- {
- "id": 0,
- "name": "string",
- "description": "string",
- "votesCount": 0,
- "updatedAt": "2019-08-24T14:15:22Z",
- "tag": {
- "id": 0,
- "name": "string",
- "backgroundColor": "string",
- "textColor": "string"
}
}
]
}
]
}Create an NPS widget
Creates the widget as a draft. Pass active: true to publish immediately — the widget must have targeting set, otherwise it stays a draft and the response note explains why. The response includes a dashboardUrl for reviewing the widget in the builder.
Authorizations:
Request Body schema: application/json
| name required | string non-empty |
object | |
object or object or object | |
| themeId | integer > 0 |
| folderId | integer > 0 |
| active | boolean Publish immediately. Needs targeting |
Responses
Request samples
- Payload
{- "name": "string",
- "settings": {
- "selectedScale": "ten",
- "title": "string",
- "scaleLabelLeastLikely": "string",
- "scaleLabelMostLikely": "string",
- "collectEmail": true,
- "emailRequired": true,
- "shouldValidateEmail": true,
- "emailText": "string",
- "emailMissingErrorText": "string",
- "emailInvalidErrorText": "string",
- "collectFeedback": true,
- "msgText": "string",
- "msgPlaceholderText": "string",
- "submitBtnText": "string",
- "successText": "string",
- "closeBtnText": "string",
- "displayType": "bottombar",
- "displayTrigger": "element_click",
- "displayTriggerTime": 0,
- "displayTriggerSelector": "string",
- "display": "once_a_month",
- "dismissOn": "cross_click",
- "themeColor": "string",
- "textColor": "string",
- "slackWebhookUrl": "string",
- "discordWebhookUrl": "string",
- "zapierWebhookUrl": "string",
- "customWebhookUrl": "string",
- "notificationEmails": "string"
}, - "targeting": {
- "showOnAllPages": true
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 201
- 400
- 401
- 403
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Get NPS score breakdown
Authorizations:
path Parameters
| widgetId required | integer > 0 |
query Parameters
| startDate | string |
| endDate | string |
Responses
Response samples
- 200
- 401
- 429
{- "npsScore": 0,
- "totalResponses": 0,
- "promoters": {
- "count": 0,
- "percentage": 0
}, - "passives": {
- "count": 0,
- "percentage": 0
}, - "detractors": {
- "count": 0,
- "percentage": 0
}
}List NPS responses
Authorizations:
path Parameters
| widgetId required | integer > 0 |
query Parameters
| limit | integer ( 0 .. 100 ] Default: 50 |
| offset | integer or null >= 0 Default: 0 |
| startDate | string |
| endDate | string |
Responses
Response samples
- 200
- 401
- 429
{- "data": [
- {
- "id": 0,
- "rating": "string",
- "comment": "string",
- "email": "string",
- "name": "string",
- "userId": "string",
- "url": "string",
- "language": "string",
- "createdAt": "2019-08-24T14:15:22Z"
}
], - "pagination": {
- "total": 0,
- "limit": 0,
- "offset": 0
}
}Response samples
- 200
- 401
- 404
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Update an NPS widget
Only the fields you send are changed. settings are merged over stored settings; targeting replaces wholesale. Live widgets can be edited, but an update that would leave one unpublishable (no targeting) returns 400; set active: false first to take it down.
Authorizations:
path Parameters
| widgetId required | integer > 0 |
Request Body schema: application/json
| name | string non-empty |
object Merged over existing settings | |
object or object or object | |
| themeId | integer or null > 0 |
| folderId | integer or null > 0 |
| active | boolean |
Responses
Request samples
- Payload
{- "name": "string",
- "settings": {
- "selectedScale": "ten",
- "title": "string",
- "scaleLabelLeastLikely": "string",
- "scaleLabelMostLikely": "string",
- "collectEmail": true,
- "emailRequired": true,
- "shouldValidateEmail": true,
- "emailText": "string",
- "emailMissingErrorText": "string",
- "emailInvalidErrorText": "string",
- "collectFeedback": true,
- "msgText": "string",
- "msgPlaceholderText": "string",
- "submitBtnText": "string",
- "successText": "string",
- "closeBtnText": "string",
- "displayType": "bottombar",
- "displayTrigger": "element_click",
- "displayTriggerTime": 0,
- "displayTriggerSelector": "string",
- "display": "once_a_month",
- "dismissOn": "cross_click",
- "themeColor": "string",
- "textColor": "string",
- "slackWebhookUrl": "string",
- "discordWebhookUrl": "string",
- "zapierWebhookUrl": "string",
- "customWebhookUrl": "string",
- "notificationEmails": "string"
}, - "targeting": {
- "showOnAllPages": true
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 200
- 400
- 401
- 404
- 409
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Widget stats by entity type
Authorizations:
path Parameters
| entityType required | string Enum: "tour" "checklist" "smartTip" "announcement" "changelog" "npsWidget" "roadmap" |
query Parameters
| startDate | string |
| endDate | string |
| entityId | integer > 0 |
Responses
Response samples
- 200
- 400
- 401
- 429
{- "entityType": "string",
- "period": {
- "start": "string",
- "end": "string"
}, - "widgets": [
- {
- "entityId": 0,
- "name": "string",
- "archivedAt": "2019-08-24T14:15:22Z",
- "events": {
- "property1": {
- "count": 0,
- "uniqueUsers": 0
}, - "property2": {
- "count": 0,
- "uniqueUsers": 0
}
}, - "totalEvents": 0,
- "totalUniqueUsers": 0
}
]
}Company-wide stats summary
Authorizations:
query Parameters
| startDate | string |
| endDate | string |
Responses
Response samples
- 200
- 400
- 401
- 429
{- "period": {
- "start": "string",
- "end": "string",
- "days": 0,
- "granularity": "string"
}, - "previousPeriod": {
- "start": "string",
- "end": "string"
}, - "features": [
- {
- "type": "string",
- "label": "string",
- "primary": {
- "label": "string",
- "count": 0,
- "previousCount": 0,
- "changePct": 0
}, - "secondary": {
- "label": "string",
- "value": 0,
- "previousValue": 0,
- "changePp": 0
}, - "events": {
- "property1": {
- "count": 0,
- "uniqueUsers": 0,
- "previousCount": 0,
- "previousUniqueUsers": 0
}, - "property2": {
- "count": 0,
- "uniqueUsers": 0,
- "previousCount": 0,
- "previousUniqueUsers": 0
}
}, - "trend": [
- {
- "period": "string",
- "count": 0
}
]
}
]
}Get an identified user
Returns the user's attributes and timestamps. metadata can be null for users identified client-side without attributes.
Authorizations:
path Parameters
| userId required | string non-empty Example: user_123 The user's external ID — the same value you pass to window.Produktly.identifyUser(). URL-encode it. |
Responses
Response samples
- 200
- 400
- 401
- 404
- 429
{- "userId": "user_123",
- "metadata": {
- "plan": "pro",
- "seats": 12
}, - "createdAt": "2026-07-01T09:00:00.000Z",
- "updatedAt": "2026-07-15T09:00:00.000Z"
}Create or replace an identified user
Server-side equivalent of identifyUser() with replace semantics: creates the user if it doesn't exist (201), otherwise replaces its entire metadata object (200). Prefer PATCH for scheduled syncs so you don't clobber attributes set client-side.
Authorizations:
path Parameters
| userId required | string non-empty Example: user_123 The user's external ID — the same value you pass to window.Produktly.identifyUser(). URL-encode it. |
Request Body schema: application/json
required | object User attributes as a JSON object (nested values allowed, max 32 KB serialized). Replaces the entire stored metadata object. | ||
| |||
Responses
Request samples
- Payload
{- "metadata": {
- "plan": "pro",
- "seats": 12
}
}Response samples
- 200
- 201
- 400
- 401
- 429
{- "userId": "user_123",
- "metadata": {
- "plan": "pro",
- "seats": 12
}, - "createdAt": "2026-07-01T09:00:00.000Z",
- "updatedAt": "2026-07-15T09:00:00.000Z"
}Merge attributes into an identified user
Shallow-merges the given attributes into the user's metadata (RFC 7386 style at the top level): keys overwrite, null removes a key, omitted keys are kept. Creates the user if it doesn't exist (201).
Authorizations:
path Parameters
| userId required | string non-empty Example: user_123 The user's external ID — the same value you pass to window.Produktly.identifyUser(). URL-encode it. |
Request Body schema: application/json
required | object Attributes to merge (shallow, top level): keys overwrite, a key set to null is removed, omitted keys are kept, nested objects replace wholesale. Merged result max 32 KB serialized. | ||
| |||
Responses
Request samples
- Payload
{- "metadata": {
- "plan": "enterprise",
- "trialEndsAt": null
}
}Response samples
- 200
- 201
- 400
- 401
- 429
{- "userId": "user_123",
- "metadata": {
- "plan": "enterprise",
- "seats": 12
}, - "createdAt": "2026-07-01T09:00:00.000Z",
- "updatedAt": "2026-07-15T09:00:00.000Z"
}Delete an identified user
Hard-deletes the identified user. Analytics events are kept (they reference the external ID as a plain string).
Authorizations:
path Parameters
| userId required | string non-empty Example: user_123 The user's external ID — the same value you pass to window.Produktly.identifyUser(). URL-encode it. |
Responses
Response samples
- 400
- 401
- 404
- 429
{- "error": {
- "code": "string",
- "message": "string",
- "details": {
- "property1": null,
- "property2": null
}
}
}Create a product tour
Creates the tour as a draft. Pass active: true to publish immediately — the tour must have at least one step and targeting set, otherwise it stays a draft and the response note explains why. The response includes a dashboardUrl for reviewing the tour in the builder.
Authorizations:
Request Body schema: application/json
| name required | string non-empty |
Array of objects or objects or objects or objects or objects or objects Tour steps in order. A tour with no steps never shows | |
object or object or object Where the tour shows. Omit for an untargeted draft; without targeting the tour can never show | |
object | |
| themeId | integer > 0 |
| folderId | integer > 0 |
| active | boolean Publish immediately. The tour needs at least one step and targeting set; otherwise it is saved as a draft and the response note explains why. |
Responses
Request samples
- Payload
{- "name": "string",
- "steps": [
- {
- "type": "highlight",
- "domQuery": "string",
- "direction": "auto",
- "highlight": true,
- "allowClickEvents": true,
- "dismissOverlaysOnLeave": true,
- "skipIfNotFound": true,
- "expandSelectors": [
- "string"
], - "id": "string",
- "title": "string",
- "content": "string",
- "width": 0,
- "height": 0,
- "nextBtnText": "string",
- "backBtnText": "string",
- "finishBtnText": "string",
- "action": "none",
- "actionText": "string",
- "actionUrl": "string",
- "openActionUrlInNewTab": true,
- "closeTourOnAction": true,
- "actionTourId": 0,
- "actionListeners": [
- {
- "type": "click",
- "domQuery": "string",
- "delay": 0,
- "hideButtons": true
}
], - "showDarkBackground": true
}
], - "targeting": {
- "showOnAllPages": true
}, - "settings": {
- "tourStart": "minified",
- "startDelay": 0,
- "trigger": "once",
- "saveProgress": true,
- "hideBeacon": true,
- "beaconPosition": "bottom-right",
- "multiPageEnabled": true,
- "timeout": 0,
- "allowClosingTour": true,
- "closeOnOutsideClick": true,
- "themeColor": "string",
- "textColor": "string",
- "nextBtnText": "string",
- "backBtnText": "string",
- "finishBtnText": "string",
- "startTourBtnText": "string",
- "continueTourBtnText": "string"
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 201
- 400
- 401
- 403
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "steps": [
- {
- "property1": null,
- "property2": null
}
], - "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Response samples
- 200
- 401
- 404
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "steps": [
- {
- "property1": null,
- "property2": null
}
], - "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Update a product tour
Only the fields you send are changed. steps and targeting replace their stored values wholesale — fetch the tour first and send the full array back, preserving step ids. settings are merged. Live tours can be edited, but an update that would leave a live tour unpublishable (no steps or no targeting) returns 400; set active: false first to take it down.
Authorizations:
path Parameters
| id required | integer > 0 |
Request Body schema: application/json
| name | string non-empty |
Array of objects or objects or objects or objects or objects or objects Replaces ALL steps. Fetch the tour first and send back the full array, preserving step ids | |
object or object or object Replaces the whole targeting config | |
object Merged over existing settings; only provided keys change | |
| themeId | integer or null > 0 |
| folderId | integer or null > 0 |
| active | boolean Set live or back to draft. Publishing requires the widget to be publishable (see the create schema); unpublishing always works. |
Responses
Request samples
- Payload
{- "name": "string",
- "steps": [
- {
- "type": "highlight",
- "domQuery": "string",
- "direction": "auto",
- "highlight": true,
- "allowClickEvents": true,
- "dismissOverlaysOnLeave": true,
- "skipIfNotFound": true,
- "expandSelectors": [
- "string"
], - "id": "string",
- "title": "string",
- "content": "string",
- "width": 0,
- "height": 0,
- "nextBtnText": "string",
- "backBtnText": "string",
- "finishBtnText": "string",
- "action": "none",
- "actionText": "string",
- "actionUrl": "string",
- "openActionUrlInNewTab": true,
- "closeTourOnAction": true,
- "actionTourId": 0,
- "actionListeners": [
- {
- "type": "click",
- "domQuery": "string",
- "delay": 0,
- "hideButtons": true
}
], - "showDarkBackground": true
}
], - "targeting": {
- "showOnAllPages": true
}, - "settings": {
- "tourStart": "minified",
- "startDelay": 0,
- "trigger": "once",
- "saveProgress": true,
- "hideBeacon": true,
- "beaconPosition": "bottom-right",
- "multiPageEnabled": true,
- "timeout": 0,
- "allowClosingTour": true,
- "closeOnOutsideClick": true,
- "themeColor": "string",
- "textColor": "string",
- "nextBtnText": "string",
- "backBtnText": "string",
- "finishBtnText": "string",
- "startTourBtnText": "string",
- "continueTourBtnText": "string"
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 200
- 400
- 401
- 404
- 409
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "steps": [
- {
- "property1": null,
- "property2": null
}
], - "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Create a checklist
Creates the checklist as a draft. Pass active: true to publish immediately — the checklist must have at least one item and targeting set, otherwise it stays a draft and the response note explains why. The response includes a dashboardUrl for reviewing the checklist in the builder.
Authorizations:
Request Body schema: application/json
| name required | string non-empty Shown as the checklist panel header |
Array of objects | |
object or object or object | |
object | |
| themeId | integer > 0 |
| folderId | integer > 0 |
| active | boolean Publish immediately. The checklist needs at least one item and targeting set; otherwise it is saved as a draft and the response note explains why. |
Responses
Request samples
- Payload
{- "name": "string",
- "items": [
- {
- "id": "string",
- "title": "string",
- "content": "string",
- "type": "start-tour",
- "tourId": 0,
- "redirectToUrl": "string",
- "completionType": "page-event",
- "eventType": "click",
- "pageEventElementSelector": "string",
- "tourToCompleteId": 0,
- "allowManualCompletion": true,
- "allowToRetake": true,
- "allowUncheck": true
}
], - "targeting": {
- "showOnAllPages": true
}, - "settings": {
- "displayMode": "floating",
- "containerSelector": "string",
- "checklistStart": "minified",
- "hideBeacon": true,
- "checklistPosition": "bottom-right",
- "beaconPosition": "bottom-right",
- "themeColor": "string",
- "textColor": "string",
- "completedColor": "string",
- "showChecklistBtnText": "string",
- "continueChecklistBtnText": "string",
- "showChecklistAfterFinishBtnText": "string",
- "percentageCompletedText": "string",
- "checklistCompletedText": "string",
- "finishChecklistBtnText": "string",
- "markAsCompletedText": "string",
- "markAsCompletedConfirmText": "string",
- "markAsCompletedCancelText": "string",
- "markAsUncompletedText": "string",
- "markAsUncompletedConfirmText": "string",
- "markAsUncompletedCancelText": "string"
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 201
- 400
- 401
- 403
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "items": [
- {
- "property1": null,
- "property2": null
}
], - "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Response samples
- 200
- 401
- 404
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "items": [
- {
- "property1": null,
- "property2": null
}
], - "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Update a checklist
Only the fields you send are changed. items and targeting replace their stored values wholesale — fetch the checklist first and send the full array back, preserving item ids (completion state is keyed on them). settings are merged. Live checklists can be edited, but an update that would leave one unpublishable (no items or no targeting) returns 400; set active: false first to take it down.
Authorizations:
path Parameters
| id required | integer > 0 |
Request Body schema: application/json
| name | string non-empty |
Array of objects Replaces ALL items. Fetch first and send back the full array, preserving item ids | |
object or object or object | |
object Merged over existing settings | |
| themeId | integer or null > 0 |
| folderId | integer or null > 0 |
| active | boolean Set live or back to draft. Publishing requires the widget to be publishable (see the create schema); unpublishing always works. |
Responses
Request samples
- Payload
{- "name": "string",
- "items": [
- {
- "id": "string",
- "title": "string",
- "content": "string",
- "type": "start-tour",
- "tourId": 0,
- "redirectToUrl": "string",
- "completionType": "page-event",
- "eventType": "click",
- "pageEventElementSelector": "string",
- "tourToCompleteId": 0,
- "allowManualCompletion": true,
- "allowToRetake": true,
- "allowUncheck": true
}
], - "targeting": {
- "showOnAllPages": true
}, - "settings": {
- "displayMode": "floating",
- "containerSelector": "string",
- "checklistStart": "minified",
- "hideBeacon": true,
- "checklistPosition": "bottom-right",
- "beaconPosition": "bottom-right",
- "themeColor": "string",
- "textColor": "string",
- "completedColor": "string",
- "showChecklistBtnText": "string",
- "continueChecklistBtnText": "string",
- "showChecklistAfterFinishBtnText": "string",
- "percentageCompletedText": "string",
- "checklistCompletedText": "string",
- "finishChecklistBtnText": "string",
- "markAsCompletedText": "string",
- "markAsCompletedConfirmText": "string",
- "markAsCompletedCancelText": "string",
- "markAsUncompletedText": "string",
- "markAsUncompletedConfirmText": "string",
- "markAsUncompletedCancelText": "string"
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 200
- 400
- 401
- 404
- 409
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "items": [
- {
- "property1": null,
- "property2": null
}
], - "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Create a smart tip
Creates the tip as a draft. Pass active: true to publish immediately — the tip must have settings.cssSelector and targeting set, otherwise it stays a draft and the response note explains why. The response includes a dashboardUrl for reviewing the tip in the builder.
Authorizations:
Request Body schema: application/json
| name required | string non-empty Internal name |
| title | string Card title (plain text) |
| content | string Card body as HTML |
required | object |
object or object or object Where the tip shows. Without targeting it can never show | |
| themeId | integer > 0 |
| folderId | integer > 0 |
| active | boolean Publish immediately. Needs settings.cssSelector and targeting; otherwise saved as a draft and the response note explains why |
Responses
Request samples
- Payload
{- "name": "string",
- "title": "string",
- "content": "string",
- "settings": {
- "cssSelector": "string",
- "displayOn": "click",
- "hideWhen": "click",
- "displayBeacon": true,
- "beaconStyle": "pulsating_point",
- "offsetX": 0,
- "offsetY": 0,
- "orientElement": "element",
- "cardOrientation": "auto",
- "hideWhenOffPage": true,
- "width": 0,
- "height": 0,
- "overrideZIndex": 0,
- "action": "none",
- "actionText": "string",
- "actionUrl": "string",
- "openActionUrlInNewTab": true,
- "tourId": 0,
- "secondaryAction": "none",
- "secondaryActionText": "string",
- "secondaryActionUrl": "string",
- "secondaryOpenActionUrlInNewTab": true,
- "secondaryTourId": 0,
- "actionPosition": "left",
- "themeColor": "string",
- "textColor": "string",
- "display": "once",
- "displayTimes": 0
}, - "targeting": {
- "showOnAllPages": true
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 201
- 400
- 401
- 403
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "title": "string",
- "content": "string",
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Response samples
- 200
- 401
- 404
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "title": "string",
- "content": "string",
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Update a smart tip
Only the fields you send are changed. settings are merged over stored settings; targeting replaces wholesale. Live tips can be edited, but an update that would leave one unpublishable (no cssSelector or no targeting) returns 400; set active: false first to take it down.
Authorizations:
path Parameters
| id required | integer > 0 |
Request Body schema: application/json
| name | string non-empty |
| title | string |
| content | string |
object Merged over existing settings | |
object or object or object Replaces the whole targeting config | |
| themeId | integer or null > 0 |
| folderId | integer or null > 0 |
| active | boolean Set live or back to draft |
Responses
Request samples
- Payload
{- "name": "string",
- "title": "string",
- "content": "string",
- "settings": {
- "cssSelector": "string",
- "displayOn": "click",
- "hideWhen": "click",
- "displayBeacon": true,
- "beaconStyle": "pulsating_point",
- "offsetX": 0,
- "offsetY": 0,
- "orientElement": "element",
- "cardOrientation": "auto",
- "hideWhenOffPage": true,
- "width": 0,
- "height": 0,
- "overrideZIndex": 0,
- "action": "none",
- "actionText": "string",
- "actionUrl": "string",
- "openActionUrlInNewTab": true,
- "tourId": 0,
- "secondaryAction": "none",
- "secondaryActionText": "string",
- "secondaryActionUrl": "string",
- "secondaryOpenActionUrlInNewTab": true,
- "secondaryTourId": 0,
- "actionPosition": "left",
- "themeColor": "string",
- "textColor": "string",
- "display": "once",
- "displayTimes": 0
}, - "targeting": {
- "showOnAllPages": true
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 200
- 400
- 401
- 404
- 409
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "title": "string",
- "content": "string",
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Create an announcement
Creates the announcement as a draft. Pass active: true to publish immediately — the announcement must have a title and targeting set, plus content for popup and toast types, otherwise it stays a draft and the response note explains why. The response includes a dashboardUrl for reviewing the announcement in the builder.
Authorizations:
Request Body schema: application/json
| name required | string non-empty |
| title | string Headline. The only text shown on topbar types |
| content | string HTML body. Rendered for popup and toast only |
object | |
object or object or object | |
| themeId | integer > 0 |
| folderId | integer > 0 |
| active | boolean Publish immediately. Needs a title and targeting, plus content for popup and toast |
Responses
Request samples
- Payload
{- "name": "string",
- "title": "string",
- "content": "string",
- "settings": {
- "type": "topbar",
- "isSticky": true,
- "pushContentDown": true,
- "toastLocation": "bottomleft",
- "width": 0,
- "height": 0,
- "showCloseButton": true,
- "showActionButton": true,
- "actionText": "string",
- "actionType": "url",
- "actionLink": "string",
- "actionTourId": 0,
- "actionOpenInNewTab": true,
- "showSecondaryActionButton": true,
- "secondaryActionText": "string",
- "secondaryActionType": "url",
- "secondaryActionLink": "string",
- "secondaryActionTourId": 0,
- "secondaryActionOpenInNewTab": true,
- "themeColor": "string",
- "textColor": "string",
- "display": "once",
- "displayTimes": 0
}, - "targeting": {
- "showOnAllPages": true
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 201
- 400
- 401
- 403
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "title": "string",
- "content": "string",
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Response samples
- 200
- 401
- 404
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "title": "string",
- "content": "string",
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Update an announcement
Only the fields you send are changed. settings are merged over stored settings; targeting replaces wholesale. Live announcements can be edited, but an update that would leave one unpublishable (no title, no targeting, or missing content for popup/toast) returns 400; set active: false first to take it down.
Authorizations:
path Parameters
| id required | integer > 0 |
Request Body schema: application/json
| name | string non-empty |
| title | string |
| content | string |
object Merged over existing settings | |
object or object or object | |
| themeId | integer or null > 0 |
| folderId | integer or null > 0 |
| active | boolean |
Responses
Request samples
- Payload
{- "name": "string",
- "title": "string",
- "content": "string",
- "settings": {
- "type": "topbar",
- "isSticky": true,
- "pushContentDown": true,
- "toastLocation": "bottomleft",
- "width": 0,
- "height": 0,
- "showCloseButton": true,
- "showActionButton": true,
- "actionText": "string",
- "actionType": "url",
- "actionLink": "string",
- "actionTourId": 0,
- "actionOpenInNewTab": true,
- "showSecondaryActionButton": true,
- "secondaryActionText": "string",
- "secondaryActionType": "url",
- "secondaryActionLink": "string",
- "secondaryActionTourId": 0,
- "secondaryActionOpenInNewTab": true,
- "themeColor": "string",
- "textColor": "string",
- "display": "once",
- "displayTimes": 0
}, - "targeting": {
- "showOnAllPages": true
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 200
- 400
- 401
- 404
- 409
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "title": "string",
- "content": "string",
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Create a micro survey
Creates the survey as a draft. Pass active: true to publish immediately — the survey must have at least one question and targeting set, otherwise it stays a draft and the response note explains why. The response includes a dashboardUrl for reviewing the survey in the builder.
Authorizations:
Request Body schema: application/json
| name required | string non-empty |
required | object |
object or object or object | |
| themeId | integer > 0 |
| folderId | integer > 0 |
| active | boolean Publish immediately. Needs at least one question and targeting |
Responses
Request samples
- Payload
{- "name": "string",
- "settings": {
- "questions": [
- {
- "question": "string",
- "type": "emoji",
- "options": [
- {
- "name": "string",
- "emoji": "string"
}
], - "multiChoiceOptions": [
- "string"
]
}
], - "display": "always",
- "displayTrigger": "click",
- "cssSelector": "string",
- "displayDelay": 0,
- "autoClose": true,
- "autoCloseAfter": 0,
- "position": "element",
- "positionCssSelector": "string",
- "themeColor": "string",
- "textColor": "string",
- "onCompleteActions": [
- {
- "type": "start_tour",
- "tourId": 0
}
], - "slackWebhookUrl": "string",
- "discordWebhookUrl": "string",
- "zapierWebhookUrl": "string",
- "customWebhookUrl": "string",
- "notificationEmails": "string"
}, - "targeting": {
- "showOnAllPages": true
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 201
- 400
- 401
- 403
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Response samples
- 200
- 401
- 404
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}Update a micro survey
Only the fields you send are changed. settings are merged over stored settings; targeting replaces wholesale. Live surveys can be edited, but an update that would leave one unpublishable (no questions or no targeting) returns 400; set active: false first to take it down.
Authorizations:
path Parameters
| id required | integer > 0 |
Request Body schema: application/json
| name | string non-empty |
object Merged over existing settings | |
object or object or object | |
| themeId | integer or null > 0 |
| folderId | integer or null > 0 |
| active | boolean |
Responses
Request samples
- Payload
{- "name": "string",
- "settings": {
- "questions": [
- {
- "question": "string",
- "type": "emoji",
- "options": [
- {
- "name": "string",
- "emoji": "string"
}
], - "multiChoiceOptions": [
- "string"
]
}
], - "display": "always",
- "displayTrigger": "click",
- "cssSelector": "string",
- "displayDelay": 0,
- "autoClose": true,
- "autoCloseAfter": 0,
- "position": "element",
- "positionCssSelector": "string",
- "themeColor": "string",
- "textColor": "string",
- "onCompleteActions": [
- {
- "type": "start_tour",
- "tourId": 0
}
], - "slackWebhookUrl": "string",
- "discordWebhookUrl": "string",
- "zapierWebhookUrl": "string",
- "customWebhookUrl": "string",
- "notificationEmails": "string"
}, - "targeting": {
- "showOnAllPages": true
}, - "themeId": 0,
- "folderId": 0,
- "active": true
}Response samples
- 200
- 400
- 401
- 404
- 409
- 429
{- "id": 0,
- "name": "string",
- "active": true,
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "folderId": 0,
- "themeId": 0,
- "settings": {
- "property1": null,
- "property2": null
}, - "rules": [
- {
- "property1": null,
- "property2": null
}
], - "ruleLogicalOperator": "string",
- "dashboardUrl": "string",
- "note": "string"
}