Guides
Account management for AI agents
A curated set of Unless REST API endpoints accepts API key authentication, which makes them usable by an AI agent acting on behalf of an account user - no interactive login required. This guide explains how to authenticate, which endpoints are available, and how to chain them to complete common account management tasks.
To use Unless directly from an AI agent like ChatGPT or Claude, give it this URL: https://docs.unless.com/guides/account-management-for-ai-agents.md. That is this page plus the full specification of every endpoint in a single Markdown file, so one fetch is all the agent needs. Then ask it to do the job, for example “publish our help center” or “list the open tasks and tell me what needs attention”.
Instructions for the agent
Section titled “Instructions for the agent”If you are an AI agent working on someone’s Unless account, follow these rules.
Ask the user for the credentials. Before the first call, ask for all four values in one message:
| Ask for | Where the user finds it |
|---|---|
| Personal API key | Dashboard, Account > Settings, the “Personal API key” row. Keys are issued with a personal_ prefix. |
| Their email address | The address they sign in with. The key is tied to that user, so the two have to match. |
| Account ID | Dashboard, Account > Settings, the “Account ID” row at the top of the page. |
| Workspace ID | Dashboard, Workspaces, the actions menu on the workspace row, “Copy workspace ID”. |
Ask for the personal key, never the account-wide one. Both authenticate, but the personal key carries only that one user’s permissions and can be replaced without affecting anyone else. A key without the personal_ prefix is probably the account-wide key, so ask again.
The account and workspace pages need the administrator role, so a user without it has to get these values from an administrator.
Do not go looking for the credentials yourself. Do not search files, .env files, environment variables, git history, shell history or a password manager, and do not reuse an account or workspace ID you happened to see somewhere. Acting on the wrong account is far worse than asking. No endpoint lists the accounts or workspaces a key can reach, so the IDs have to come from the user.
Make the calls yourself. Issue the HTTP requests with whatever tool you have. Do not hand the user a curl command and ask them to paste the output back. If you have no way to reach https://api.unless.com, say so plainly instead of delegating the work.
Keep the key in the request headers. Do not repeat it back to the user, write it to a file, commit it, or include it in a summary.
Stay in the workspace the user named. Every call takes x-website-id, and most reads also want the same value as a websiteId query parameter. If the account has several workspaces and the user did not say which one, ask.
Confirm before anything a visitor will see. Publishing the help center, setting a personalization or an audience live, and accepting content library changes all reach real visitors. Describe what you are about to do and wait for a yes. Reads need no confirmation.
Report what the API actually returned. On failure, give the status code and the response body rather than guessing at the cause, and do not report a change as done until a response confirmed it.
Authentication
Section titled “Authentication”Every request needs four headers:
| Header | Value |
|---|---|
x-api-key | The user’s personal API key from Account > Settings. The account-wide key also authenticates, but prefer the personal one. |
x-user-email | The email address of the user the agent acts on behalf of. |
x-account-id | The account ID, found in the account settings. |
x-website-id | The ID of the workspace (website) to operate on. |
All endpoints are served from https://api.unless.com and prefixed with /api/v1. Requests and responses are JSON.
Example:
curl https://api.unless.com/api/v1/tasks?websiteId=<website-id> \ -H "x-api-key: <api-key>" \ -H "x-user-email: <user-email>" \ -H "x-account-id: <account-id>" \ -H "x-website-id: <website-id>"Notes:
- Send either an API key or a JWT, never both. Requests carrying both an
Authorizationheader and anx-api-keyheader are rejected. - The permissions of the resolved user apply. A key only reaches the websites its user has access to.
Endpoint catalog
Section titled “Endpoint catalog”This catalog tells an agent which endpoint fits which job. Every endpoint links to its entry in the REST API reference, where the headers, parameters, request body and responses are documented in full.
Investigate an account
Section titled “Investigate an account”| Endpoint | Use it to |
|---|---|
GET /api/v1/tasks | List the account’s work items: knowledge suggestions, detected gaps, flagged conversations, sales signals. |
GET /api/v1/ai/configuration | Inspect how the AI is configured: main language, product name, custom rules. |
GET /api/v1/ai/training | See which knowledge sources the AI is trained on and how ingestions went. |
GET /api/v1/audiences | List the visitor segments defined for the account. |
GET /api/v1/personalizations | List the personalizations (experiences) running on a website. |
GET /api/v1/help-center/publish-status | Check whether the help center is published, publishing or failed. |
GET /api/v1/ai/quality/control | List the control questions the AI is scored against. |
GET /api/v1/ai/quality/control/report | List quality control reports, or fetch one report’s per-question results with ?reportId=. |
GET /api/v1/ai/actions | List the procedures the AI can run: what each one does, whether it is enabled, and which training documents trigger it. |
GET /api/v1/components/custom-components | List the custom components available to the account. |
GET /api/v1/accounts/logs | Read the audit trail of who changed what. Last 90 days, 100 entries. |
Read the numbers
Section titled “Read the numbers”Insights are read-only over the API. Every endpoint takes startTime and endTime as inclusive YYYY-MM-DD days, except the ROI and maturity endpoints, which report over the account’s whole history. Results come from a query cache, so check status before trusting the numbers, and reach for forceRefresh=true only when you know the cache is stale.
| Endpoint | Use it to |
|---|---|
GET /api/v1/insights/pageviews | Pageview totals, split by new versus returning and personalized versus not. |
GET /api/v1/insights/pageviews/per-day | The same split, one entry per day. |
GET /api/v1/insights/outcomes | How many outcomes (conversions) were recorded. |
GET /api/v1/insights/outcomes/per-day | Outcomes per day, optionally for one personalization. |
GET /api/v1/insights/audiences/per-day | How many sessions one audience matched per day. Needs audienceId and domainName. |
GET /api/v1/insights/components/events | Engagement per variation: views, CTA clicks, and the AI chat counters. |
GET /api/v1/insights/components/events/per-day | The same counters, one entry per variation per day. |
GET /api/v1/insights/wiki/events | Content library updates the AI made, and the tokens it spent. |
GET /api/v1/insights/wiki/events/per-day | The same, one entry per day. |
GET /api/v1/insights/roi | What the platform handled, per surface, against the account’s business figures. |
GET /api/v1/insights/ai-maturity | The maturity assessment: score, stage and level summaries, and the features behind each cell. |
Read conversations
Section titled “Read conversations”| Endpoint | Use it to |
|---|---|
GET /api/v1/visitors/conversations | List conversations, or fetch one transcript with ?conversationId=. |
GET /api/v1/ai/conversations/details | Read one conversation’s analysis: sentiment, rating, outcome, tags. |
Work with the content library
Section titled “Work with the content library”The content library (wiki) is the AI’s own editable knowledge base. Changes an agent makes are staged as a suggestion task and only reach the library once accepted.
| Endpoint | Use it to |
|---|---|
GET /api/v1/wiki/tree | Browse the entries under a path. Walk it repeatedly to see the whole tree. |
GET /api/v1/wiki/pages/{path} | Read one page. |
GET /api/v1/wiki/change-log | See what changed, with the before and after content of each edit. |
POST /api/v1/wiki/chat | Instruct the content library agent in natural language. Streams, and stages its changes as a task. |
POST /api/v1/wiki/force-import | Re-import a source URL, ignoring what the importer already knows. |
PATCH /api/v1/wiki/tasks/{taskId}/changes/{changeId} | Accept, deny or retry a single staged change. |
POST /api/v1/wiki/tasks/{taskId}/accept-all | Accept every staged change in a task. |
POST /api/v1/wiki/tasks/{taskId}/deny-all | Discard every staged change in a task. |
POST /api/v1/wiki/tasks/bulk-accept | Accept several tasks at once. |
POST /api/v1/wiki/tasks/bulk-deny | Discard several tasks at once. |
Act on an account
Section titled “Act on an account”| Endpoint | Use it to |
|---|---|
PUT /api/v1/tasks | Change a task’s state (open, done, deleted), assign it, or edit it. |
POST /api/v1/tasks | Create a new task for the account team. |
POST /api/v1/ai/training/data | Ingest a website (or another source) so the AI learns its content. |
POST /api/v1/help-center/publish | Publish the help center so content changes go live. |
POST /api/v1/audiences | Create a new audience, or update an existing one. |
POST /api/v1/ai/quality/control | Add or update a control question and its expected answer. |
POST /api/v1/ai/quality/control/report | Start a quality control run that scores the AI against every control question. Returns the reportId to poll. |
POST /api/v1/ai/actions | Create a new procedure, or update an existing one. |
POST /api/v1/ai/configuration | Change how the AI behaves: language, product name, custom rules, PII filtering. |
POST /api/v1/personalizations | Create a personalization, or update an existing one. |
POST /api/v1/help-center/categories | Create or update a help center category. |
POST /api/v1/help-center/faqs | Create or update a help center FAQ. You supply the question; the AI writes the answer. |
POST /api/v1/help-center/faqs/retry | Regenerate an FAQ’s answer, for instance after the training data changed. |
POST /api/v1/help-center/categories/reorder | Set the sort position of several categories. |
POST /api/v1/help-center/faqs/reorder | Set the sort position of several FAQs. |
POST /api/v1/wiki/change-log/{id}/restore | Roll a content library page back to one side of a logged change. |
Deleting is deliberately not part of the API key surface. Removing a procedure, an audience, a control question, a training source, an FAQ, a category, a personalization, a custom component or a content library page is dashboard-only. So is anything that changes what the insights measure: the ROI business figures, granting a maturity certificate and skipping a maturity task.
Ask the AI
Section titled “Ask the AI”| Endpoint | Use it to |
|---|---|
POST /api/v1/ai/query | Ask the account’s trained AI a question and get an answer in realtime. |
Chaining endpoints
Section titled “Chaining endpoints”Most real tasks combine a read to establish context with one or more writes, followed by a read to verify the result.
Work through open tasks
Section titled “Work through open tasks”GET /api/v1/tasks?websiteId=...&state=opento list what needs attention.- Investigate a task using the read endpoints, for example
GET /api/v1/ai/trainingfor a knowledge gap task. PUT /api/v1/taskswith{ "taskId": ..., "websiteId": ..., "state": "done" }once handled.
Ingest a website
Section titled “Ingest a website”GET /api/v1/ai/trainingto check whether the site is already a source.POST /api/v1/ai/training/datawith{ "trainingData": { "accountId": ..., "websiteId": ..., "type": "url", "method": "all", "url": "https://www.example.com", "isPublic": true } }.- Poll
GET /api/v1/ai/trainingand watchindexedUrlsCountandlastScanTimestampto follow progress.
Publish the help center
Section titled “Publish the help center”GET /api/v1/help-center/publish-statusto confirm no publish is running (state.runningJobIdisnull).POST /api/v1/help-center/publishfor a full publish, or with apartialscope for specific FAQs.- Poll
GET /api/v1/help-center/publish-statusuntil the job’s status is terminal.
Create an audience
Section titled “Create an audience”GET /api/v1/audiencesto inspect existing audiences and theirruleformat.POST /api/v1/audienceswith aname, arule(JSON string) and astate.GET /api/v1/audiences?ruleId=...to verify the result.
Create or update a procedure
Section titled “Create or update a procedure”A procedure is something the AI can do beyond answering: call an API, collect information, or escalate to a human. POST /api/v1/ai/actions takes the whole procedure rather than a patch, so an update starts by reading the current one.
GET /api/v1/ai/actionsto list the procedures and copy theactionIdof the one to change.- Decide on the
actionId. To update, use the existing one. To create, generate a UUID yourself rather than letting the API generate one, becauseargumentshas to carry the same value and you cannot know an API-generated ID until the write has already happened. POST /api/v1/ai/actionswith the full procedure, includingactionIdandarguments.accountIdandwebsiteIdcome from the auth headers and overwrite whatever the body says.GET /api/v1/ai/actionsagain to read back what was stored. The write answers{ "actionId": ..., "message": "AI action upserted" }.
The write endpoint stores whatever you send without validating it, so a procedure that is missing a field is created successfully and then fails the first time the AI tries to run it. Check the readback rather than trusting the 200.
Things that are easy to get wrong:
argumentsis required and easy to miss. It holds the command’s input, and forcommand: "api"it must be the procedure’s ownactionId- the AI dispatches the procedure as/api <arguments>, so an empty or wrong value means nothing gets called. Formcpit is the MCP tool ID, and fordirect-answerthe answer text.- Variable
typeaccepts onlystring,multiline,email,regexandselect.number,boolean,dateandphonelook plausible but are not implemented, and a storednumbermakesGET /api/v1/ai/actionsanswer502for every procedure in the workspace until you correct it. - Send every field you want to keep. Anything left out of the body is not preserved.
- Set
smartActionexplicitly:truelets the AI fill the variables from the conversation,falseasks the visitor for each one. bodyis a string containing JSON, not a nested object.- Credentials go in
headersas a{{placeholder}}resolved from the visitor’s secure profile. A value declared invariablesends up in the chat data, which is stored with the conversation and sent to the model. - Deleting a procedure is not available with an API key, only from the dashboard.
A minimal api procedure, with the same UUID in both places:
{ "actionId": "3f6c1b52-9a4e-4c8d-9f1a-2b7d5e0c4a13", "arguments": "3f6c1b52-9a4e-4c8d-9f1a-2b7d5e0c4a13", "name": "Look up order status", "description": "Retrieves the status of an order from the shop backend.", "command": "api", "enabled": true, "smartAction": true, "method": "GET", "endpoint": "https://api.example.com/orders/{{orderId}}", "body": "{}", "bodyType": "json", "variables": [{ "name": "orderId", "description": "The order number the visitor is asking about", "type": "string" }]}Add a help center FAQ
Section titled “Add a help center FAQ”POST /api/v1/help-center/categoriesto create the category, or read an existingcategoryIdfrom the published help center.POST /api/v1/help-center/faqswith{ "faq": { "question": ..., "categoryId": ..., "websiteId": ... } }. You supply the question only - the FAQ is stored withanswerStatus: "pending"and the AI writes the answer from the training data.- Poll the FAQ until
answerStatusleavespending. UsePOST /api/v1/help-center/faqs/retryif the answer needs rewriting after a training change. POST /api/v1/help-center/publishand pollGET /api/v1/help-center/publish-status. Nothing reaches visitors until that publish completes.
Note that faq.websiteId and category.websiteId must be set in the body as well as in the x-website-id header, and that the URL slug is derived from the question or name and then kept stable across later edits.
Edit the content library through the agent
Section titled “Edit the content library through the agent”GET /api/v1/wiki/treeandGET /api/v1/wiki/pages/{path}to see what is already there.POST /api/v1/wiki/chatwith{ "instruction": ... }. The response is a stream of plain text, not JSON: the agent’s narration arrives incrementally, and the last line is the marker__WIKI_CHAT_RESULT__followed by a JSON object withtaskId,changesStagedandagentSummary. Split on the marker.- Review the staged changes on the task, then
PATCH /api/v1/wiki/tasks/{taskId}/changes/{changeId}with{ "action": "accept" }per change, orPOST /api/v1/wiki/tasks/{taskId}/accept-allfor the lot. GET /api/v1/wiki/change-logto confirm what was written. If a change was wrong,POST /api/v1/wiki/change-log/{id}/restorewith{ "target": "before" }undoes it.
The agent never writes directly. A changesStaged of zero means it decided nothing needed changing, not that it failed.
Score the AI with a quality control report
Section titled “Score the AI with a quality control report”Generating a report costs one AI call per control question, so it is only available on the Enterprise, Flex, Fixed and Plus plans. On any other plan POST /api/v1/ai/quality/control/report answers 403.
GET /api/v1/ai/quality/controlto see the control questions. A report scores every one of them, so add what is missing withPOST /api/v1/ai/quality/controlfirst.POST /api/v1/ai/quality/control/reportto start the run. It answers400when there are no control questions yet, and otherwise returns thereportId.- Poll
GET /api/v1/ai/quality/control/report. The new report appears in the list once its first question has been scored, withprocessedcounting up tototalQuestionsInReport. GET /api/v1/ai/quality/control/report?reportId=...for the per-question results onceprocessedequalstotalQuestionsInReport.