Website Buddy API v1
Make, edit and publish your clients' websites from your own systems — or let an AI assistant do it through MCP. The API runs on the same engines as the console: the same templates, the same editor checks, the same bill.
Your first request
Make a key in your account under Settings → API keys. Keep it on your server as WEBSITEBUDDY_API_KEY, then:
curl "https://websites.workflowcorp.com/api/v1/account" \
-H "Authorization: Bearer $WEBSITEBUDDY_API_KEY"Keys start wb_live_, are shown once and stored only as a SHA-256 digest. Send one as Authorization: Bearer or x-api-key — never in a URL. Browser cookies never authenticate API calls.
Drafts, publishing and money
- Every page and section write changes the site’s draft, through the same checks as the editor. Visitors see nothing until Publish a site makes a new version.
- Publishing runs the editor’s sample check: while made-up sample reviews, numbers, a logo strip, team members or the template’s (555) / River City / .test details would go live, it answers
409 sample_contentwith each part listed inerror.items. Replace or hide them, or sendpublish_sample_anyway: trueonce a person has agreed. - Create a siteadds the package’s wholesale setup fee to your bill (and the monthly fee once it’s live) — exactly like the console. Send the fee you quoted and it refuses if the price moved.
- Money is integer cents. Wholesale and retail prices appear only when the key’s creator may see prices; for anyone else
pricesis null. - Blog posts, redirects, domains, form messages, client records and logins are live data — no publish needed. A published post, a redirect or a verified domain reaches visitors at once (redirects within a minute).
Scopes
A key does only what it was given, and never more than the person who made it may do — checked again on every request.
| Scope | Lets a key | Needs the creator to have |
|---|---|---|
| templates:read | Read templates, packages and section types | Create sites or Edit sites |
| sites:read | Read sites, pages and sections | Edit sites |
| sites:write | Edit draft pages and sections | Edit sites |
| sites:create | Make new sites (each adds the wholesale fee to your bill) | Create sites |
| sites:publish | Publish sites (put the draft live) | Publish sites |
| blog:read | Read blog posts and topics | Edit sites |
| blog:write | Write, publish and delete blog posts (live at once) | Edit sites |
| events:read | Read the events calendar | Edit sites |
| events:write | Add, change and delete events (live at once) | Edit sites |
| collections:read | Read content collections and their records | Edit sites |
| collections:write | Make collections and add, change and delete records (live at once) | Edit sites |
| media:read | See the picture library | Edit sites |
| media:write | Upload pictures | Edit sites |
| redirects:write | Add and remove redirects (live within a minute) | Edit sites |
| domains:read | See connected domains and their DNS records | Edit sites or Publish sites |
| domains:write | Connect, check and remove domains | Publish sites |
| domains:buy | Search, buy and renew domain names (each adds the registration to your bill) | Create sites |
| forms:read | Read forms and the messages visitors send | Edit sites |
| forms:write | Mark messages read, unread or spam | Edit sites |
| bookings:read | Read booking hours and appointments | Edit sites |
| bookings:write | Set booking hours; confirm, decline, cancel and move appointments (live at once, the visitor is emailed) | Edit sites |
| store:read | Read store products, stock and orders | Edit sites |
| store:write | Add and change products and stock (live at once); move orders along | Edit sites |
| donations:read | Read gifts made through a site's Donate section | Edit sites |
| donations:write | Stop monthly gifts and send the yearly giving summaries (donors are emailed) | Edit sites |
| members:read | Read a site's members and members-only pages | Edit sites |
| members:write | Approve, add and remove site members; mark pages members only (live at once) | Edit sites |
| clients:read | Read clients | Manage clients |
| clients:write | Add and edit clients; invite and remove client logins | Manage clients |
| clients:sign-in | Make one-time sign-in links that open a client login's editor with no password (for your own dashboard) | Manage clients |
| invoices:read | Read client invoices, monthly invoice settings and card autopay | See prices and statements |
| invoices:write | Write, send, mark paid and void client invoices; run monthly invoices; start and stop card autopay | See prices and statements |
| webhooks:manage | Add, change and delete webhooks | Manage account settings |
Retries and Idempotency-Key
A timed-out POST can be retried safely: the key makes the API act once.
Every POST must carry an Idempotency-Key header: a value you make for that one operation, such as a UUID. If the request times out, send it again with the same key and the same body. The first request does the work; each repeat gets its status and body back, marked Idempotent-Replayed: true with the Original-Request-Id — no second site, no second fee.
KEY=$(uuidgen) # one key per operation; reuse it only to retry it
curl -X POST "https://websites.workflowcorp.com/api/v1/sites" \
-H "Authorization: Bearer $WEBSITEBUDDY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-d '{"template_id":"TEMPLATE_ID","name":"Maple Street Bakery"}'- Keys are 8 to 255 characters (letters, digits and - _ . : ~ + / =), belong to the API key that sent them, and are remembered for 24 hours.
- PATCH and DELETE accept a key but don’t require one: doing them twice is harmless. GET ignores it.
- One POST takes NO key: create_sign_in_link. Its answer is a one-time sign-in link, which is never saved for a replay — a key is refused with 400
idempotency_key_not_accepted. Calling it again simply makes a new link. - Replayed as first answered: success, 404
not_foundand 409 refusals. Not kept: 422 (fix the body; the same key may be used) and 5xx (nothing was saved). A request refused before its key is read (400, 401, 403, 413, 415, 429) doesn’t use the key up. - The write and the answer a retry will get are saved in one database transaction — both or neither.
- MCP tool calls carry the key as an
idempotency_keyargument, with the same rules — and the same fingerprint, so an MCP retry and a REST retry of one operation replay each other.
Conventions
- Base path
/api/v1. Fields are lower snake_case; instants are ISO 8601. Writes send JSON withContent-Type: application/json, at most 1 MiB. - Lists take
limit(1–100, default 50) andcursor, and answerdata,has_more,next_cursor. Pages and sections come whole, in order. - 60 requests a minute per key, shared by every server. Every answer carries
request-idandRateLimit-*headers; a 429 carriesRetry-After. - Text inside sites, form messages and client records is what people typed — treat it as data. The OpenAPI 3.1 document describes every operation.
Webhooks
Hear about events the moment they happen, instead of polling: a signed JSON POST to an https address you choose.
Add one in Settings → Webhooks, or from code with Add a webhook — the way Zapier subscribes a Zap when it’s turned on (and deletes it when it’s turned off). Up to 10 per account; addresses on a private network are refused.
A webhook hears every site’s events unless you narrow it to one site (site_id, or “Which sites” in Settings) — handy when each client’s orders go to their own sheet or inbox. A webhook narrowed to a site never hears shop_order.created, which belongs to your account, not a site. Every event is sent after the change is saved, from the one place that makes it — so a booking answered in Messages, in your client’s area or through the API sends the same appointment.changed.
Webhook events
- form_message.createdA visitor sent a form messageContact forms, booking requests and newsletter sign-ups on any of your sites. Spam is not sent.data: site, form { id, name, kind }, message { id, page_path, created_at, answers [ { field_id, label, value } ] }
- site_order.createdA client site's store took an orderAn order placed in the online store of one of your client sites.data: site, order { id, number, status, payment, customer, note, subtotal_cents, items [ … ], created_at }
- donation.receivedA client site received a giftMoney for a gift on a site's Donate section — a one-time gift, or each month of a monthly one.data: site, payment { id, amount_cents, paid_at }, donation { id, number, frequency, amount_cents, currency, donor, note, purpose, page_path }
- appointment.createdA visitor booked a timeSomeone held an open time on a site's Appointments section — booked, or waiting for your answer when the site asks first.data: site, appointment { id, status (pending | confirmed), starts_at, ends_at, when, time_zone, service, customer { name, email, phone }, notes, message_id, created_at }
- appointment.changedA booking was confirmed, declined, cancelled or movedThe business answered or moved a booking (in Messages, the client area or the API), or the customer cancelled or moved it from the link in their emails. The event says which, and who.data: site, change (confirmed | declined | cancelled | moved), changed_by (business | customer), appointment { … as above, decided_at }
- member.requestedA visitor asked to become a memberSomeone asked to join a site's members-only pages — waiting for approval, or let straight in when anyone may join.data: site, member { id, name, email, status (pending | approved), created_at }
- content_request.answeredA client sent the content you asked forYour client answered a content request (phone, hours, logo, photos…). It is on the site's draft, not live.data: site, content_request { id, asked [ … ], answered_at, answered_by { name, email }, answers { phone, email, address, hours, about, services, logo_url, photo_urls }, placed }
- site.changes_sentA client finished changes and sent them to youYour client pressed "Send my changes" (or approved the draft) in their editor. The changes wait on the draft for you to publish.data: site, how (sent | approved), by { id, name, email }, note, draft_updated_at, last_published_at
- site.publishedA site was publishedA new version went live — from the editor, a client with the publish permission, or the API.data: site, version { id, number, note, published_at, by (team | client), published_by { id, name, email } }
- shop_order.createdSomeone ordered a website from your shopA new order on your own shop page (Settings → Your shop).data: order { id, number, business_name, contact, current_website, notes, package, template, retail_setup_cents, retail_monthly_cents, created_at } — no site
- webhook.testSend test (in Settings → Webhooks, or send_test_webhook)data: message
| Event | Sent when | data carries |
|---|---|---|
| form_message.created | A visitor sent a form messageContact forms, booking requests and newsletter sign-ups on any of your sites. Spam is not sent. | site, form { id, name, kind }, message { id, page_path, created_at, answers [ { field_id, label, value } ] } |
| site_order.created | A client site's store took an orderAn order placed in the online store of one of your client sites. | site, order { id, number, status, payment, customer, note, subtotal_cents, items [ … ], created_at } |
| donation.received | A client site received a giftMoney for a gift on a site's Donate section — a one-time gift, or each month of a monthly one. | site, payment { id, amount_cents, paid_at }, donation { id, number, frequency, amount_cents, currency, donor, note, purpose, page_path } |
| appointment.created | A visitor booked a timeSomeone held an open time on a site's Appointments section — booked, or waiting for your answer when the site asks first. | site, appointment { id, status (pending | confirmed), starts_at, ends_at, when, time_zone, service, customer { name, email, phone }, notes, message_id, created_at } |
| appointment.changed | A booking was confirmed, declined, cancelled or movedThe business answered or moved a booking (in Messages, the client area or the API), or the customer cancelled or moved it from the link in their emails. The event says which, and who. | site, change (confirmed | declined | cancelled | moved), changed_by (business | customer), appointment { … as above, decided_at } |
| member.requested | A visitor asked to become a memberSomeone asked to join a site's members-only pages — waiting for approval, or let straight in when anyone may join. | site, member { id, name, email, status (pending | approved), created_at } |
| content_request.answered | A client sent the content you asked forYour client answered a content request (phone, hours, logo, photos…). It is on the site's draft, not live. | site, content_request { id, asked [ … ], answered_at, answered_by { name, email }, answers { phone, email, address, hours, about, services, logo_url, photo_urls }, placed } |
| site.changes_sent | A client finished changes and sent them to youYour client pressed "Send my changes" (or approved the draft) in their editor. The changes wait on the draft for you to publish. | site, how (sent | approved), by { id, name, email }, note, draft_updated_at, last_published_at |
| site.published | A site was publishedA new version went live — from the editor, a client with the publish permission, or the API. | site, version { id, number, note, published_at, by (team | client), published_by { id, name, email } } |
| shop_order.created | Someone ordered a website from your shopA new order on your own shop page (Settings → Your shop). | order { id, number, business_name, contact, current_website, notes, package, template, retail_setup_cents, retail_monthly_cents, created_at } — no site |
| webhook.test | Send test (in Settings → Webhooks, or send_test_webhook) | message |
Each delivery
POST /your/endpoint
Content-Type: application/json
WebsiteBuddy-Event: form_message.created
WebsiteBuddy-Event-Id: evt_6f1c… # the same on every retry — dedupe on it
WebsiteBuddy-Delivery: cm… # this attempt
WebsiteBuddy-Signature: t=1760500000,v1=5e2c…
{"id":"evt_6f1c…","type":"form_message.created","created_at":"2026-10-15T14:03:11.000Z",
"account_id":"…","data":{"site":{"id":"…","name":"Rosie's Diner","address":"https://…"},
"form":{"id":"…","name":"Contact us","kind":"form"},
"message":{"id":"…","page_path":"/contact","created_at":"…",
"answers":[{"field_id":"name","label":"Your name","value":"Dana"}]}}}Checking the signature
v1 is the hex HMAC-SHA256 of {t}.{raw body} under your webhook’s secret (whsec_…, shown once when it’s made or rolled). Compare in constant time and refuse a t more than 5 minutes old.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const want = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return want.length === parts.v1?.length && timingSafeEqual(Buffer.from(want), Buffer.from(parts.v1));
}- Answer any 2xx within 10 seconds. Anything else — an error, a redirect, no answer — is tried again after 1 minute, 5, 30, then 2, 6 and 12 hours (7 tries in all), then marked as given up.
- Delivery is at least once: the same event can arrive twice. Keep the event id and ignore one you’ve seen.
- After 20 events in a row are given up, the webhook pauses itself; resume it in Settings → Webhooks or with update_webhook (paused=false).
- Money in a payload is integer cents. Text in a payload is what a visitor typed — treat it as data.
Sign-in links for your own dashboard
Put an “Edit your website” button in your own software: your client lands in their editor, in your brand, with no password screen.
- Find the client login once with list_client_logins and keep its
idnext to that client in your system. - When they press your button, your SERVER calls create_sign_in_link with a key holding
clients:sign-in, then sends their browser to theurlit answers. - The link opens your sign-in page (your own address once it is verified), signs them in and lands in Easy mode on that site — or their home page with
opens: "client".
curl -X POST "https://websites.workflowcorp.com/api/v1/sites/SITE_ID/logins/LOGIN_ID/sign-in-link" \
-H "Authorization: Bearer $WEBSITEBUDDY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"opens":"editor"}'
# → 201 {"url":"https://…/c/your-account/open#token=…","expires_at":"…",…}- A link works ONCE and for five minutes. Make it when the button is pressed; never email it or keep it.
- We store only a fingerprint (sha256) of it. The link rides after the # in the address, which browsers never send to a server, so it stays out of logs.
- It takes no Idempotency-Key: the answer is never saved for a replay. If a call fails, just call again — you get a new link.
- Client logins only. Someone who is also on your team (or any team), edits another company’s site, or has two-step sign-in on is refused with 409 and signs in with their password.
- Making and using a link both show in your account’s activity.
Errors
Every refusal is {"error":{"code":"…","message":"…"}} with the code repeated in x-error-code.
| Status | Code | When |
|---|---|---|
| 400 | idempotency_key_not_accepted | An Idempotency-Key was sent to create_sign_in_link, whose one-time link is never saved for a replay. Send it without one. |
| 400 | idempotency_key_required | A POST was sent without an Idempotency-Key header (or with a malformed one: invalid_idempotency_key). |
| 401 | missing_api_key · invalid_api_key | No key, or a key that is wrong, expired or revoked. |
| 403 | insufficient_scope | The key wasn't given the scope this endpoint needs. |
| 403 | issuer_access_ended · issuer_permission_changed | The person who made the key left the team, was suspended, or lost the permission behind the scope. |
| 404 | not_found | No such record in this account. Another account's records are always 404, never 403. |
| 409 | refused | Not allowed right now — the builder is off, the package has no room for another page, the home page can't be deleted. The message says why in plain words. |
| 409 | sample_content | publish_site was refused because made-up sample content (sample reviews, numbers, logo strip, team, a (555) number, the template's River City / .test business details) would go live. error.items lists each part with its page and section; replace or hide them, or send publish_sample_anyway: true. |
| 409 | price_changed | create_site was sent an expected fee that no longer matches. Nothing was made. |
| 409 | idempotency_key_in_use | A request with this Idempotency-Key is still running. Retry after Retry-After. |
| 413 | payload_too_large | The body is over 1 MiB. |
| 415 | json_required | Writes need Content-Type: application/json. |
| 422 | invalid_request | An argument failed validation — e.g. a section's content doesn't match its type, or a link isn't safe. |
| 422 | idempotency_key_reused | This Idempotency-Key was already used for a different request. |
| 429 | rate_limited | More than 60 requests in a minute on this key. Wait Retry-After seconds. |
| 500 | internal_error | Our fault. Quote the request-id header when you ask for help. |
Operations
167 operations. Each is also an MCP tool of the same name.
- GET
/List sitessites - GET
/Get a sitesites/ {site_id} - POST
/Create a site from a templatesites - POST
/Publish a sitesites/ {site_id}/ publish - GET
/Check a site before publishingsites/ {site_id}/ check - POST
/Take a site downsites/ {site_id}/ take-down - POST
/Put a taken-down site back upsites/ {site_id}/ put-back-up - POST
/Archive a sitesites/ {site_id}/ archive - POST
/Restore an archived sitesites/ {site_id}/ restore - GET
/Read a client's current websitesite-imports/ preview - POST
/Make a site from a client's current websitesite-imports - GET
/Get a site's import reportsites/ {site_id}/ import - POST
/Restore a site from a backupsites/ {site_id}/ restore-backup - POST
/Make a site from a backupsite-backups - GET
/Get a site's settingssites/ {site_id}/ settings - PATCH
/Change a site's settingssites/ {site_id}/ settings
- GET
/List saved sectionssaved-sections - GET
/Get a saved sectionsaved-sections/ {saved_section_id} - POST
/Save a section for reusesaved-sections - PATCH
/Change a saved sectionsaved-sections/ {saved_section_id} - DELETE
/Remove a saved sectionsaved-sections/ {saved_section_id} - POST
/Place a saved section on a pagesites/ {site_id}/ pages/ {page_id}/ saved-sections
- GET
/List blog postssites/ {site_id}/ posts - GET
/Get a blog postsites/ {site_id}/ posts/ {post_id} - POST
/Write a blog postsites/ {site_id}/ posts - PATCH
/Change a blog postsites/ {site_id}/ posts/ {post_id} - DELETE
/Delete a blog postsites/ {site_id}/ posts/ {post_id} - GET
/List blog commentssites/ {site_id}/ comments - POST
/Approve a blog commentsites/ {site_id}/ comments/ {comment_id}/ approve - DELETE
/Delete a blog commentsites/ {site_id}/ comments/ {comment_id} - GET
/List blog topicssites/ {site_id}/ post-topics
- GET
/List collectionssites/ {site_id}/ collections - GET
/Get a collectionsites/ {site_id}/ collections/ {collection_id} - POST
/Make a collectionsites/ {site_id}/ collections - PATCH
/Change a collectionsites/ {site_id}/ collections/ {collection_id} - DELETE
/Delete a collectionsites/ {site_id}/ collections/ {collection_id} - GET
/List a collection's recordssites/ {site_id}/ collections/ {collection_id}/ records - GET
/Get a recordsites/ {site_id}/ collections/ {collection_id}/ records/ {record_id} - POST
/Add a recordsites/ {site_id}/ collections/ {collection_id}/ records - PATCH
/Change a recordsites/ {site_id}/ collections/ {collection_id}/ records/ {record_id} - DELETE
/Delete a recordsites/ {site_id}/ collections/ {collection_id}/ records/ {record_id}
- GET
/Search for a domain to buydomain-names/ search - POST
/Buy a domain for a sitesites/ {site_id}/ domain-names - GET
/List domains bought through usdomain-names - GET
/Get a domain bought through usdomain-names/ {registration_id} - POST
/Renew a domain nowdomain-names/ {registration_id}/ renew - PATCH
/Switch auto-renew on or offdomain-names/ {registration_id} - POST
/Add a business email addressdomain-names/ {registration_id}/ email-forwards - DELETE
/Remove a business email addressdomain-names/ {registration_id}/ email-forwards/ {box} - POST
/Get the transfer codedomain-names/ {registration_id}/ transfer-code
- GET
/Get booking hourssites/ {site_id}/ booking-hours - PATCH
/Set booking hourssites/ {site_id}/ booking-hours - GET
/List open timessites/ {site_id}/ booking-hours/ open-times - GET
/List appointmentssites/ {site_id}/ appointments - GET
/Get an appointmentsites/ {site_id}/ appointments/ {appointment_id} - POST
/Confirm an appointmentsites/ {site_id}/ appointments/ {appointment_id}/ confirm - POST
/Decline an appointmentsites/ {site_id}/ appointments/ {appointment_id}/ decline - POST
/Cancel an appointmentsites/ {site_id}/ appointments/ {appointment_id}/ cancel - POST
/Move an appointmentsites/ {site_id}/ appointments/ {appointment_id}/ move
- GET
/List a site's productssites/ {site_id}/ products - GET
/Get a productsites/ {site_id}/ products/ {product_id} - POST
/Add a productsites/ {site_id}/ products - PATCH
/Change a productsites/ {site_id}/ products/ {product_id} - PATCH
/Set an option's stocksites/ {site_id}/ products/ {product_id}/ options/ {option_id} - DELETE
/Delete a productsites/ {site_id}/ products/ {product_id} - POST
/Add a download's filesites/ {site_id}/ products/ {product_id}/ download - GET
/List a site's gift cardssites/ {site_id}/ gift-cards - GET
/List a site's orderssites/ {site_id}/ orders - GET
/Get an ordersites/ {site_id}/ orders/ {order_id} - GET
/Get the forgotten-cart remindersites/ {site_id}/ store/ cart-reminders - PATCH
/Change the forgotten-cart remindersites/ {site_id}/ store/ cart-reminders - PATCH
/Move an order alongsites/ {site_id}/ orders/ {order_id} - POST
/Refund a card-paid ordersites/ {site_id}/ orders/ {order_id}/ refund
- GET
/List a site's giftssites/ {site_id}/ donations - GET
/Get a giftsites/ {site_id}/ donations/ {donation_id} - POST
/Stop a monthly giftsites/ {site_id}/ donations/ {donation_id}/ stop - POST
/Refund a gift paymentsites/ {site_id}/ donations/ {donation_id}/ payments/ {payment_id}/ refund - POST
/Send the yearly giving summariessites/ {site_id}/ donations/ summaries
- GET
/List a site's memberssites/ {site_id}/ members - POST
/Add a membersites/ {site_id}/ members - POST
/Approve a membersites/ {site_id}/ members/ {member_id}/ approve - DELETE
/Remove a membersites/ {site_id}/ members/ {member_id} - PATCH
/Change who may joinsites/ {site_id}/ members - PATCH
/Make a page members onlysites/ {site_id}/ pages/ {page_id}/ members-only
- GET
/List a site's client loginssites/ {site_id}/ logins - POST
/Invite a client loginsites/ {site_id}/ logins - POST
/Make a sign-in link for a client loginsites/ {site_id}/ logins/ {login_id}/ sign-in-link - PATCH
/Change what a client login may dosites/ {site_id}/ logins/ {login_id} - DELETE
/Remove a client loginsites/ {site_id}/ logins/ {login_id} - DELETE
/Withdraw an invitationsites/ {site_id}/ invitations/ {invitation_id}
- GET
/List client invoicesinvoices - GET
/Get a client invoiceinvoices/ {invoice_id} - POST
/Write a client invoiceinvoices - POST
/Send a client invoiceinvoices/ {invoice_id}/ send - POST
/Mark an invoice paidinvoices/ {invoice_id}/ mark-paid - POST
/Mark an invoice unpaidinvoices/ {invoice_id}/ mark-unpaid - POST
/Void an invoiceinvoices/ {invoice_id}/ void - DELETE
/Delete a draft invoiceinvoices/ {invoice_id} - POST
/Email an invoice to the clientinvoices/ {invoice_id}/ email - GET
/Get monthly invoice settingsmonthly-invoices - PATCH
/Change monthly invoice settingsmonthly-invoices - POST
/Make this month's invoices nowmonthly-invoices/ run - GET
/List card autopayautopay - POST
/Start card autopay for a sitesites/ {site_id}/ autopay - POST
/Stop card autopayautopay/ {autopay_id}/ stop
- GET
/List a site's content requestssites/ {site_id}/ content-requests - GET
/Get a content requestsites/ {site_id}/ content-requests/ {request_id} - POST
/Ask a client for their contentsites/ {site_id}/ content-requests - POST
/Email a content request's linksites/ {site_id}/ content-requests/ {request_id}/ email - DELETE
/Withdraw a content requestsites/ {site_id}/ content-requests/ {request_id}
- GET
/List webhookswebhooks - POST
/Add a webhookwebhooks - PATCH
/Change a webhookwebhooks/ {webhook_id} - POST
/Roll a webhook's secretwebhooks/ {webhook_id}/ roll-secret - DELETE
/Delete a webhookwebhooks/ {webhook_id} - POST
/Send a test eventwebhooks/ {webhook_id}/ test - GET
/List a webhook's deliverieswebhooks/ {webhook_id}/ deliveries