Manage widgets with the API
List, add, change, rotate and delete Wicket widgets, and read their numbers, with the Client API.
Use these routes to manage your Wicket widgets from a script: add a widget when you deploy a site, keep its hostnames current, rotate its secret key, and read how it does. Every route is under https://api.coritan.com/api/v1/client/wicket.
Before you begin
Section titled Before you begin- You need an access token or an account API key (Authentication). The examples send it as
$CORITAN_TOKEN. - Reading needs any role on the account. Adding, changing and deleting widgets and rotating keys need the account owner, or a team member with the Admin or Technical role (Team members). An API key with only Read permissions can read and cannot change anything (Authentication).
- A widget that belongs to another account answers
404, the same as one that does not exist. - The account may make 60 changes in 10 minutes, counting every add, change, delete and rotation. The next one answers
429with aRetry-Afterheader.
List your widgets
Section titled List your widgetscurl "https://api.coritan.com/api/v1/client/wicket/widgets?status=attention&sort=-challenges" \
-H "Authorization: Bearer $CORITAN_TOKEN"
All four query words are optional.
q- Only widgets whose name, site key or one of whose hostnames contains this text, ignoring case.
statusall(the default),active,disabled(our team turned it off) orattention(the widget needs attention).sortname,created,challengesorsolve_rate, each with a leading-for descending. The default is-created, newest first.challengesandsolve_rateuse the last 24 hours, and a widget with no challenges has no solve rate and comes last.page,per_page- The page, starting at 1, and how many widgets it holds. The default is 24 and the most is 100.
A status or sort word that is not in the lists answers 422, and the message names the words that are.
{
"items": [
{
"id": 12,
"name": "Sign-up form",
"site_key": "wk_6fJ2kQ9xT1mZ8bP4rL0aVc",
"mode": "managed",
"status": "active",
"branded": true,
"hostnames": ["example.com", "app.example.com"],
"hostname_count": 2,
"created_at": "2026-10-17T15:14:30Z",
"last_challenge_at": "2026-10-17T16:02:00Z",
"stats_24h": {"issued": 1204, "passed": 1180, "failed": 9, "solve_rate": 0.98},
"attention": ["not_verifying"],
"disabled_reason": null
}
],
"total": 1,
"page": 1,
"per_page": 24,
"limit": {"used": 1, "limit": 25},
"stats": {
"issued_24h": 1204,
"passed_24h": 1180,
"solve_rate_24h": 0.98,
"attention": [{"widget_id": 12, "name": "Sign-up form", "reason": "not_verifying", "disabled_reason": null}]
}
}
| Field | Meaning |
|---|---|
hostnames, hostname_count |
The first three hostnames, and how many the widget lists. |
stats_24h |
Challenges started, passed and failed in the last 24 hours. solve_rate is passed divided by issued, or null when nothing was issued. |
attention |
Why the widget needs attention, if it does: disabled, not_verifying or hostname_refused (Analytics and what needs attention). |
disabled_reason |
Why our team turned the widget off, for a widget whose status is disabled, and null for any other. |
total |
How many widgets match q and status, over every page. |
limit |
How many widgets the account has and how many its plan allows. -1 is unlimited. |
stats |
Totals for the whole account, not only this page. attention has one entry for each reason of each widget that needs attention. Each entry has widget_id, name, reason and disabled_reason, which is the reason our team gave when reason is disabled and null for any other reason. |
Add a widget
Section titled Add a widgetcurl -X POST https://api.coritan.com/api/v1/client/wicket/widgets \
-H "Authorization: Bearer $CORITAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Sign-up form", "hostnames": ["example.com", "app.example.com"], "mode": "managed"}'
name is 1 to 100 characters, and hostnames lists at least one. mode is managed (the default), non_interactive or invisible. You can also send appearance, with any of theme (auto, light or dark), size (normal, flexible or compact) and language (auto, or a code such as es), and branded: false to leave Wicket's name off the widget.
The answer is 201. It holds the widget and its secret key:
{
"widget": {
"id": 12,
"name": "Sign-up form",
"site_key": "wk_6fJ2kQ9xT1mZ8bP4rL0aVc",
"mode": "managed",
"status": "active",
"branded": true,
"hostnames": ["example.com", "app.example.com"],
"hostname_count": 2,
"created_at": "2026-10-17T15:14:30Z",
"last_challenge_at": null,
"stats_24h": {"issued": 0, "passed": 0, "failed": 0, "solve_rate": null},
"attention": [],
"appearance": {"theme": "auto", "size": "normal", "language": "auto"},
"secret_prefix": "wks_Qm4x7K",
"secret_rotated_at": null,
"previous_secret_expires_at": null,
"disabled_reason": null,
"limits": {"hostnames": 10},
"embed": {
"script_url": "https://wicket.coritan.com/v1/api.js",
"siteverify_url": "https://api.coritan.com/api/v1/wicket/siteverify"
}
},
"secret": "wks_Qm4x7KpR2dVbT9sLhW1cYe6NzA0uJfG3oXiBk5aMvH"
}
Important
The secret is shown once. Store it in your server's settings now. No route returns it again, and secret_prefix is only its first ten characters.
embed holds the two addresses you need: script_url is the script your page loads, and siteverify_url is where your server sends the token and the secret key.
Each hostname is checked and written in lower case, so Example.COM. is stored as example.com, and a repeated hostname is stored once. A hostname Wicket refuses answers 422 with the hostname and the reason:
{"detail": "\"exa mple.com\" is not a hostname: a hostname has no spaces"}
Read one widget
Section titled Read one widgetcurl https://api.coritan.com/api/v1/client/wicket/widgets/12 \
-H "Authorization: Bearer $CORITAN_TOKEN"
The answer has the fields of the widget above, with every hostname in hostnames. limits.hostnames is how many hostnames your plan lets one widget list. While a rotation's grace period is running, previous_secret_expires_at says when the old secret key stops working. For a widget our team turned off, status is disabled and disabled_reason says why. The list and the summary give the same disabled_reason, so you can show it without reading each widget.
Change a widget
Section titled Change a widgetSend only what changes. hostnames is the complete new list, so it replaces the current one, and a setting you leave out of appearance keeps its value.
curl -X PATCH https://api.coritan.com/api/v1/client/wicket/widgets/12 \
-H "Authorization: Bearer $CORITAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"hostnames": ["example.com", "app.example.com", "shop.example.com"], "appearance": {"theme": "dark"}}'
The answer is the widget as in Read one widget. A request that sends nothing to change answers 422. The plan's hostname limit counts the hostnames the widget keeps and the ones the request adds, so swapping one hostname for another at the limit works, and adding one more does not.
Rotate the secret key
Section titled Rotate the secret keyRotate a secret key when it may have leaked, or when you lose it. grace says what happens to the old key:
2h- The old secret key keeps working for 2 hours, so you can change your server without a gap. This is the default.
none- The old secret key stops working at once.
curl -X POST https://api.coritan.com/api/v1/client/wicket/widgets/12/secret \
-H "Authorization: Bearer $CORITAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"grace": "2h"}'
The answer has the same shape as adding a widget: the widget, with secret_rotated_at and, for 2h, previous_secret_expires_at, and the new secret, shown once. A widget remembers one old secret key, so rotating again during a grace period ends the earlier key at once. We email the account owner each time a secret key is rotated.
Delete a widget
Section titled Delete a widgetcurl -X DELETE https://api.coritan.com/api/v1/client/wicket/widgets/12 \
-H "Authorization: Bearer $CORITAN_TOKEN"
{"status": "deleted"}
Warning
The widget's site key and secret key stop working at once, so a form that uses the widget stops passing its check. A deleted widget cannot be brought back.
Read a widget's numbers
Section titled Read a widget's numberscurl "https://api.coritan.com/api/v1/client/wicket/widgets/12/analytics?range=7d" \
-H "Authorization: Bearer $CORITAN_TOKEN"
range is 24h (the default), 7d, 30d, 90d or 365d. Any other word answers 422, and the message names the five allowed ones, such as range must be one of: 24h, 7d, 30d, 90d, 365d, not '1y'.
{
"range": "7d",
"from": "2026-10-11T16:00:00Z",
"to": "2026-10-17T16:21:09Z",
"granularity": "hour",
"allowed_days": 7,
"clamped": false,
"upgrade": null,
"series": [
{"t": "2026-10-17T15:00:00Z", "issued": 52, "passed": 50, "passed_interactive": 3, "failed": 1, "interactive_shown": 4}
],
"totals": {
"issued": 1204,
"passed": 1180,
"passed_interactive": 40,
"failed": 9,
"interactive_shown": 51,
"unsolved": 15,
"solve_rate": 0.98,
"verify": {"valid": 1150, "invalid": 4, "expired": 12, "duplicate": 2}
},
"top": {
"hostname": [{"value": "example.com", "issued": 900, "passed": 890, "failed": 3}],
"action": [],
"country": [],
"browser": [],
"os": []
}
}
| Field | Meaning |
|---|---|
series |
One point for each hour (granularity is hour, for 24h and 7d) or each day (day, for 30d, 90d and 365d), with a point of zeros for a period with no challenges. t is the start of the period. |
totals |
The sums over the series. unsolved is issued minus passed minus failed: challenges nobody finished. passed_interactive counts passes that needed a tick. |
totals.verify |
What your server's verify calls found: tokens that were valid, invalid, expired or used twice (duplicate). A verify of zeros while passed is high means your server is not verifying tokens. |
top |
The ten values with the most challenges for each of hostname, action, country, browser and os, over the same span. |
When the range is longer than your plan reads back, the answer is still 200. It covers the days your plan allows, clamped is true, allowed_days is that number of days, and upgrade.plan names the plan that allows the range you asked for:
{"range": "90d", "allowed_days": 7, "clamped": true, "upgrade": {"plan": "plan-business"}}
The plan is the smallest one above yours that allows the range. 365d on any plan but Enterprise names plan-enterprise, and Enterprise reads every range in full.
Read the summary
Section titled Read the summarycurl https://api.coritan.com/api/v1/client/wicket/summary \
-H "Authorization: Bearer $CORITAN_TOKEN"
{
"widgets": 3,
"limit": 25,
"issued_24h": 1580,
"passed_24h": 1522,
"solve_rate_24h": 0.9633,
"attention": [{"widget_id": 12, "name": "Sign-up form", "reason": "not_verifying", "disabled_reason": null}]
}
widgets is how many widgets the account has and limit how many its plan allows (-1 is unlimited). The counts cover the last 24 hours and every widget. attention has an entry for each widget that needs attention and each reason, in the same shape as in the list: widget_id, name, reason and disabled_reason, which is set only when reason is disabled.
Result
Section titled ResultThe widget exists, lists the hostnames you gave it, and answers challenges from pages on those hostnames. Your server verifies the tokens at embed.siteverify_url with the secret key.
Troubleshooting
Section titled Troubleshooting402with"code": "entitlement_exceeded"- The request would take the account past a limit of its plan.
detail.entitlementiswicket_widgets(widgets on the account),wicket_hostnames(hostnames on one widget) orwicket_unbranded(a widget without Wicket's name).detail.upgrade.planis the plan that raises it. No add-on does, soupgrade.addonisnull. 403with"code": "role_refused"- You acted for another account as a team member whose role cannot make changes. Only the Admin and Technical roles can.
404- The widget does not exist or belongs to another account. Check the
id, and theX-Coritan-Accountheader if you act for another account. 422- A value was refused.
detailis a sentence that names the value and the reason, such as a hostname with a port or anappearancesetting that does not exist. 429- The account made more than 60 changes in 10 minutes. Wait for the number of seconds in the
Retry-Afterheader.
Related
Section titled RelatedNext steps
Section titled Next steps- Verify a token on your server with the secret key the call returned.
- Add Wicket to your site to put the widget's
embed.script_urlon a page. - Read a widget's analytics in the dashboard.
API operations on this page
| Method | Path | What it does |
|---|---|---|
GET | /api/v1/client/wicket/widgets | List your widgets, with how each did in the last 24 hours and why any needs a look |
POST | /api/v1/client/wicket/widgets | Add a widget |
GET | /api/v1/client/wicket/widgets/{widget_id} | Get widget |
PATCH | /api/v1/client/wicket/widgets/{widget_id} | Change a widget's name, hostnames, mode, appearance or branding |
DELETE | /api/v1/client/wicket/widgets/{widget_id} | Delete a widget |
POST | /api/v1/client/wicket/widgets/{widget_id}/secret | Make a new secret key for a widget |
GET | /api/v1/client/wicket/widgets/{widget_id}/analytics | Widget analytics |
GET | /api/v1/client/wicket/summary | Widgets summary |