Skip to content

Commit b04ff56

Browse files
claude[bot]claude
andauthored
docs(App API): bulk provisioning, delete endpoint, limits & site lifecycle (#51)
* docs(app-api): document bulk provisioning, delete endpoint, limits & site lifecycle - Add a Bulk provisioning subsection showing the urls array form (up to 100 sites per /site/add) and reconciling results by URL. - Add a Rate limits & quotas note (max 100 URLs per /site/add and /site/exists; /sites/list pagination vs /sites/list/basic full list) with a reviewer TODO for the unverified request-rate limit. - Document the delete endpoint with the correct verified path and method (DELETE|POST /site/delete/{siteid}) plus a curl example and batch delete. - Fix incorrect /site/{siteid}/delete references in prose. - Document the destructive site lifecycle: no paused state; re-adding a deleted site issues new credentials. - Note what the App API does not manage (trials/subscriptions/billing). - Add a legacy deprecation callout to the Hosting API page. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wfaar2urX8t8vVEPHmguev * docs(app-api): finalize rate-limit note per reviewer answers Replace the reviewer-only HTML-comment TODO about request-rate limits with a published note: the App API does not currently enforce a per-key request-rate limit on authenticated calls, with guidance to batch considerately at scale (e.g. the urls array on /site/add). No number invented. Delete-path and legacy /hosting questions resolved in the PR description; no doc-body changes needed for those. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wfaar2urX8t8vVEPHmguev * docs(app-api): note best-effort plugin removal on site delete, recommend WP-CLI --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent fc04d68 commit b04ff56

3 files changed

Lines changed: 87 additions & 2 deletions

File tree

src/content/docs/API solutions/App API/patchstack-app-api.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ _Patchstack App API is available for the Developer and Enterprise plan users_
1111

1212
Patchstack App API enables users to run all the Patchstack App account actions remotely over an API. It allows you to access protection logs, generate security reports, manage site settings, add new sites, create custom rules, and much more.
1313

14+
:::note[What the App API does not manage]
15+
The App API manages **sites and their security** on your Patchstack account — provisioning, plugin connection, protection, reporting and site settings. It does **not** manage trials, subscriptions, plan changes or billing for your own end customers. If you are reselling Patchstack (for example through WHMCS), run trials, subscriptions and invoicing in your own billing system and use the App API purely to provision and manage the underlying sites. Your Patchstack account's own subscription is handled separately in the Patchstack app UI.
16+
:::
17+
1418
### Documentation and endpoints
1519
Find all the Patchstack App API endpoints with examples from the documentation here:
1620
<a href="https://api.patchstack.com/app-api/documentation" target="_blank">https<span></span>://api.patchstack.com/app-api/documentation</a>

src/content/docs/API solutions/App API/patchstack-integration.md

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ To minimize API calls and reduce latency when displaying site information to you
4444

4545
All Patchstack API endpoints return standard HTTP status codes. Your integration should handle these appropriately to ensure reliability and provide meaningful feedback to users. E.g. handle status codes 200, 401, 422, 500.
4646

47+
#### Rate limits & quotas
48+
- **Sites per request**`POST /site/add` and `POST /site/exists` accept between 1 and **100 URLs** per call in the `urls` array. Split larger batches across multiple requests.
49+
- **Listing sites**`POST /sites/list` is paginated (`page`, `per_page`, default 20 per page); page through it when rendering large accounts. `POST /sites/list/basic` is *not* paginated — it returns the full `[{id, url}, …]` array for the account in one response, which is what makes it convenient for reconciliation.
50+
51+
:::note[No per-key request-rate limit]
52+
The App API does not currently enforce a per-key request-rate limit on authenticated calls. Even so, when provisioning at large scale, batch your requests considerately rather than issuing one call per site — for example, pass multiple URLs in the `urls` array on `/site/add` (and `/site/exists`) instead of looping a separate request per site.
53+
:::
54+
4755
## Flow of integration & plugin
4856
Patchstack works by assigning an API key to a site that has been added to the Patchstack App. This API key is then used in the WordPress plugin to activate the connection to Patchstack.
4957

@@ -140,10 +148,48 @@ The response will look something like below.
140148

141149
The top-level `lastid` and `oauth` fields refer to the **last** site in the batch and are kept for backwards compatibility. New integrations should read from the per-URL `sites` map.
142150

151+
###### Bulk provisioning
152+
The `urls` parameter is an **array**, so you can provision up to **100 sites in a single `/site/add` call** rather than looping one request per site. Pass every URL in the array and the response returns one entry per URL under the `sites` map, keyed by the exact URL you sent:
153+
154+
```bash
155+
curl -X 'POST' \
156+
'https://api.patchstack.com/monitor/site/add' \
157+
-H 'UserToken: <token>' \
158+
-H 'Content-Type: application/json' \
159+
-d '{
160+
"urls": [
161+
"https://site1.com",
162+
"https://site2.com"
163+
],
164+
"cms_id": 1
165+
}'
166+
```
167+
168+
```json
169+
{
170+
"success": "Successfully added the site(s).",
171+
"count": 2,
172+
"sites": {
173+
"https://site1.com": {
174+
"siteid": 12345,
175+
"oauth": { "id": 12333, "secret": "", "apikey": "…-12333" }
176+
},
177+
"https://site2.com": {
178+
"siteid": 12346,
179+
"oauth": { "id": 12334, "secret": "", "apikey": "…-12334" }
180+
}
181+
},
182+
"lastid": 12346,
183+
"oauth": { "id": 12334, "secret": "", "apikey": "…-12334" }
184+
}
185+
```
186+
187+
Reconcile the results **by URL**: iterate the `sites` map and match each key back to the record on your side, rather than relying on array order or the top-level `lastid`. The `/site/exists` endpoint accepts the same `urls` array (up to 100) so you can pre-check a whole batch in one call before adding it. If you need to re-fetch the `{siteid, url}` mapping for every site on the account later, call [`POST /monitor/sites/list/basic`](https://api.patchstack.com/app-api/documentation) and match by URL.
188+
143189
###### 3. Store in local datastore
144190
Store the `siteid` and `apikey` for each URL in a datastore on your infrastructure. This avoids having to query the Patchstack App API each time you need to identify a customer's site.
145191

146-
- **`siteid`** is the canonical Patchstack site identifier. It is what the portal displays and what every per-site API endpoint expects in the URL (`/site/view/{siteid}`, `/site/{siteid}/delete`, `/download/wordpress/{siteid}`, etc.). Store this if you ever need to cross-reference your records against the Patchstack portal.
192+
- **`siteid`** is the canonical Patchstack site identifier. It is what the portal displays and what every per-site API endpoint expects in the URL (`/site/view/{siteid}`, `/site/delete/{siteid}`, `/download/wordpress/{siteid}`, etc.). Store this if you ever need to cross-reference your records against the Patchstack portal.
147193
- **`apikey`** is the pre-formatted plugin license key (`<oauth.secret>-<oauth.id>`). Pass it directly to the WordPress plugin during activation — you do not need to assemble it yourself.
148194

149195
:::note[`siteid` and `oauth.id` are independent fields]
@@ -243,6 +289,37 @@ Simply plug it in your own environment and give customers deeper insights into t
243289

244290
[Click here for integration and more information](/api-solutions/app-api/patchstack-iframe/)
245291

292+
## Site lifecycle: removing & re-adding sites
293+
294+
###### Removing a site
295+
To remove a site from the account — for example when a customer cancels or you clean up a never-activated site — call the delete endpoint with the `siteid` you stored in step 2. The path is `/site/delete/{siteid}` and it accepts both `DELETE` and `POST`.
296+
297+
```bash
298+
curl -X 'DELETE' \
299+
'https://api.patchstack.com/monitor/site/delete/12345' \
300+
-H 'UserToken: <token>' \
301+
-H 'Content-Type: application/json'
302+
```
303+
304+
```json
305+
{
306+
"success": "Successfully deleted the site."
307+
}
308+
```
309+
310+
To also uninstall the Patchstack plugin from the remote WordPress site as part of the deletion, send `{"delete": true}` in the request body. To remove several sites at once, call `DELETE /site/delete` with a body of `{"sites": [12345, 12346]}`.
311+
312+
:::caution[Plugin removal on site deletion]
313+
Deleting a site with the `delete` flag also attempts to remove the Patchstack plugin from the connected WordPress site. This remote removal is best-effort and may not complete if the site connection is unstable, or if the plugin has already been deactivated or disconnected (but not deleted). For reliable removal, manage the plugin directly on the site with WP-CLI (for example `wp plugin delete patchstack`) rather than relying on the remote deletion.
314+
:::
315+
316+
:::caution[Deleting a site is destructive and permanent]
317+
There is **no paused or suspended state** in Patchstack. Deleting a site removes it and its data, and it stops counting toward your plan. If you later re-add the same URL with `/site/add`, Patchstack provisions a **brand-new site** with **new `siteid` and new OAuth credentials** (a new plugin `apikey`) — the previous credentials are not restored. Any integration that "suspends" and "resumes" a site must therefore delete on suspend and re-provision + re-activate the plugin on resume, then overwrite the stored `siteid`/`apikey` with the new values.
318+
:::
319+
320+
###### Cleaning up never-activated sites
321+
A site that was provisioned but whose plugin never connected will report `{"activated": false}` from [`GET /site/plugin/installed/{siteid}`](https://api.patchstack.com/app-api/documentation#/Sites/d932033c445ab06e5fd2dcb6ea8eead3). Combine that check with the delete endpoint above to periodically prune sites that were added but never came online. See the [FAQ](#how-to-determine-if-a-site-has-been-activated-and-is-connected) for the difference between the `plugin/installed` and `state` checks.
322+
246323
## Noteworthy API endpoints
247324
The list below are noteworthy API endpoints that might be interesting to our partners.
248325

@@ -310,7 +387,7 @@ Which outputs the following:
310387
### What's the difference between `siteid` and `oauth.id`? Can I assume they're equal?
311388
They are two independent fields with different purposes — do not assume they are equal.
312389

313-
- **`siteid`** is the canonical Patchstack site identifier. It is what the portal displays and what every per-site API endpoint expects (e.g. `/site/view/{siteid}`, `/site/{siteid}/delete`, `/download/wordpress/{siteid}`). Store this for cross-referencing your records against the Patchstack portal.
390+
- **`siteid`** is the canonical Patchstack site identifier. It is what the portal displays and what every per-site API endpoint expects (e.g. `/site/view/{siteid}`, `/site/delete/{siteid}`, `/download/wordpress/{siteid}`). Store this for cross-referencing your records against the Patchstack portal.
314391
- **`oauth.id`** is the OAuth credential identifier. Its only purpose is to form the plugin license key, which the `/site/add` response already returns pre-formatted as `apikey` (`<oauth.secret>-<oauth.id>`). You never need to read `oauth.id` as a standalone value — just pass `apikey` to the plugin.
315392

316393
For sites provisioned before 13 May 2026, `siteid` and `oauth.id` coincidentally held the same value because of how rows were inserted in our database. They may differ for any site provisioned since. If you previously stored `oauth.id` under the assumption it equalled the site identifier, see the [callout in step 2 of integration](#3-store-in-local-datastore) for how to reconcile your stored values.

src/content/docs/partners/hosting-api.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ updatedAt: "Mon Jul 31 2023 12:13:26 GMT+0000 (Coordinated Universal Time)"
1111
---
1212
# Hosting API Documentation
1313

14+
:::caution[Legacy API — use the App API instead]
15+
The Hosting API (`api.patchstack.com/hosting/`, `HostToken` authentication) is a legacy integration and is no longer the recommended way to integrate with Patchstack. New integrations should use the [Patchstack App API](/api-solutions/app-api/patchstack-app-api/) (`api.patchstack.com/monitor/`, `UserToken` authentication), which is actively maintained and documented. This page is kept for reference for existing Hosting API integrations only.
16+
:::
17+
1418
## Introduction
1519

1620
The information below describes the API URL’s, payloads and authentication necessary to communicate with our API. A special authentication key will have to be supplied in each request (that will be created specifically for the hosting provider) against an endpoint.

0 commit comments

Comments
 (0)