@wildo-ai/saas-technical-doc 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/companion/application-documentation/application-administration-documentation.d.ts +52 -0
- package/dist/esm/companion/application-documentation/application-administration-documentation.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/application-administration-documentation.js +58 -0
- package/dist/esm/companion/application-documentation/application-administration-documentation.js.map +1 -0
- package/dist/esm/companion/application-documentation/application-authentication-documentation.d.ts +76 -0
- package/dist/esm/companion/application-documentation/application-authentication-documentation.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/application-authentication-documentation.js +116 -0
- package/dist/esm/companion/application-documentation/application-authentication-documentation.js.map +1 -0
- package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts +87 -0
- package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/application-connection-documentation.js +138 -0
- package/dist/esm/companion/application-documentation/application-connection-documentation.js.map +1 -0
- package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts +48 -0
- package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/application-integration-documentation.js +63 -0
- package/dist/esm/companion/application-documentation/application-integration-documentation.js.map +1 -0
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts +21 -3
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js +166 -10
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts +8 -0
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js +8 -1
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js.map +1 -1
- package/dist/esm/companion/index.d.ts +4 -0
- package/dist/esm/companion/index.d.ts.map +1 -1
- package/dist/esm/companion/index.js +4 -0
- package/dist/esm/companion/index.js.map +1 -1
- package/dist/esm/companion/openapi-generator.d.ts.map +1 -1
- package/dist/esm/companion/openapi-generator.js +9 -7
- package/dist/esm/companion/openapi-generator.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js +10 -2
- package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-markdown-links.d.ts +28 -0
- package/dist/esm/companion/rendering/technical-documentation-markdown-links.d.ts.map +1 -0
- package/dist/esm/companion/rendering/technical-documentation-markdown-links.js +52 -0
- package/dist/esm/companion/rendering/technical-documentation-markdown-links.js.map +1 -0
- package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.js +7 -2
- package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts +1 -0
- package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-render-model.js +91 -84
- package/dist/esm/companion/rendering/technical-documentation-render-model.js.map +1 -1
- package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts +74 -69
- package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts.map +1 -1
- package/dist/esm/content/application-consumer-documentation-content.techdoc.js +654 -1120
- package/dist/esm/content/application-consumer-documentation-content.techdoc.js.map +1 -1
- package/dist/esm/runtime/decode-jwt-claims.d.ts +7 -4
- package/dist/esm/runtime/decode-jwt-claims.d.ts.map +1 -1
- package/dist/esm/runtime/decode-jwt-claims.js +7 -4
- package/dist/esm/runtime/decode-jwt-claims.js.map +1 -1
- package/dist/esm/runtime/docs-auth-session.schemas.d.ts +16 -5
- package/dist/esm/runtime/docs-auth-session.schemas.d.ts.map +1 -1
- package/dist/esm/runtime/docs-auth-session.schemas.js +16 -5
- package/dist/esm/runtime/docs-auth-session.schemas.js.map +1 -1
- package/dist/esm/runtime/use-docs-auth-session.d.ts +9 -7
- package/dist/esm/runtime/use-docs-auth-session.d.ts.map +1 -1
- package/dist/esm/runtime/use-docs-auth-session.js +9 -7
- package/dist/esm/runtime/use-docs-auth-session.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/dist/esm/.builder.pid +0 -9
|
@@ -15,122 +15,53 @@ export const APPLICATION_CONSUMER_DOCUMENTATION_AUTHORED_CONTENT_V1 = [
|
|
|
15
15
|
{
|
|
16
16
|
managedPath: 'get-started.md',
|
|
17
17
|
unitRef: 'technical-documentation:unit/application-orientation',
|
|
18
|
-
sourceRefs: [
|
|
18
|
+
sourceRefs: [
|
|
19
|
+
'saas-technical-doc:engine-content/application-orientation',
|
|
20
|
+
'source:companion-projection:application-connection',
|
|
21
|
+
],
|
|
19
22
|
markdown: `# Get started
|
|
20
23
|
|
|
21
|
-
This documentation is for people who
|
|
22
|
-
this application**. You do not need to know how the application was built, and
|
|
23
|
-
you do not need to understand every technology before choosing a path. Start
|
|
24
|
-
with the result you need.
|
|
24
|
+
This documentation is for the people who use, administer, integrate with or operate {{APPLICATION_NAME}}. Pick the row that matches what you need to do; each leads to a page you can act on.
|
|
25
25
|
|
|
26
26
|
## What do you need to do?
|
|
27
27
|
|
|
28
|
-
|
|
|
28
|
+
| You need to | Start here | You will find |
|
|
29
29
|
| --- | --- | --- |
|
|
30
|
-
| Sign in,
|
|
31
|
-
| Invite
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
the MCP connection required by the Cursor, Codex and Claude Code guides. If a
|
|
40
|
-
section is absent, do not assume that its capability or administrative surface
|
|
41
|
-
exists in this application.
|
|
42
|
-
|
|
43
|
-
~~~mermaid
|
|
44
|
-
flowchart TD
|
|
45
|
-
accTitle: Choose a documentation path from the outcome you need
|
|
46
|
-
accDescr: A reader starts from a human access task, an administration task, an integration task, or an operational investigation. Each path leads to a guide, then to an exact reference only when implementation details are needed.
|
|
47
|
-
need["What outcome do you need?"] --> person{"A person's access?"}
|
|
48
|
-
person -->|"Sign in or recover"| identity["Access and identity"]
|
|
49
|
-
person -->|"Invite, change or remove"| administration["User administration"]
|
|
50
|
-
person -->|"No"| system{"Connect or operate software?"}
|
|
51
|
-
system -->|"Connect or automate"| integrations["Integrations"]
|
|
52
|
-
system -->|"Investigate or export evidence"| security["Security and audit"]
|
|
53
|
-
identity --> contract["Use the exact reference when needed"]
|
|
54
|
-
administration --> contract
|
|
55
|
-
integrations --> contract
|
|
56
|
-
security --> contract
|
|
57
|
-
~~~
|
|
58
|
-
|
|
59
|
-
The diagram is a starting map, not an access model. Opening a guide does not
|
|
60
|
-
grant a capability. The current application's navigation, visible controls and
|
|
61
|
-
published operation contracts determine what is actually available to you.
|
|
62
|
-
|
|
63
|
-
## Before you change anything
|
|
64
|
-
|
|
65
|
-
Confirm four things before an administrative or integration write:
|
|
30
|
+
| Sign in, set up a second factor, or get back into your account | [Access and identity](/access-and-identity/overview) | The sign-in methods, multifactor enrollment and recovery, and what a refusal means |
|
|
31
|
+
| Invite someone, change what they may do, or remove them | [User administration](/user-administration) | Memberships, the roles available in this application, organization units, single sign-on and directory provisioning |
|
|
32
|
+
| Call the API from your own code | [Send your first API request](/integrations/rest/send-request) | A working request in ten minutes, then the [conventions](/integrations/rest/conventions) every operation follows |
|
|
33
|
+
| Be notified when something changes | [Webhooks](/integrations/webhooks) | Signed deliveries, a receiver you can run, retries and operations |
|
|
34
|
+
| Build in n8n, Zapier or Make | [Automation platforms](/integrations/automation-platforms) | The exact settings for each tool |
|
|
35
|
+
| Connect an AI assistant such as Cursor, Codex or Claude Code | [AI coding tools](/integrations/ai-coding-tools) | The MCP connection and what the assistant may do |
|
|
36
|
+
| Investigate who did what, or export security evidence | [Security and audit](/security-and-audit) | The audit trail, exports and delivery to your SIEM |
|
|
37
|
+
| Understand a plan, an invoice or why access changed | [Billing and subscriptions](/billing-and-subscriptions) | How billing state turns into access, and how to reconcile a change |
|
|
38
|
+
| Look up one operation, field or role | [API reference](/api) | The exact contract of every operation, generated from the application itself |
|
|
66
39
|
|
|
67
|
-
|
|
68
|
-
intended application environment and organization.
|
|
69
|
-
2. **Identity and scope** — use your own sign-in for a human task, or a dedicated
|
|
70
|
-
workload credential for software. Do not borrow a broader administrator
|
|
71
|
-
credential simply to make a request succeed.
|
|
72
|
-
3. **Expected result** — write down the state that should change and the state
|
|
73
|
-
that must remain unchanged.
|
|
74
|
-
4. **Verification** — decide how you will confirm the result from the
|
|
75
|
-
application's authoritative state, especially if a request times out after a
|
|
76
|
-
write.
|
|
40
|
+
Sections appear in the navigation only when this application offers the capability. If a section named above is missing, the application does not expose that capability, and no setting on your side adds it.
|
|
77
41
|
|
|
78
|
-
## Three
|
|
42
|
+
## Three things to know before you change anything
|
|
79
43
|
|
|
80
|
-
|
|
44
|
+
1. **Organization.** Almost everything belongs to one organization. Check which organization you are working in before you invite, change or export; the same person can be a member of several.
|
|
45
|
+
2. **Identity.** Use your own account for work you do yourself and a dedicated API key or OAuth client for software. Never lend an administrator's credential to an integration to make a request succeed; give the integration the smallest role that works.
|
|
46
|
+
3. **Verification.** The application is the source of truth. After an administrative change, a payment or an API write, confirm the result in the application rather than trusting a green status alone.
|
|
81
47
|
|
|
82
|
-
|
|
83
|
-
administration for an individual membership, or the SCIM journey when an
|
|
84
|
-
identity provider owns the user lifecycle. Single sign-on authenticates the
|
|
85
|
-
person; it does not by itself create the intended organization membership or
|
|
86
|
-
role.
|
|
48
|
+
## How the documentation is organised
|
|
87
49
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
the direction and shape of the work:
|
|
92
|
-
|
|
93
|
-
- use **REST** when your integration initiates a read or change;
|
|
94
|
-
- use a **webhook** when the application should notify your receiver of a
|
|
95
|
-
published event;
|
|
96
|
-
- use an **automation platform** when n8n, Zapier or Make should coordinate the
|
|
97
|
-
workflow;
|
|
98
|
-
- use **MCP or A2A** only when the corresponding agent surface is present.
|
|
99
|
-
|
|
100
|
-
Prove one read with a narrowly scoped credential before enabling a write. After
|
|
101
|
-
any timeout or lost response, read the authoritative state before retrying.
|
|
102
|
-
|
|
103
|
-
### Investigate an unexpected result
|
|
104
|
-
|
|
105
|
-
Start with [Security and audit](/security-and-audit) when you need to establish
|
|
106
|
-
who did what, in which organization, and whether the operation succeeded. Start
|
|
107
|
-
with [Troubleshoot user access](/user-administration/troubleshoot-user-access)
|
|
108
|
-
when the symptom is a sign-in, membership, role or visibility problem. Preserve
|
|
109
|
-
correlation identifiers and timestamps; they make a support or audit review
|
|
110
|
-
materially faster.
|
|
111
|
-
|
|
112
|
-
## Guides and references have different jobs
|
|
113
|
-
|
|
114
|
-
A **guide** explains the intended outcome, prerequisites, safe sequence,
|
|
115
|
-
decisions and recovery path. The **API reference** defines exact operations,
|
|
116
|
-
parameters, fields, role requirements and response schemas. Use the guide to
|
|
117
|
-
choose and operate the right journey; use the reference while implementing a
|
|
118
|
-
specific request. Do not turn an endpoint list into an operating procedure, and
|
|
119
|
-
do not treat explanatory prose as a substitute for the current operation
|
|
120
|
-
contract.
|
|
50
|
+
- **Guides** explain a task from start to finish: what you need, what to click or send, what you should see, and what to do when it fails.
|
|
51
|
+
- **Reference** pages hold exact values you look up rather than read: statuses, error codes, limits, field meanings. The [API reference](/api) is generated from the running application, so it is always the current contract.
|
|
52
|
+
- Every guide links to the reference it relies on, and no guide repeats a value the reference owns.
|
|
121
53
|
|
|
122
54
|
## When you ask for help
|
|
123
55
|
|
|
124
|
-
|
|
125
|
-
correlation identifier, expected result and observed result. **Never include a
|
|
126
|
-
password, API key, OAuth secret, SCIM token or session value** in a ticket,
|
|
127
|
-
message or screenshot.`,
|
|
56
|
+
Give the organization, the page or operation you were using, the time, what you expected and what you saw. For an API call, add the HTTP status and the \`error.correlationId\` from the response; for a webhook, the delivery identifier and \`jti\`. Never include a password, an API key, a bearer token or another person's data.`,
|
|
128
57
|
},
|
|
129
58
|
{
|
|
130
59
|
managedPath: 'access-and-identity/overview.md',
|
|
131
60
|
unitRef: 'technical-documentation:unit/authentication',
|
|
132
61
|
sourceRefs: [
|
|
133
62
|
'saas-technical-doc:engine-content/authentication',
|
|
63
|
+
'source:companion-projection:application-authentication',
|
|
64
|
+
'source:companion-projection:application-connection',
|
|
134
65
|
'source:consumer-fact:access-authentication-methods',
|
|
135
66
|
],
|
|
136
67
|
markdown: `# Access and identity
|
|
@@ -173,13 +104,13 @@ receive a refusal.
|
|
|
173
104
|
| Run an unattended workload | [API keys](/access-and-identity/api-keys) | A person’s browser session |
|
|
174
105
|
| Let another client act for a person | [OAuth provider and delegated access](/access-and-identity/oauth-provider) | The person’s password or an over-privileged machine key |
|
|
175
106
|
|
|
176
|
-
## What
|
|
107
|
+
## What {{APPLICATION_NAME}} accepts to sign in
|
|
177
108
|
|
|
178
|
-
{{
|
|
109
|
+
{{APPLICATION_AUTHENTICATION:enabledSignInMethods}}
|
|
179
110
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
111
|
+
### The full catalogue, for comparison
|
|
112
|
+
|
|
113
|
+
{{CONSUMER_FACT:source:consumer-fact:access-authentication-methods#markdown}}
|
|
183
114
|
|
|
184
115
|
## What happens after a credential is presented
|
|
185
116
|
|
|
@@ -211,6 +142,7 @@ Use the generated API reference for the exact operation-level contract.`,
|
|
|
211
142
|
unitRef: 'technical-documentation:unit/sign-in-and-mfa',
|
|
212
143
|
sourceRefs: [
|
|
213
144
|
'saas-technical-doc:engine-content/sign-in-and-mfa',
|
|
145
|
+
'source:companion-projection:application-authentication',
|
|
214
146
|
'source:consumer-fact:access-authentication-methods',
|
|
215
147
|
],
|
|
216
148
|
markdown: `# Sign in and multifactor authentication
|
|
@@ -250,6 +182,22 @@ authorization are separate. A correct password, passkey or provider response
|
|
|
250
182
|
can start a session while the selected organization or role still refuses the
|
|
251
183
|
person's intended work.
|
|
252
184
|
|
|
185
|
+
## What this application requires of you
|
|
186
|
+
|
|
187
|
+
{{APPLICATION_AUTHENTICATION:enabledSignInMethods}}
|
|
188
|
+
|
|
189
|
+
A second factor is a separate decision from the method that starts the sign-in:
|
|
190
|
+
|
|
191
|
+
{{APPLICATION_AUTHENTICATION:multifactorPolicy}}
|
|
192
|
+
|
|
193
|
+
If a password is one of the methods above, it must satisfy these rules:
|
|
194
|
+
|
|
195
|
+
{{APPLICATION_AUTHENTICATION:passwordRules}}
|
|
196
|
+
|
|
197
|
+
## How long a session lasts, and what failed attempts cost
|
|
198
|
+
|
|
199
|
+
{{APPLICATION_AUTHENTICATION:sessionAndLockout}}
|
|
200
|
+
|
|
253
201
|
## Follow the sign-in journey
|
|
254
202
|
|
|
255
203
|
| Your next task | Start here | Successful result |
|
|
@@ -327,7 +275,8 @@ follow the order presented for this attempt.
|
|
|
327
275
|
1. Open the sign-in page for the intended environment and verify the expected
|
|
328
276
|
application identity before entering a credential.
|
|
329
277
|
2. Choose a method currently offered to this person and organization. A method
|
|
330
|
-
from the
|
|
278
|
+
from the catalogue that is absent from the sign-in screen is not available for
|
|
279
|
+
this attempt.
|
|
331
280
|
3. Complete the first factor or organization SSO journey using the person's own
|
|
332
281
|
credential and device.
|
|
333
282
|
4. Complete a required second-factor challenge. If enrollment is requested,
|
|
@@ -361,7 +310,10 @@ recovery value, full provider response or session cookie in support evidence.`,
|
|
|
361
310
|
{
|
|
362
311
|
managedPath: 'access-and-identity/mfa-enrollment-and-recovery.md',
|
|
363
312
|
unitRef: 'technical-documentation:unit/mfa-enrollment-and-recovery',
|
|
364
|
-
sourceRefs: [
|
|
313
|
+
sourceRefs: [
|
|
314
|
+
'saas-technical-doc:engine-content/mfa-enrollment-and-recovery',
|
|
315
|
+
'source:companion-projection:application-authentication',
|
|
316
|
+
],
|
|
365
317
|
markdown: `# Enroll and recover multifactor authentication
|
|
366
318
|
|
|
367
319
|
Use multifactor authentication with a factor controlled by the person signing
|
|
@@ -375,6 +327,10 @@ here.
|
|
|
375
327
|
| --- | --- |
|
|
376
328
|
| Use the person's own session, have the device or inbox needed by the offered method, and choose a protected place for recovery material | The new factor is verified, a fresh challenge succeeds and recovery material is stored separately from the primary credential |
|
|
377
329
|
|
|
330
|
+
## What this application asks for, and what enrolment gives you
|
|
331
|
+
|
|
332
|
+
{{APPLICATION_AUTHENTICATION:multifactorPolicy}}
|
|
333
|
+
|
|
378
334
|
~~~mermaid
|
|
379
335
|
flowchart TD
|
|
380
336
|
accTitle: Enroll a factor and preserve a safe recovery path
|
|
@@ -521,9 +477,8 @@ organization.
|
|
|
521
477
|
organization-scope** problem.
|
|
522
478
|
- A resource that appears missing can be genuinely absent or outside the
|
|
523
479
|
person's visible scope. Do not confirm hidden data from the error alone.
|
|
524
|
-
- A method listed in the
|
|
525
|
-
|
|
526
|
-
availability evidence.
|
|
480
|
+
- A method listed in the catalogue is not necessarily enabled for this person and
|
|
481
|
+
organization. The current sign-in screen is the availability evidence.
|
|
527
482
|
|
|
528
483
|
Never use an administrator account, another person's session or a machine
|
|
529
484
|
credential to make a user action succeed. That hides the real problem and
|
|
@@ -2299,7 +2254,6 @@ complete authorization response.`,
|
|
|
2299
2254
|
unitRef: 'technical-documentation:unit/user-administration',
|
|
2300
2255
|
sourceRefs: [
|
|
2301
2256
|
'saas-technical-doc:engine-content/user-administration',
|
|
2302
|
-
'source:consumer-fact:organization-membership-administration',
|
|
2303
2257
|
],
|
|
2304
2258
|
markdown: `# User administration
|
|
2305
2259
|
|
|
@@ -2326,7 +2280,7 @@ flowchart LR
|
|
|
2326
2280
|
|
|
2327
2281
|
## Choose how your organization manages users
|
|
2328
2282
|
|
|
2329
|
-
|
|
2283
|
+
Membership states, the roles available in this application and the last-owner rule are listed once, on [Roles and permissions](/user-administration/roles-and-permissions).
|
|
2330
2284
|
|
|
2331
2285
|
Choose **manual management** when an administrator should invite people, choose
|
|
2332
2286
|
their access, pause access, or remove them directly in the application. Each
|
|
@@ -2453,7 +2407,6 @@ credentials into tickets or audit notes.`,
|
|
|
2453
2407
|
unitRef: 'technical-documentation:unit/invite-and-onboard-user',
|
|
2454
2408
|
sourceRefs: [
|
|
2455
2409
|
'saas-technical-doc:engine-content/invite-and-onboard-user',
|
|
2456
|
-
'source:consumer-fact:organization-membership-administration',
|
|
2457
2410
|
],
|
|
2458
2411
|
markdown: `# Invite and onboard a user
|
|
2459
2412
|
|
|
@@ -2525,7 +2478,7 @@ Then confirm all three choices with the person’s manager or access owner:
|
|
|
2525
2478
|
|
|
2526
2479
|
## Send and follow the invitation
|
|
2527
2480
|
|
|
2528
|
-
|
|
2481
|
+
Membership states, the roles available in this application and the last-owner rule are listed once, on [Roles and permissions](/user-administration/roles-and-permissions).
|
|
2529
2482
|
|
|
2530
2483
|
After you send it, find the person in the member list and check that the state
|
|
2531
2484
|
is **Invited**. At this point, the person has not received active organization
|
|
@@ -2534,7 +2487,10 @@ email filtering rules, then use the published resend action for the pending
|
|
|
2534
2487
|
invitation. Resending replaces the earlier acceptance link; do not create a
|
|
2535
2488
|
second invitation for the same person just to send another email.
|
|
2536
2489
|
|
|
2537
|
-
The invitation link is valid for **seven days** and can be used once
|
|
2490
|
+
The invitation link is valid for **seven days** and can be used once; resend
|
|
2491
|
+
it to issue a fresh link. An invitation that is neither accepted nor revoked
|
|
2492
|
+
is removed automatically after **30 days**, so a person who never responds
|
|
2493
|
+
does not stay listed as invited indefinitely. If the
|
|
2538
2494
|
link is expired, revoked, already consumed, or no longer matches an invited
|
|
2539
2495
|
membership, the acceptance must fail rather than activating a different state.
|
|
2540
2496
|
|
|
@@ -2583,6 +2539,7 @@ offer when the underlying access decision changes.`,
|
|
|
2583
2539
|
unitRef: 'technical-documentation:unit/organization-roles',
|
|
2584
2540
|
sourceRefs: [
|
|
2585
2541
|
'saas-technical-doc:engine-content/organization-roles',
|
|
2542
|
+
'source:companion-projection:application-administration',
|
|
2586
2543
|
'source:companion-projection:organization-roles',
|
|
2587
2544
|
'source:consumer-fact:organization-membership-administration',
|
|
2588
2545
|
],
|
|
@@ -2628,6 +2585,13 @@ The role descriptions below explain their intended use and boundary. A
|
|
|
2628
2585
|
specific operation may impose a narrower rule, so the API reference remains
|
|
2629
2586
|
authoritative when you need to know exactly who may call one operation.
|
|
2630
2587
|
|
|
2588
|
+
## Who may administer membership here
|
|
2589
|
+
|
|
2590
|
+
The roles above say what each role is for. This says which of them may perform
|
|
2591
|
+
each membership action this application publishes:
|
|
2592
|
+
|
|
2593
|
+
{{APPLICATION_ADMINISTRATION:memberActions}}
|
|
2594
|
+
|
|
2631
2595
|
## Roles available in this application
|
|
2632
2596
|
|
|
2633
2597
|
{{APPLICATION_ORGANIZATION_ROLES}}
|
|
@@ -2706,7 +2670,6 @@ different credential to bypass the refusal.`,
|
|
|
2706
2670
|
unitRef: 'technical-documentation:unit/change-user-access',
|
|
2707
2671
|
sourceRefs: [
|
|
2708
2672
|
'saas-technical-doc:engine-content/change-user-access',
|
|
2709
|
-
'source:consumer-fact:organization-membership-administration',
|
|
2710
2673
|
],
|
|
2711
2674
|
markdown: `# Change a user's access
|
|
2712
2675
|
|
|
@@ -2785,7 +2748,7 @@ flowchart TD
|
|
|
2785
2748
|
|
|
2786
2749
|
## How membership state affects authority
|
|
2787
2750
|
|
|
2788
|
-
|
|
2751
|
+
Membership states, the roles available in this application and the last-owner rule are listed once, on [Roles and permissions](/user-administration/roles-and-permissions).
|
|
2789
2752
|
|
|
2790
2753
|
Only an **Active** membership contributes organization authority. A role is not
|
|
2791
2754
|
a job title: it is a named permission set evaluated in the selected
|
|
@@ -2866,6 +2829,7 @@ credential to bypass the refusal.`,
|
|
|
2866
2829
|
unitRef: 'technical-documentation:unit/manage-organization-units',
|
|
2867
2830
|
sourceRefs: [
|
|
2868
2831
|
'saas-technical-doc:engine-content/manage-organization-units',
|
|
2832
|
+
'source:companion-projection:application-administration',
|
|
2869
2833
|
'source:companion-projection:organization-unit-aware-resources',
|
|
2870
2834
|
'source:consumer-fact:organization-units',
|
|
2871
2835
|
],
|
|
@@ -2882,6 +2846,10 @@ organization settings. If it does not, use the **Organization units API
|
|
|
2882
2846
|
reference** linked from this page. This guide explains the business decisions;
|
|
2883
2847
|
the reference supplies the exact operations the application publishes.
|
|
2884
2848
|
|
|
2849
|
+
## Who may administer organization units here
|
|
2850
|
+
|
|
2851
|
+
{{APPLICATION_ADMINISTRATION:organizationUnitActions}}
|
|
2852
|
+
|
|
2885
2853
|
> **Start with the business decision.** Name the information or action that a
|
|
2886
2854
|
> department must control before you build the hierarchy. If nothing in the
|
|
2887
2855
|
> application is unit-aware, a unit is only a label and grants no useful access.
|
|
@@ -3160,7 +3128,6 @@ result with a representative member’s own account.`,
|
|
|
3160
3128
|
unitRef: 'technical-documentation:unit/suspend-or-remove-user',
|
|
3161
3129
|
sourceRefs: [
|
|
3162
3130
|
'saas-technical-doc:engine-content/suspend-or-remove-user',
|
|
3163
|
-
'source:consumer-fact:organization-membership-administration',
|
|
3164
3131
|
],
|
|
3165
3132
|
markdown: `# Suspend or remove a user
|
|
3166
3133
|
|
|
@@ -3215,7 +3182,7 @@ flowchart LR
|
|
|
3215
3182
|
| The person should temporarily lose access | Suspend the membership | Keep the membership record while access is on hold; reactivate only when the hold is cleared |
|
|
3216
3183
|
| The person has left this organization or must permanently lose this organization access | Remove the membership | End this organization relationship and its roles |
|
|
3217
3184
|
|
|
3218
|
-
|
|
3185
|
+
Membership states, the roles available in this application and the last-owner rule are listed once, on [Roles and permissions](/user-administration/roles-and-permissions).
|
|
3219
3186
|
|
|
3220
3187
|
## Protect organization administration first
|
|
3221
3188
|
|
|
@@ -3283,7 +3250,6 @@ transition is not valid from the current membership state.`,
|
|
|
3283
3250
|
unitRef: 'technical-documentation:unit/review-users-and-access',
|
|
3284
3251
|
sourceRefs: [
|
|
3285
3252
|
'saas-technical-doc:engine-content/review-users-and-access',
|
|
3286
|
-
'source:consumer-fact:organization-membership-administration',
|
|
3287
3253
|
],
|
|
3288
3254
|
markdown: `# Review users and access
|
|
3289
3255
|
|
|
@@ -3329,7 +3295,7 @@ Start broad so an invited, suspended or inactive record is not omitted simply
|
|
|
3329
3295
|
because it cannot currently authorize an operation. Then narrow the review to
|
|
3330
3296
|
the access that carries the most business or administrative impact.
|
|
3331
3297
|
|
|
3332
|
-
|
|
3298
|
+
Membership states, the roles available in this application and the last-owner rule are listed once, on [Roles and permissions](/user-administration/roles-and-permissions).
|
|
3333
3299
|
|
|
3334
3300
|
## Prepare the review
|
|
3335
3301
|
|
|
@@ -3712,9 +3678,8 @@ list—not a 404:
|
|
|
3712
3678
|
}
|
|
3713
3679
|
~~~
|
|
3714
3680
|
|
|
3715
|
-
Errors use the SCIM error media type and may include a \`scimType\`
|
|
3716
|
-
\`
|
|
3717
|
-
\`tooMany\`. Preserve the HTTP status, SCIM type, redacted detail, operation,
|
|
3681
|
+
Errors use the SCIM error media type and may include a \`scimType\` of
|
|
3682
|
+
\`invalidSyntax\`, \`invalidValue\`, \`uniqueness\` or \`tooMany\`. Preserve the HTTP status, SCIM type, redacted detail, operation,
|
|
3718
3683
|
provider job identifier and time. Never preserve the bearer token.
|
|
3719
3684
|
|
|
3720
3685
|
## Standards used by this profile
|
|
@@ -3800,7 +3765,7 @@ using it for automatic provisioning.
|
|
|
3800
3765
|
"autoCreateUsers": true,
|
|
3801
3766
|
"autoVerifyEmail": true,
|
|
3802
3767
|
"autoDeactivateUsers": false,
|
|
3803
|
-
"defaultRole": "
|
|
3768
|
+
"defaultRole": "ORG_MEMBER",
|
|
3804
3769
|
"attributeMapping": {
|
|
3805
3770
|
"email": "emails[primary eq true].value",
|
|
3806
3771
|
"firstName": "name.givenName",
|
|
@@ -4499,7 +4464,10 @@ unrestricted evidence export in an ordinary support ticket.`,
|
|
|
4499
4464
|
{
|
|
4500
4465
|
managedPath: 'security-and-audit/investigate-event.md',
|
|
4501
4466
|
unitRef: 'technical-documentation:unit/investigate-audit-event',
|
|
4502
|
-
sourceRefs: [
|
|
4467
|
+
sourceRefs: [
|
|
4468
|
+
'saas-technical-doc:engine-content/investigate-audit-event',
|
|
4469
|
+
'source:consumer-fact:organization-audit-trail',
|
|
4470
|
+
],
|
|
4503
4471
|
markdown: `# Investigate an audit event
|
|
4504
4472
|
|
|
4505
4473
|
Begin with a question, not with the entire event stream: “Why was this request
|
|
@@ -4507,6 +4475,17 @@ refused?”, “Who changed this person's role?” or “What happened after thi
|
|
|
4507
4475
|
credential was used?” Choose the smallest time range and organization that can
|
|
4508
4476
|
answer it.
|
|
4509
4477
|
|
|
4478
|
+
## What you can narrow the trail by
|
|
4479
|
+
|
|
4480
|
+
{{CONSUMER_FACT:source:consumer-fact:organization-audit-trail#queryMarkdown}}
|
|
4481
|
+
|
|
4482
|
+
Every event carries a category and a severity. Both are closed lists, so they are
|
|
4483
|
+
the two filters that reliably cut a broad question down:
|
|
4484
|
+
|
|
4485
|
+
{{CONSUMER_FACT:source:consumer-fact:organization-audit-trail#categoriesMarkdown}}
|
|
4486
|
+
|
|
4487
|
+
{{CONSUMER_FACT:source:consumer-fact:organization-audit-trail#severitiesMarkdown}}
|
|
4488
|
+
|
|
4510
4489
|
| Before you begin | Successful result |
|
|
4511
4490
|
| --- | --- |
|
|
4512
4491
|
| Have one reviewable question, the intended organization/environment, an approximate time and any event, correlation, resource or actor identifier already known | The relevant event chain and actor/outcome are explained, current resource state is reconciled, and any anomaly has an owner without changing the evidence |
|
|
@@ -5337,7 +5316,10 @@ delivery can coexist with a broken mapping or detection.`,
|
|
|
5337
5316
|
{
|
|
5338
5317
|
managedPath: 'security-and-audit/operate-siem.md',
|
|
5339
5318
|
unitRef: 'technical-documentation:unit/operate-and-troubleshoot-siem-delivery',
|
|
5340
|
-
sourceRefs: [
|
|
5319
|
+
sourceRefs: [
|
|
5320
|
+
'saas-technical-doc:engine-content/operate-and-troubleshoot-siem-delivery',
|
|
5321
|
+
'source:consumer-fact:organization-siem-export',
|
|
5322
|
+
],
|
|
5341
5323
|
markdown: `# Operate and troubleshoot SIEM delivery
|
|
5342
5324
|
|
|
5343
5325
|
The organization audit record and the SIEM copy have different health signals.
|
|
@@ -5363,6 +5345,10 @@ flowchart TD
|
|
|
5363
5345
|
captured -->|Authorized purge| purge["Remove recovery row without retry"]
|
|
5364
5346
|
~~~
|
|
5365
5347
|
|
|
5348
|
+
## When a destination keeps failing
|
|
5349
|
+
|
|
5350
|
+
{{CONSUMER_FACT:source:consumer-fact:organization-siem-export#deliveryReliabilityMarkdown}}
|
|
5351
|
+
|
|
5366
5352
|
## Start from the failure boundary
|
|
5367
5353
|
|
|
5368
5354
|
| Symptom | First checks |
|
|
@@ -5765,7 +5751,6 @@ only after the expected application proof is clear.`,
|
|
|
5765
5751
|
unitRef: 'technical-documentation:unit/start-billing-subscription',
|
|
5766
5752
|
sourceRefs: [
|
|
5767
5753
|
'saas-technical-doc:engine-content/start-billing-subscription',
|
|
5768
|
-
'source:consumer-fact:billing-lifecycle',
|
|
5769
5754
|
],
|
|
5770
5755
|
markdown: `# Start a subscription
|
|
5771
5756
|
|
|
@@ -5840,7 +5825,8 @@ Do not use the success URL as the success criterion. In the application, verify:
|
|
|
5840
5825
|
|
|
5841
5826
|
Use the maintained subscription meanings when reading the result:
|
|
5842
5827
|
|
|
5843
|
-
|
|
5828
|
+
The subscription states and what each one means for access are listed under
|
|
5829
|
+
[Reconcile access after a billing change](/billing-and-subscriptions/reconcile-access).
|
|
5844
5830
|
|
|
5845
5831
|
If the state remains incomplete, past due, unpaid or absent, do not create a
|
|
5846
5832
|
second checkout immediately. Use the troubleshooting and reconciliation pages
|
|
@@ -5848,7 +5834,7 @@ to determine whether the first attempt is still being processed.
|
|
|
5848
5834
|
|
|
5849
5835
|
### Example: the browser closes after payment
|
|
5850
5836
|
|
|
5851
|
-
Suppose an administrator approves
|
|
5837
|
+
Suppose an administrator approves a paid plan, completes the hosted
|
|
5852
5838
|
payment, and the browser closes before returning. Reopen Billing in the same
|
|
5853
5839
|
organization and read the current subscription. If the approved plan is Active
|
|
5854
5840
|
and the expected limit works, record that result and close the task. If the
|
|
@@ -5944,8 +5930,9 @@ state and payment evidence.
|
|
|
5944
5930
|
|
|
5945
5931
|
### Worked example: increase capacity mid-period
|
|
5946
5932
|
|
|
5947
|
-
|
|
5948
|
-
|
|
5933
|
+
The figures below are illustrative; your own limits are shown in Billing.
|
|
5934
|
+
Suppose an organization's plan allows 50 automated runs and it needs 20 more
|
|
5935
|
+
immediately. Before confirming an add-on, record:
|
|
5949
5936
|
|
|
5950
5937
|
- the current 50-run limit and current billing-period end;
|
|
5951
5938
|
- the add-on price and whether the presented adjustment is immediate;
|
|
@@ -6164,6 +6151,12 @@ ordinary support evidence—not the complete document or payment details.`,
|
|
|
6164
6151
|
],
|
|
6165
6152
|
markdown: `# Understand metered usage
|
|
6166
6153
|
|
|
6154
|
+
This page applies only if a plan or add-on you hold charges for a measured
|
|
6155
|
+
quantity. If everything you subscribe to is a flat recurring price, no usage is
|
|
6156
|
+
recorded and no usage line appears on an invoice — check
|
|
6157
|
+
[Understand plans, prices and access](/billing-and-subscriptions/plans-and-access)
|
|
6158
|
+
to see which of the two you have.
|
|
6159
|
+
|
|
6167
6160
|
A metered product charges for a measured quantity, such as processed documents,
|
|
6168
6161
|
automated runs or storage consumed. The application records the activity first;
|
|
6169
6162
|
the billing provider receives grouped usage later and uses its configured meter
|
|
@@ -6346,7 +6339,6 @@ card details, provider secrets and complete invoice documents.`,
|
|
|
6346
6339
|
unitRef: 'technical-documentation:unit/troubleshoot-billing-change',
|
|
6347
6340
|
sourceRefs: [
|
|
6348
6341
|
'saas-technical-doc:engine-content/troubleshoot-billing-change',
|
|
6349
|
-
'source:consumer-fact:billing-lifecycle',
|
|
6350
6342
|
],
|
|
6351
6343
|
markdown: `# Troubleshoot a billing change
|
|
6352
6344
|
|
|
@@ -6382,7 +6374,8 @@ scope, then correlate provider evidence only where the state requires it.
|
|
|
6382
6374
|
|
|
6383
6375
|
Use these maintained state meanings while diagnosing:
|
|
6384
6376
|
|
|
6385
|
-
|
|
6377
|
+
The full list of subscription states, and what each one means for access, is under
|
|
6378
|
+
[Reconcile access after a billing change](/billing-and-subscriptions/reconcile-access).
|
|
6386
6379
|
|
|
6387
6380
|
## Start from the symptom
|
|
6388
6381
|
|
|
@@ -7087,7 +7080,10 @@ sequenceDiagram
|
|
|
7087
7080
|
{
|
|
7088
7081
|
managedPath: 'integrations/ai-coding-tools.md',
|
|
7089
7082
|
unitRef: 'technical-documentation:unit/ai-coding-tools',
|
|
7090
|
-
sourceRefs: [
|
|
7083
|
+
sourceRefs: [
|
|
7084
|
+
'saas-technical-doc:engine-content/ai-coding-tools',
|
|
7085
|
+
'source:companion-projection:application-connection',
|
|
7086
|
+
],
|
|
7091
7087
|
markdown: `# AI coding tools
|
|
7092
7088
|
|
|
7093
7089
|
This application publishes an **MCP tool server** that supported AI coding
|
|
@@ -7099,7 +7095,7 @@ access, and it does not turn every application action into an autonomous one.
|
|
|
7099
7095
|
|
|
7100
7096
|
- the server origin for the intended application environment;
|
|
7101
7097
|
- authorization to use the MCP audience at
|
|
7102
|
-
|
|
7098
|
+
**{{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}**;
|
|
7103
7099
|
- membership and roles for the organization you intend to work in;
|
|
7104
7100
|
- confirmation of which tools may run automatically and which require approval.
|
|
7105
7101
|
|
|
@@ -7148,7 +7144,10 @@ be pasted into either.`,
|
|
|
7148
7144
|
{
|
|
7149
7145
|
managedPath: 'integrations/ai-tools/cursor.md',
|
|
7150
7146
|
unitRef: 'technical-documentation:unit/connect-cursor',
|
|
7151
|
-
sourceRefs: [
|
|
7147
|
+
sourceRefs: [
|
|
7148
|
+
'saas-technical-doc:engine-content/connect-cursor',
|
|
7149
|
+
'source:companion-projection:application-connection',
|
|
7150
|
+
],
|
|
7152
7151
|
markdown: `# Connect Cursor
|
|
7153
7152
|
|
|
7154
7153
|
Use this guide when Cursor Agent should read or act on information in this
|
|
@@ -7182,14 +7181,13 @@ Create the selected **mcp.json** file and add:
|
|
|
7182
7181
|
{
|
|
7183
7182
|
"mcpServers": {
|
|
7184
7183
|
"application": {
|
|
7185
|
-
"url": "
|
|
7184
|
+
"url": "{{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}"
|
|
7186
7185
|
}
|
|
7187
7186
|
}
|
|
7188
7187
|
}
|
|
7189
7188
|
~~~
|
|
7190
7189
|
|
|
7191
|
-
|
|
7192
|
-
environment. Keep **/api/v1/mcp** exactly once. Do not add an Authorization
|
|
7190
|
+
The URL above is this application’s MCP endpoint. Confirm it belongs to the environment you intend before connecting. Keep **/api/v1/mcp** exactly once. Do not add an Authorization
|
|
7193
7191
|
header to the shared JSON when the server supports interactive OAuth.
|
|
7194
7192
|
|
|
7195
7193
|
Cursor's [current MCP documentation](https://cursor.com/docs/mcp) owns the
|
|
@@ -7287,7 +7285,10 @@ sequenceDiagram
|
|
|
7287
7285
|
{
|
|
7288
7286
|
managedPath: 'integrations/ai-tools/codex.md',
|
|
7289
7287
|
unitRef: 'technical-documentation:unit/connect-codex',
|
|
7290
|
-
sourceRefs: [
|
|
7288
|
+
sourceRefs: [
|
|
7289
|
+
'saas-technical-doc:engine-content/connect-codex',
|
|
7290
|
+
'source:companion-projection:application-connection',
|
|
7291
|
+
],
|
|
7291
7292
|
markdown: `# Connect Codex
|
|
7292
7293
|
|
|
7293
7294
|
Use this guide when Codex needs authorized information or operations from this
|
|
@@ -7318,12 +7319,12 @@ Add this table to the selected configuration file:
|
|
|
7318
7319
|
|
|
7319
7320
|
~~~toml
|
|
7320
7321
|
[mcp_servers.application]
|
|
7321
|
-
url = "
|
|
7322
|
+
url = "{{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}"
|
|
7322
7323
|
default_tools_approval_mode = "prompt"
|
|
7323
7324
|
~~~
|
|
7324
7325
|
|
|
7325
|
-
|
|
7326
|
-
|
|
7326
|
+
The URL above is this application’s MCP endpoint. Confirm it belongs to the environment
|
|
7327
|
+
you intend before connecting. Keep **/api/v1/mcp** exactly once. The prompt approval mode means
|
|
7327
7328
|
Codex asks before using tools from this server.
|
|
7328
7329
|
|
|
7329
7330
|
The [official Codex MCP documentation](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)
|
|
@@ -7423,7 +7424,10 @@ connectivity. A working read does not justify automatic write approval.
|
|
|
7423
7424
|
{
|
|
7424
7425
|
managedPath: 'integrations/ai-tools/claude-code.md',
|
|
7425
7426
|
unitRef: 'technical-documentation:unit/connect-claude-code',
|
|
7426
|
-
sourceRefs: [
|
|
7427
|
+
sourceRefs: [
|
|
7428
|
+
'saas-technical-doc:engine-content/connect-claude-code',
|
|
7429
|
+
'source:companion-projection:application-connection',
|
|
7430
|
+
],
|
|
7427
7431
|
markdown: `# Connect Claude Code
|
|
7428
7432
|
|
|
7429
7433
|
Use this guide when Claude Code should read or act on information in this
|
|
@@ -7452,11 +7456,11 @@ Start with local scope unless a reviewed team or personal-wide need exists.
|
|
|
7452
7456
|
Run this from the intended project:
|
|
7453
7457
|
|
|
7454
7458
|
~~~bash
|
|
7455
|
-
claude mcp add --transport http application
|
|
7459
|
+
claude mcp add --transport http application {{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}
|
|
7456
7460
|
~~~
|
|
7457
7461
|
|
|
7458
|
-
|
|
7459
|
-
|
|
7462
|
+
The URL above is this application’s MCP endpoint. Confirm it belongs to the environment
|
|
7463
|
+
you intend before connecting, and keep **/api/v1/mcp** exactly once. Add **--scope project** or
|
|
7460
7464
|
**--scope user** only after making the scope decision above.
|
|
7461
7465
|
|
|
7462
7466
|
The [current Claude Code MCP documentation](https://code.claude.com/docs/en/mcp)
|
|
@@ -7677,6 +7681,7 @@ evidence, but they do not replace the application's authoritative audit trail.
|
|
|
7677
7681
|
unitRef: 'technical-documentation:unit/first-request',
|
|
7678
7682
|
sourceRefs: [
|
|
7679
7683
|
'saas-technical-doc:engine-content/first-request',
|
|
7684
|
+
'source:companion-projection:application-connection',
|
|
7680
7685
|
'source:consumer-fact:access-api-keys',
|
|
7681
7686
|
],
|
|
7682
7687
|
markdown: `# Create your own integration
|
|
@@ -7770,7 +7775,7 @@ is the application's maintained API-key transport contract.
|
|
|
7770
7775
|
|
|
7771
7776
|
~~~bash
|
|
7772
7777
|
curl --fail-with-body \
|
|
7773
|
-
--url "<
|
|
7778
|
+
--url "{{APPLICATION_CONNECTION_VALUE:baseUrl}}<documented-operation-path>" \
|
|
7774
7779
|
{{CONSUMER_FACT:source:consumer-fact:access-api-keys#curlRequestHeader}} \
|
|
7775
7780
|
--header "accept: application/json"
|
|
7776
7781
|
~~~
|
|
@@ -7860,1203 +7865,717 @@ including collections, controlled writes and troubleshooting.`,
|
|
|
7860
7865
|
{
|
|
7861
7866
|
managedPath: 'integrations/rest-api.md',
|
|
7862
7867
|
unitRef: 'technical-documentation:unit/common-rest-api',
|
|
7863
|
-
sourceRefs: [
|
|
7868
|
+
sourceRefs: [
|
|
7869
|
+
'saas-technical-doc:engine-content/common-rest-api',
|
|
7870
|
+
],
|
|
7864
7871
|
markdown: `# REST API
|
|
7865
7872
|
|
|
7866
|
-
|
|
7867
|
-
and needs an immediate response. This section explains the decisions shared by
|
|
7868
|
-
REST integrations. The generated API reference remains authoritative for exact
|
|
7869
|
-
paths, fields, roles, statuses and resource-specific limits.
|
|
7873
|
+
The REST API lets another system read and change this application's data with an immediate answer. Everything the application shows in its own screens is reachable the same way: each screen is backed by the operations listed in the [API reference](/api), and an integration calls those operations directly.
|
|
7870
7874
|
|
|
7871
|
-
|
|
7872
|
-
| --- | --- |
|
|
7873
|
-
| Name one business outcome, the system that owns it, the organization in scope and the identity that should perform it | A repeatable request reaches the intended environment, proves only the required access and can be reconciled safely after failure |
|
|
7875
|
+
## What you get
|
|
7874
7876
|
|
|
7875
|
-
|
|
7877
|
+
- One JSON API under \`/api/v1\`, documented operation by operation in the API reference: path, method, request fields, response shape, accepted credentials, required roles and the failures each operation can return.
|
|
7878
|
+
- One set of conventions shared by every operation: how to authenticate, how collections page and sort, what an error looks like, how limits and optimistic locking work. They are on one page, [REST API conventions](/integrations/rest/conventions), so the reference does not have to repeat them.
|
|
7879
|
+
- Two credential kinds. An **API key** is a long-lived secret for software that acts on behalf of an organization or the whole application. A **bearer token** is what a signed-in person or an OAuth client holds. [API keys](/access-and-identity/api-keys) explains how to create one and what it may do.
|
|
7876
7880
|
|
|
7877
|
-
|
|
7881
|
+
## Choose the right tool first
|
|
7882
|
+
|
|
7883
|
+
| You need to | Use | Why |
|
|
7878
7884
|
| --- | --- | --- |
|
|
7879
|
-
| Read
|
|
7880
|
-
|
|
|
7881
|
-
|
|
|
7882
|
-
|
|
|
7885
|
+
| Read or change data now, and know the result | REST | The application answers each request with the outcome |
|
|
7886
|
+
| React after something happens in the application | [Webhooks](/integrations/webhooks) | The application calls you; no polling |
|
|
7887
|
+
| Let an AI assistant work with the application | [MCP](/integrations/mcp) | Tools are discovered and invoked by the assistant, not scripted by you |
|
|
7888
|
+
| Build in n8n, Zapier or Make | [Automation platforms](/integrations/automation-platforms) | Those tools already speak REST; the guides show the exact settings |
|
|
7883
7889
|
|
|
7884
|
-
|
|
7885
|
-
own a business workflow, which organization is implied or whether a write is
|
|
7886
|
-
safe to repeat. Those decisions belong to the integration design and the exact
|
|
7887
|
-
operation contract.
|
|
7890
|
+
## The four pages of this section
|
|
7888
7891
|
|
|
7889
|
-
|
|
7892
|
+
1. [Send your first API request](/integrations/rest/send-request): reach the right environment with the right credential and prove it in ten minutes.
|
|
7893
|
+
2. [Read collections](/integrations/rest/read-collections): page, sort, search and filter without skipping or duplicating records.
|
|
7894
|
+
3. [Write and reconcile changes](/integrations/rest/write-and-reconcile): make a change once, use optimistic locking, and recover from a lost response.
|
|
7895
|
+
4. [Troubleshoot an API request](/integrations/rest/troubleshoot): turn a status code and an error body into the one thing to fix.
|
|
7890
7896
|
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7897
|
+
Keep [REST API conventions](/integrations/rest/conventions) open beside the API reference while you work; it is the page these guides point at for exact names and values.`,
|
|
7898
|
+
},
|
|
7899
|
+
{
|
|
7900
|
+
managedPath: 'integrations/rest/conventions.md',
|
|
7901
|
+
unitRef: 'technical-documentation:unit/rest-api-conventions',
|
|
7902
|
+
sourceRefs: [
|
|
7903
|
+
'saas-technical-doc:engine-content/rest-api-conventions',
|
|
7904
|
+
'source:companion-projection:application-connection',
|
|
7905
|
+
'source:consumer-fact:access-api-keys',
|
|
7906
|
+
'source:consumer-fact:rest-conventions',
|
|
7907
|
+
],
|
|
7908
|
+
markdown: `# REST API conventions
|
|
7909
|
+
|
|
7910
|
+
Every operation in the API reference follows the same rules for addressing, authentication, collections, writes, errors and limits. Read this page once. After that, an operation's entry in the reference only has to tell you what is specific to it: its path, its fields, the roles it accepts and the failures it can return.
|
|
7911
|
+
|
|
7912
|
+
## Addressing
|
|
7913
|
+
|
|
7914
|
+
- Every path in the API reference already starts with the \`/api/v1\` mount. Prepend the base URL of the environment you are calling:
|
|
7915
|
+
|
|
7916
|
+
{{APPLICATION_CONNECTION:baseUrls}}
|
|
7917
|
+
|
|
7918
|
+
- Organization data lives under \`/api/v1/organizations/{organizationId}/…\`. The organization identifier is the one shown in the application. A collection under an organization your credential is not a member of answers **403**; a single record that belongs to another organization answers **404**, so that the application never confirms what exists outside your scope.
|
|
7919
|
+
- The same resource is often reachable through more than one parent path (for example through the organization directly and through a related record). Those paths are aliases of one operation and answer with the same data; use whichever matches the identifiers you already hold.
|
|
7920
|
+
- Send and expect \`application/json\`. Identifiers are opaque strings: store them, compare them, never parse them.
|
|
7921
|
+
- Header names are case-insensitive; this documentation writes them the way the application emits them.
|
|
7922
|
+
|
|
7923
|
+
## Authentication
|
|
7924
|
+
|
|
7925
|
+
{{CONSUMER_FACT:source:consumer-fact:access-api-keys#markdown}}
|
|
7926
|
+
|
|
7927
|
+
Signed-in people and OAuth clients use a bearer token instead: \`Authorization: Bearer <access token>\`. Each operation's **Security** entry in the reference lists which of the two it accepts. Send exactly one credential per request.
|
|
7928
|
+
|
|
7929
|
+
## Collections
|
|
7930
|
+
|
|
7931
|
+
List and search operations share one query vocabulary:
|
|
7932
|
+
|
|
7933
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#collectionQueryMarkdown}}
|
|
7934
|
+
|
|
7935
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#paginationResponseMarkdown}}
|
|
7936
|
+
|
|
7937
|
+
A concrete request and its response, using the members of your own organization:
|
|
7938
|
+
|
|
7939
|
+
~~~bash
|
|
7940
|
+
curl --fail-with-body \\
|
|
7941
|
+
--url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID/organization-members?page=1&limit=20&sort=joinedAt:desc" \\
|
|
7942
|
+
--header "Authorization: $ORGANIZATION_API_KEY" \\
|
|
7943
|
+
--header "accept: application/json"
|
|
7901
7944
|
~~~
|
|
7902
7945
|
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
- [
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7946
|
+
~~~json
|
|
7947
|
+
{
|
|
7948
|
+
"data": [
|
|
7949
|
+
{
|
|
7950
|
+
"_id": "0d3f2a9e-6c41-4b7a-8e15-5f9c2b7d4a10",
|
|
7951
|
+
"userId": "b8e4c2f1-3a57-4d09-9f6e-1c2d3e4f5a6b",
|
|
7952
|
+
"userEmail": "alex.morgan@example.com",
|
|
7953
|
+
"organizationId": "7c1f6f0a-4b1e-4d8f-9a0c-2e5b3c9d1f42",
|
|
7954
|
+
"roles": ["ORG_MEMBER"],
|
|
7955
|
+
"status": "ACTIVE",
|
|
7956
|
+
"createdAt": "2026-07-03T09:15:00.000Z",
|
|
7957
|
+
"updatedAt": "2026-08-18T08:00:00.000Z",
|
|
7958
|
+
"_version": 4
|
|
7959
|
+
}
|
|
7960
|
+
],
|
|
7961
|
+
"pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
|
|
7962
|
+
}
|
|
7963
|
+
~~~
|
|
7964
|
+
|
|
7965
|
+
## Writes
|
|
7966
|
+
|
|
7967
|
+
- The reference states each operation's success status and the fields it accepts. A field you see in a read response is not automatically writable; send only what the request schema lists.
|
|
7968
|
+
- Operations marked **idempotent** in the reference can be repeated safely. For any other write, a lost response is an ambiguous outcome: read the record back before sending the write again. [Write and reconcile changes](/integrations/rest/write-and-reconcile) shows the procedure.
|
|
7969
|
+
- Bulk variants (\`…/bulk\` paths) apply one change to several identifiers. Read the operation's response schema before assuming every identifier was applied, and reconcile each one with a read after a failure.
|
|
7970
|
+
|
|
7971
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#concurrencyMarkdown}}
|
|
7972
|
+
|
|
7973
|
+
## Errors
|
|
7974
|
+
|
|
7975
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#errorEnvelopeMarkdown}}
|
|
7976
|
+
|
|
7977
|
+
For example, removing the last owner of an organization is refused like this:
|
|
7978
|
+
|
|
7979
|
+
~~~json
|
|
7980
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#errorExampleJson}}
|
|
7981
|
+
~~~
|
|
7982
|
+
|
|
7983
|
+
Branch on the HTTP status first, then on \`error.type\`, and on \`error.code\` only for refusals the operation documents by name:
|
|
7984
|
+
|
|
7985
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#errorStatusMarkdown}}
|
|
7986
|
+
|
|
7987
|
+
## Rate limits
|
|
7988
|
+
|
|
7989
|
+
The application enforces several windows at once and reports the tightest one:
|
|
7990
|
+
|
|
7991
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#rateLimitMarkdown}}
|
|
7992
|
+
|
|
7993
|
+
Back off when \`x-ratelimit-remaining\` reaches zero rather than waiting for the **429**. Parallel workers share the same counters when they share a credential.
|
|
7994
|
+
|
|
7995
|
+
## Support evidence
|
|
7996
|
+
|
|
7997
|
+
When you ask for help, quote the operation path, the HTTP status, \`error.type\`, \`error.code\` when present, and \`error.correlationId\`. That identifier joins your request to the application's logs and audit trail and contains no personal data. Never paste a credential, a bearer token or a full response body into a ticket.`,
|
|
7939
7998
|
},
|
|
7940
7999
|
{
|
|
7941
8000
|
managedPath: 'integrations/rest/send-request.md',
|
|
7942
8001
|
unitRef: 'technical-documentation:unit/send-api-request',
|
|
7943
8002
|
sourceRefs: [
|
|
7944
8003
|
'saas-technical-doc:engine-content/send-api-request',
|
|
8004
|
+
'source:companion-projection:application-connection',
|
|
7945
8005
|
'source:consumer-fact:access-api-keys',
|
|
7946
8006
|
],
|
|
7947
8007
|
markdown: `# Send your first API request
|
|
7948
8008
|
|
|
7949
|
-
|
|
7950
|
-
correct environment, the credential is accepted, the identity can see the
|
|
7951
|
-
intended organization and the response matches the published schema. Choose a
|
|
7952
|
-
read operation that has no business side effect.
|
|
8009
|
+
Ten minutes from an API key to a verified connection. The first request proves four things and nothing else: you reached the right environment, the credential is accepted, it sees the organization you expect, and you can read the response. Use a read operation that changes nothing.
|
|
7953
8010
|
|
|
7954
|
-
|
|
7955
|
-
| --- | --- |
|
|
7956
|
-
| Have the environment's server origin, one documented read operation, a dedicated credential accepted by that operation and one expected visible resource | The request returns the documented result in the intended scope, an expected refusal remains refused, and the credential can be revoked independently |
|
|
8011
|
+
## What you need
|
|
7957
8012
|
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
8013
|
+
- The **base URL** of the environment you are calling. {{APPLICATION_NAME}} publishes these:
|
|
8014
|
+
|
|
8015
|
+
{{APPLICATION_CONNECTION:baseUrls}}
|
|
8016
|
+
|
|
8017
|
+
- Your **organization identifier**, shown in the application's organization settings and present in the URL of most screens.
|
|
8018
|
+
- An **organization API key** created for this integration, following [Create and store an API key](/access-and-identity/api-keys-create-and-store). Keep it in a secret store or an environment variable, never in the request URL or a build log.
|
|
8019
|
+
|
|
8020
|
+
Give the request a name in your notes before you send it. "List the members of the Support organization, expect Alex Morgan" is a result you can check; "the call returned JSON" is not.
|
|
8021
|
+
|
|
8022
|
+
## Step 1: read your own organization
|
|
8023
|
+
|
|
8024
|
+
The safest first call reads the organization the key belongs to. It needs no other identifier and changes nothing.
|
|
8025
|
+
|
|
8026
|
+
~~~bash
|
|
8027
|
+
BASE_URL="https://api.example.com" # from the API reference's Servers list
|
|
8028
|
+
ORGANIZATION_ID="7c1f6f0a-4b1e-4d8f-9a0c-2e5b3c9d1f42" # from the application's organization settings
|
|
8029
|
+
ORGANIZATION_API_KEY="sk_org_…" # the key you created
|
|
8030
|
+
|
|
8031
|
+
curl --fail-with-body \\
|
|
8032
|
+
--url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID" \\
|
|
8033
|
+
{{CONSUMER_FACT:source:consumer-fact:access-api-keys#curlRequestHeader}} \\
|
|
8034
|
+
--header "accept: application/json"
|
|
7969
8035
|
~~~
|
|
7970
8036
|
|
|
7971
|
-
|
|
8037
|
+
Replace the three values with yours; leave the header exactly as shown. {{CONSUMER_FACT:source:consumer-fact:access-api-keys#markdown}}
|
|
7972
8038
|
|
|
7973
|
-
|
|
7974
|
-
- one documented read operation;
|
|
7975
|
-
- a dedicated API key or another credential explicitly accepted by that operation;
|
|
7976
|
-
- an organization and resource that the identity is expected to see.
|
|
8039
|
+
A successful answer is **200** with the organization record: its \`_id\` is the identifier you sent, and \`name\` is the organization you expected. Anything else, read the [failure table](#if-it-fails) below before changing more than one thing.
|
|
7977
8040
|
|
|
7978
|
-
|
|
7979
|
-
single-resource read in a test organization is usually better than a broad
|
|
7980
|
-
export. Record the expected identifier or count before calling the API so that
|
|
7981
|
-
you can distinguish “valid JSON” from the correct business result.
|
|
8041
|
+
## Step 2: read a collection
|
|
7982
8042
|
|
|
7983
|
-
|
|
7984
|
-
send the key in the standard authorization header:
|
|
8043
|
+
Now list the organization's members. This exercises the collection envelope you will meet on every list and search operation:
|
|
7985
8044
|
|
|
7986
8045
|
~~~bash
|
|
7987
|
-
curl --fail-with-body
|
|
7988
|
-
--url "
|
|
7989
|
-
|
|
8046
|
+
curl --fail-with-body \\
|
|
8047
|
+
--url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID/organization-members?limit=5&sort=joinedAt:desc" \\
|
|
8048
|
+
--header "Authorization: $ORGANIZATION_API_KEY" \\
|
|
7990
8049
|
--header "accept: application/json"
|
|
7991
8050
|
~~~
|
|
7992
8051
|
|
|
7993
|
-
|
|
7994
|
-
\`Bearer\` prefix to an API key, put credentials in the URL, or print the fully
|
|
7995
|
-
expanded command into an ordinary build log. For a user or delegated OAuth
|
|
7996
|
-
journey, use the credential scheme documented by that operation instead of
|
|
7997
|
-
sending two credentials and relying on accidental precedence.
|
|
8052
|
+
The response is \`{ "data": [ … ], "pagination": { "page": 1, "limit": 5, "total": …, "totalPages": … } }\`. Check that \`total\` matches the member count you see in the application. [Read collections](/integrations/rest/read-collections) covers paging, sorting and filters in full.
|
|
7998
8053
|
|
|
7999
|
-
##
|
|
8054
|
+
## Step 3: prove the boundary holds
|
|
8000
8055
|
|
|
8001
|
-
|
|
8056
|
+
A connection is only safe when a wrong credential is refused. Send the first request again with the key deliberately broken:
|
|
8002
8057
|
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8058
|
+
~~~bash
|
|
8059
|
+
curl --include \\
|
|
8060
|
+
--url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID" \\
|
|
8061
|
+
--header "Authorization: sk_org_not_a_real_key" \\
|
|
8062
|
+
--header "accept: application/json"
|
|
8063
|
+
~~~
|
|
8008
8064
|
|
|
8009
|
-
Then
|
|
8010
|
-
deliberately not allowed to use, without probing another customer's known
|
|
8011
|
-
identifier. The documented refusal demonstrates that scope enforcement remains
|
|
8012
|
-
present; it is as important as the successful read.
|
|
8065
|
+
Expect **401** with \`"type": "AUTHENTICATION"\` in the error body. Then send the correct key against an organization it does not belong to and expect **404**: the application does not confirm that organizations outside your scope exist. Those two refusals are the negative proof; record them beside the successful read.
|
|
8013
8066
|
|
|
8014
|
-
If
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8067
|
+
## If it fails
|
|
8068
|
+
|
|
8069
|
+
| You see | It means | Do this |
|
|
8070
|
+
| --- | --- | --- |
|
|
8071
|
+
| No response, or a TLS or DNS error | The base URL is wrong or unreachable from where you run | Compare the URL with the Servers list; test from the network the integration will run on |
|
|
8072
|
+
| **401** \`AUTHENTICATION\` | The key was not accepted | Check the header carries the full key with no \`Bearer\` prefix, that the key is \`active\`, and that it belongs to this environment |
|
|
8073
|
+
| **403** \`AUTHORIZATION\` | The key is valid but its roles do not allow this operation | Compare the roles on the key with the roles the operation lists in the reference |
|
|
8074
|
+
| **404** \`NOT_FOUND\` | The organization identifier is wrong, or this key cannot see it | Copy the identifier from the application again |
|
|
8075
|
+
| **429** \`RATE_LIMIT\` | Too many requests | Wait \`Retry-After\` seconds |
|
|
8018
8076
|
|
|
8019
|
-
|
|
8077
|
+
Every error an operation produces carries \`error.correlationId\`. Quote it when you ask for help. [Troubleshoot an API request](/integrations/rest/troubleshoot) goes deeper.
|
|
8020
8078
|
|
|
8021
|
-
|
|
8022
|
-
credential to prove the operating procedure. Only then choose one small write
|
|
8023
|
-
and define how you will detect whether it applied after a timeout.
|
|
8079
|
+
## Before your first write
|
|
8024
8080
|
|
|
8025
|
-
|
|
8026
|
-
organization, expected business result, observed status and correlation value.
|
|
8027
|
-
That is enough to reproduce the connection proof without retaining the secret
|
|
8028
|
-
or an unrestricted response payload.`,
|
|
8081
|
+
Repeat Step 1 from the environment where the integration will actually run. Then rotate the key once ([API key lifecycle](/access-and-identity/api-keys-lifecycle)) to prove that the operating procedure works before you depend on it. Only then choose one small write and read [Write and reconcile changes](/integrations/rest/write-and-reconcile).`,
|
|
8029
8082
|
},
|
|
8030
8083
|
{
|
|
8031
8084
|
managedPath: 'integrations/rest/read-collections.md',
|
|
8032
8085
|
unitRef: 'technical-documentation:unit/work-with-api-collections',
|
|
8033
|
-
sourceRefs: [
|
|
8034
|
-
|
|
8086
|
+
sourceRefs: [
|
|
8087
|
+
'saas-technical-doc:engine-content/work-with-api-collections',
|
|
8088
|
+
'source:consumer-fact:rest-conventions',
|
|
8089
|
+
],
|
|
8090
|
+
markdown: `# Read collections
|
|
8035
8091
|
|
|
8036
|
-
A collection is the set of records one operation lets
|
|
8037
|
-
current organization and access scope**. It is not automatically every record
|
|
8038
|
-
in the application. A reliable integration reads that visible set in bounded
|
|
8039
|
-
pages, processes each record safely and can resume without silently skipping or
|
|
8040
|
-
duplicating business work.
|
|
8092
|
+
A collection is the set of records one list or search operation lets your credential see in one organization. Read it in pages, sort it deliberately, filter it on the fields the operation offers, and stop where the response says to stop. Every list and search operation in the API reference uses the vocabulary below.
|
|
8041
8093
|
|
|
8042
|
-
|
|
8043
|
-
| --- | --- |
|
|
8044
|
-
| Choose one documented list or search operation, the organization in scope, the filters that express the business question and a stable way to recognize processed records | Every returned page is validated and checkpointed, completion follows the returned pagination metadata, and a repeated or resumed scan does not create a duplicate business effect |
|
|
8094
|
+
## The query vocabulary
|
|
8045
8095
|
|
|
8046
|
-
|
|
8096
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#collectionQueryMarkdown}}
|
|
8047
8097
|
|
|
8048
|
-
|
|
8049
|
-
all records” is usually too broad. Prefer a bounded outcome such as:
|
|
8098
|
+
Filter and sort fields are per operation. Each operation's entry in the API reference lists them as query parameters, so open the operation before writing the request; a filter accepted by one resource is not a convention for another.
|
|
8050
8099
|
|
|
8051
|
-
|
|
8052
|
-
- find records in a documented state that need attention;
|
|
8053
|
-
- copy the minimum fields required for a downstream report;
|
|
8054
|
-
- verify that a previous write produced the intended current state.
|
|
8100
|
+
## The response envelope
|
|
8055
8101
|
|
|
8056
|
-
|
|
8057
|
-
available filters and sort fields, pagination ceiling and required authority.
|
|
8058
|
-
**A parameter accepted by one collection is not a platform-wide convention.**
|
|
8059
|
-
Do not reuse a filter name, page size or sort order from another resource.
|
|
8102
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#paginationResponseMarkdown}}
|
|
8060
8103
|
|
|
8061
|
-
|
|
8104
|
+
An empty \`data\` array on page 1 is a valid answer. Confirm the organization and the filters before treating it as a failure.
|
|
8062
8105
|
|
|
8063
|
-
|
|
8064
|
-
the current page, limit, total count and total pages with the returned data.
|
|
8065
|
-
Process the current page successfully before advancing. Stop from returned
|
|
8066
|
-
pagination metadata; do not guess from a short page or copy a maximum size from
|
|
8067
|
-
another resource.
|
|
8106
|
+
## Walk every page
|
|
8068
8107
|
|
|
8069
|
-
~~~
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8108
|
+
~~~bash
|
|
8109
|
+
page=1
|
|
8110
|
+
while : ; do
|
|
8111
|
+
response=$(curl --fail-with-body --silent \\
|
|
8112
|
+
--url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID/organization-members?page=$page&limit=100&sort=joinedAt:asc" \\
|
|
8113
|
+
--header "Authorization: $ORGANIZATION_API_KEY" \\
|
|
8114
|
+
--header "accept: application/json")
|
|
8115
|
+
echo "$response" | jq -c '.data[]' >> members.ndjson
|
|
8116
|
+
totalPages=$(echo "$response" | jq '.pagination.totalPages')
|
|
8117
|
+
[ "$page" -ge "$totalPages" ] && break
|
|
8118
|
+
page=$((page + 1))
|
|
8119
|
+
done
|
|
8079
8120
|
~~~
|
|
8080
8121
|
|
|
8081
|
-
|
|
8122
|
+
Three rules keep a scan correct:
|
|
8082
8123
|
|
|
8083
|
-
1.
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
that the result belongs to the intended environment and organization.
|
|
8087
|
-
3. Process each record **idempotently**. Reprocessing the same stable record
|
|
8088
|
-
identifier must not create a second payment, message, account or other
|
|
8089
|
-
business effect.
|
|
8090
|
-
4. Persist a checkpoint only after the page's work is safely recorded. Keep the
|
|
8091
|
-
page number, filter and sort basis, stable identifiers or counts, and the
|
|
8092
|
-
returned pagination metadata.
|
|
8093
|
-
5. Advance only while the response says another page exists. Do not stop merely
|
|
8094
|
-
because a page contains fewer items than requested.
|
|
8095
|
-
|
|
8096
|
-
An empty first page can be a correct result. Prove it by confirming the caller,
|
|
8097
|
-
organization, filters and expected visibility rather than treating emptiness as
|
|
8098
|
-
a transport failure.
|
|
8099
|
-
|
|
8100
|
-
## Read the pagination metadata literally
|
|
8101
|
-
|
|
8102
|
-
The standard collection envelope contains the returned data plus **page**,
|
|
8103
|
-
**limit**, **total** and **totalPages**. These values describe the server's
|
|
8104
|
-
answer to that request:
|
|
8105
|
-
|
|
8106
|
-
- **page** identifies the page just returned;
|
|
8107
|
-
- **limit** is the page size applied by that operation;
|
|
8108
|
-
- **total** is the matching record count reported for the query;
|
|
8109
|
-
- **totalPages** tells the caller whether another numbered page remains.
|
|
8110
|
-
|
|
8111
|
-
Do not manufacture a next page from the number of items alone. If the operation
|
|
8112
|
-
publishes a different paging model, follow that operation instead of translating
|
|
8113
|
-
it into this one.
|
|
8114
|
-
|
|
8115
|
-
## Decide how changes during a scan are handled
|
|
8116
|
-
|
|
8117
|
-
Records may be created or changed while a multi-page scan runs. If the exact
|
|
8118
|
-
operation does not publish snapshot or cursor semantics, do not claim a
|
|
8119
|
-
point-in-time view. Make downstream processing idempotent, retain stable record
|
|
8120
|
-
identifiers and schedule a reconciliation pass appropriate to the business risk.
|
|
8121
|
-
|
|
8122
|
-
Choose the recovery posture before production:
|
|
8123
|
-
|
|
8124
|
-
- For a low-risk report, a later complete pass may be sufficient.
|
|
8125
|
-
- For a state synchronization, persist stable identifiers and compare the final
|
|
8126
|
-
downstream state with a fresh authoritative read.
|
|
8127
|
-
- For financial, access or other sensitive effects, use a documented stable
|
|
8128
|
-
ordering or change marker when available and explicitly investigate gaps.
|
|
8129
|
-
- If the scan fails halfway through, resume from the last **completed**
|
|
8130
|
-
checkpoint. Reprocessing the boundary page is safer than skipping uncertain
|
|
8131
|
-
work when processing is idempotent.
|
|
8132
|
-
|
|
8133
|
-
Never assume that page 2 still starts after the same record if concurrent
|
|
8134
|
-
changes can reorder the collection. The operation contract determines whether a
|
|
8135
|
-
stable scan is possible; the client cannot create that guarantee by remembering
|
|
8136
|
-
only a page number.
|
|
8137
|
-
|
|
8138
|
-
## Verify completion
|
|
8139
|
-
|
|
8140
|
-
Compare the processed count and checkpoints with the final returned metadata,
|
|
8141
|
-
then sample a small number of recognizable records in the intended organization.
|
|
8142
|
-
Run one controlled repeat and confirm it produces no duplicate downstream
|
|
8143
|
-
effect. If records can change during the pass, run the planned reconciliation
|
|
8144
|
-
and explain any difference rather than forcing the counts to appear equal.
|
|
8145
|
-
|
|
8146
|
-
Retain the operation identity, environment, organization, filter and sort basis,
|
|
8147
|
-
start/end time, pages processed, final pagination metadata and non-sensitive
|
|
8148
|
-
checkpoint. Do not log full result sets merely to prove the scan ran; store the
|
|
8149
|
-
minimum identifiers and counts needed to investigate omissions or duplicates.
|
|
8124
|
+
1. **Sort by a field the reference lists as sortable, or a stable timestamp** such as \`joinedAt:asc\` for members, when you walk more than one page. A sort field the operation does not accept is ignored, not refused, and the default order applies; sorting by a field that changes during the scan (a status, a name being edited) can move a record between pages and make you skip or repeat it.
|
|
8125
|
+
2. **Stop on \`totalPages\`**, not on a short page. The last page is short by definition; an earlier page is short only when records were deleted while you scanned.
|
|
8126
|
+
3. **Process each record idempotently** and key your own records on \`_id\`. If the scan is interrupted, resume from the last page whose work you completed; repeating a page is safe when processing is idempotent, skipping one never is.
|
|
8150
8127
|
|
|
8151
|
-
##
|
|
8128
|
+
## Search and filter
|
|
8152
8129
|
|
|
8153
|
-
|
|
8154
|
-
turning collection results into mutations.
|
|
8155
|
-
- [Troubleshoot an API request](/integrations/rest/troubleshoot) when one page
|
|
8156
|
-
fails or visibility differs from the expected organization scope.
|
|
8157
|
-
- Return to [REST API](/integrations/rest-api) to review the request, identity,
|
|
8158
|
-
organization and operation boundaries.`,
|
|
8159
|
-
},
|
|
8160
|
-
{
|
|
8161
|
-
managedPath: 'integrations/rest/write-and-reconcile.md',
|
|
8162
|
-
unitRef: 'technical-documentation:unit/write-and-reconcile-api-changes',
|
|
8163
|
-
sourceRefs: ['saas-technical-doc:engine-content/write-and-reconcile-api-changes'],
|
|
8164
|
-
markdown: `# Write and reconcile API changes
|
|
8130
|
+
Search operations (\`…/search\`) add \`q\` for free text. Combine it with filters and sorting:
|
|
8165
8131
|
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8132
|
+
~~~bash
|
|
8133
|
+
curl --fail-with-body \\
|
|
8134
|
+
--url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID/organization-members/search?q=morgan&status=ACTIVE&sort=joinedAt:desc" \\
|
|
8135
|
+
--header "Authorization: $ORGANIZATION_API_KEY" \\
|
|
8136
|
+
--header "accept: application/json"
|
|
8137
|
+
~~~
|
|
8169
8138
|
|
|
8170
|
-
|
|
8171
|
-
| --- | --- |
|
|
8172
|
-
| Choose one documented write, a test organization, a least-privilege identity, the expected current state and a stable way to recognize the intended business change | One controlled change reaches the expected state, an unauthorized or invalid change remains refused, and a lost response can be reconciled without creating a duplicate effect |
|
|
8139
|
+
Date-range filters are objects and use bracket notation, one key per bound:
|
|
8173
8140
|
|
|
8174
|
-
|
|
8141
|
+
~~~text
|
|
8142
|
+
?createdAt[startDate]=2026-09-01T00:00:00Z&createdAt[endDate]=2026-09-30T23:59:59Z
|
|
8143
|
+
~~~
|
|
8175
8144
|
|
|
8176
|
-
|
|
8177
|
-
identifier lets you recognize the same intended change later. Use a documented
|
|
8178
|
-
idempotency contract when the operation publishes one. Otherwise, prevent
|
|
8179
|
-
concurrent duplicate work in your own system and reconcile from authoritative
|
|
8180
|
-
state after uncertainty.
|
|
8145
|
+
Send instants in ISO 8601 with a timezone. An unparseable bound answers **400** with the field named in \`error.validationErrors\`.
|
|
8181
8146
|
|
|
8182
|
-
|
|
8147
|
+
## Counts and summaries
|
|
8183
8148
|
|
|
8184
|
-
|
|
8185
|
-
- the **intended business change**, which should happen once;
|
|
8186
|
-
- the **resulting application record**, which proves the current state.
|
|
8149
|
+
Beside the full list, most collections expose \`…/summary\`, which answers the fields the application uses for pickers and tables and is smaller and faster than the full record. Some also expose \`…/count\`, which answers only the total without loading records. The API reference shows which variants an operation has. Use them for dashboards and reconciliation counts; use the full list only when you need every field.
|
|
8187
8150
|
|
|
8188
|
-
|
|
8189
|
-
documents one. A locally generated identifier is still useful for your logs and
|
|
8190
|
-
work queue, but it does not make the server deduplicate the request.
|
|
8151
|
+
## Reconcile a long scan
|
|
8191
8152
|
|
|
8192
|
-
|
|
8153
|
+
Records can change while a scan runs. When the scan feeds a report, a later complete pass corrects it. When it feeds a synchronization, keep the \`_id\` and \`_version\` of each record you processed and compare them with a fresh read before you overwrite anything downstream; \`_version\` increases on every write, so a changed value tells you the record moved.
|
|
8193
8154
|
|
|
8194
|
-
|
|
8195
|
-
pre-change state.
|
|
8196
|
-
2. Validate the request locally against the published schema. Send only fields
|
|
8197
|
-
the write accepts; a field returned by a read is not automatically writable.
|
|
8198
|
-
3. Confirm the caller, organization and role immediately before the mutation.
|
|
8199
|
-
Use a dedicated workload identity rather than a person's broad credential.
|
|
8200
|
-
4. Send the change once and retain the status, stable error code and correlation
|
|
8201
|
-
identifier without retaining the credential or unrestricted payload.
|
|
8202
|
-
5. Read the authoritative resource again. Verify the intended state and scope,
|
|
8203
|
-
not only the HTTP success response.
|
|
8204
|
-
6. Exercise one expected refusal—for example insufficient authority or an
|
|
8205
|
-
invalid transition—and confirm that authoritative state did not change.
|
|
8155
|
+
## When a page fails
|
|
8206
8156
|
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
Ambiguous --> SafeToRetry: authoritative state shows not applied
|
|
8218
|
-
SafeToRetry --> Sent
|
|
8219
|
-
Confirmed --> [*]
|
|
8220
|
-
Refused --> [*]
|
|
8221
|
-
~~~
|
|
8157
|
+
A failed page answers with the same error envelope as any request; the status table in [REST API conventions](/integrations/rest/conventions#errors) says what each status means. A **429** carries \`Retry-After\`; wait that long before resuming from the same page. Do not resume from page 1.`,
|
|
8158
|
+
},
|
|
8159
|
+
{
|
|
8160
|
+
managedPath: 'integrations/rest/write-and-reconcile.md',
|
|
8161
|
+
unitRef: 'technical-documentation:unit/write-and-reconcile-api-changes',
|
|
8162
|
+
sourceRefs: [
|
|
8163
|
+
'saas-technical-doc:engine-content/write-and-reconcile-api-changes',
|
|
8164
|
+
'source:consumer-fact:rest-conventions',
|
|
8165
|
+
],
|
|
8166
|
+
markdown: `# Write and reconcile changes
|
|
8222
8167
|
|
|
8223
|
-
|
|
8224
|
-
yet classify the business outcome. It is not success, failure or permission to
|
|
8225
|
-
send the mutation again.
|
|
8168
|
+
A write is finished when you know what the application now contains, not when the HTTP call returns. This page shows how to make a change once, how to refuse to overwrite someone else's change, and what to do when the response never arrives.
|
|
8226
8169
|
|
|
8227
|
-
##
|
|
8170
|
+
## Send the change
|
|
8228
8171
|
|
|
8229
|
-
|
|
8172
|
+
1. Open the operation in the API reference and copy its request schema. Send only the fields it lists; a field you saw in a read is not necessarily writable.
|
|
8173
|
+
2. Read the record first and keep its \`_version\`.
|
|
8174
|
+
3. Send the write with the version in the \`if-match\` header.
|
|
8175
|
+
4. Read the record again and compare the fields you changed with what you intended. A **200** proves the application accepted the request; the second read proves the business result.
|
|
8230
8176
|
|
|
8231
|
-
|
|
8232
|
-
2. Read the target through the documented authoritative operation.
|
|
8233
|
-
3. Compare its current state with both the pre-change state and intended result.
|
|
8234
|
-
4. If the result is present, record the original attempt as applied even though
|
|
8235
|
-
its response was lost.
|
|
8236
|
-
5. If the result is absent and the read is conclusive, allow one bounded retry
|
|
8237
|
-
under the same business-change identity.
|
|
8238
|
-
6. If the state is conflicting or visibility is insufficient, route the item
|
|
8239
|
-
for human reconciliation instead of guessing.
|
|
8177
|
+
Updating a member's roles, with optimistic locking:
|
|
8240
8178
|
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8179
|
+
~~~bash
|
|
8180
|
+
curl --fail-with-body \\
|
|
8181
|
+
--request PUT \\
|
|
8182
|
+
--url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID/organization-members/$MEMBER_ID" \\
|
|
8183
|
+
--header "Authorization: $ORGANIZATION_API_KEY" \\
|
|
8184
|
+
--header "content-type: application/json" \\
|
|
8185
|
+
--header "if-match: 4" \\
|
|
8186
|
+
--data '{ "roles": ["ORG_MANAGER"] }'
|
|
8187
|
+
~~~
|
|
8244
8188
|
|
|
8245
|
-
|
|
8189
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#concurrencyMarkdown}}
|
|
8246
8190
|
|
|
8247
|
-
-
|
|
8248
|
-
- **401 or 403:** repair identity or authority; more retries cannot grant access.
|
|
8249
|
-
- **404:** check identifier and visible organization scope before assuming absence.
|
|
8250
|
-
- **409:** read current state and resolve the business conflict.
|
|
8251
|
-
- **429 or retryable server failure:** follow the documented delay and use
|
|
8252
|
-
bounded backoff.
|
|
8253
|
-
- **Timeout or connection loss:** read authoritative state before deciding
|
|
8254
|
-
whether another write is safe.
|
|
8191
|
+
Without \`if-match\` the write applies unconditionally, last writer wins. Use the header whenever a person or another integration can touch the same record.
|
|
8255
8192
|
|
|
8256
|
-
|
|
8257
|
-
target the wrong organization or apply a broader role than intended. Always
|
|
8258
|
-
compare the resulting resource with the approved change, not merely with the
|
|
8259
|
-
response schema.
|
|
8193
|
+
## Read the refusal
|
|
8260
8194
|
|
|
8261
|
-
|
|
8195
|
+
A write the application will not perform answers with the standard error envelope. Three cases matter for a writer:
|
|
8262
8196
|
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8197
|
+
| Status | \`error.type\` | What happened | What to do |
|
|
8198
|
+
| --- | --- | --- | --- |
|
|
8199
|
+
| **400** | \`VALIDATION\` | The body breaks the schema; \`error.validationErrors\` names each field | Fix the request. Resending it unchanged fails again |
|
|
8200
|
+
| **409** | \`CONFLICT\` | A stale \`if-match\`, a uniqueness rule, or a state transition the record forbids | Read the record, then decide: merge and resend, or stop because the change no longer applies |
|
|
8201
|
+
| **422** | \`BUSINESS_RULE\` | The request is well-formed but a domain rule refuses it; \`error.customMessageReference\` names the rule | Only a different request, or a different state, can succeed |
|
|
8268
8202
|
|
|
8269
|
-
|
|
8203
|
+
For example, removing the last owner of an organization answers **409** with \`error.code\` set to \`LAST_ORGANIZATION_OWNER\`: grant owner access to another member first, then retry. The [REST API conventions](/integrations/rest/conventions#errors) page lists every status and error type.
|
|
8270
8204
|
|
|
8271
|
-
|
|
8272
|
-
status, stable error code and correlation identifier. Redact credentials,
|
|
8273
|
-
personal data and restricted request or response fields. Also retain the
|
|
8274
|
-
non-sensitive before/after proof needed to show whether the business change was
|
|
8275
|
-
applied, refused or reconciled.
|
|
8205
|
+
## Recover from a lost response
|
|
8276
8206
|
|
|
8277
|
-
|
|
8278
|
-
intend, what did the application finally contain, and why was another attempt
|
|
8279
|
-
safe or refused?**
|
|
8207
|
+
A timeout, a dropped connection or a crash between sending and reading the answer leaves the outcome unknown. Do not resend on reflex; the application may already have applied the change.
|
|
8280
8208
|
|
|
8281
|
-
|
|
8209
|
+
1. Stop automatic retries for this change.
|
|
8210
|
+
2. Read the target record. For a create, search for it by the business key you sent (an email, a reference number, a name).
|
|
8211
|
+
3. If the change is there, treat the original attempt as applied and continue.
|
|
8212
|
+
4. If it is absent, send it once more. Include the same \`if-match\` value you used the first time when the record existed before; a **409** now means something else changed in between.
|
|
8213
|
+
5. If the state is neither what you expected before nor after, hand the item to a person with the request you sent, the record you read and both timestamps.
|
|
8214
|
+
|
|
8215
|
+
Operations marked **idempotent** in the API reference can be resent without this procedure. Everything else needs it.
|
|
8216
|
+
|
|
8217
|
+
## Bulk changes
|
|
8218
|
+
|
|
8219
|
+
Bulk variants (\`…/bulk\` paths) take a list of identifiers and apply one change to each. Read the operation's response schema in the API reference before assuming that every identifier was applied, and after a failure reconcile each identifier individually with a read. Do not resend the whole batch because one item failed.
|
|
8282
8220
|
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
-
|
|
8286
|
-
refusal, conflict or transport failure without destroying evidence.
|
|
8287
|
-
- Return to [REST API](/integrations/rest-api) to review the shared authority and
|
|
8288
|
-
scope boundaries.`,
|
|
8221
|
+
## Keep the right evidence
|
|
8222
|
+
|
|
8223
|
+
For each change keep the operation path, the identifiers, the \`if-match\` value, the status, \`error.type\` and \`error.code\` when refused, and \`error.correlationId\`. That is enough to answer "what did we intend, what does the application contain, and why was a second attempt safe or refused". Never store the credential or the full request body next to it.`,
|
|
8289
8224
|
},
|
|
8290
8225
|
{
|
|
8291
8226
|
managedPath: 'integrations/rest/troubleshoot.md',
|
|
8292
8227
|
unitRef: 'technical-documentation:unit/troubleshoot-api-request',
|
|
8293
|
-
sourceRefs: [
|
|
8228
|
+
sourceRefs: [
|
|
8229
|
+
'saas-technical-doc:engine-content/troubleshoot-api-request',
|
|
8230
|
+
'source:consumer-fact:access-api-keys',
|
|
8231
|
+
'source:consumer-fact:rest-conventions',
|
|
8232
|
+
],
|
|
8294
8233
|
markdown: `# Troubleshoot an API request
|
|
8295
8234
|
|
|
8296
|
-
|
|
8297
|
-
changing organizations and retrying at the same time destroys the evidence that
|
|
8298
|
-
would distinguish an authentication problem from an authorization or data
|
|
8299
|
-
problem.
|
|
8235
|
+
Start from what the application returned: the HTTP status and the error body. Together they name the one boundary that failed. Change one thing, resend, and keep the first failure's evidence until the fix is proven.
|
|
8300
8236
|
|
|
8301
|
-
|
|
8302
|
-
| --- | --- |
|
|
8303
|
-
| Preserve one failed attempt's time, environment, operation, caller reference, organization, status and correlation evidence | The failure is assigned to one boundary, the smallest safe correction is tested, and any uncertain write is reconciled before another attempt |
|
|
8237
|
+
## Read the status and the error body
|
|
8304
8238
|
|
|
8305
|
-
|
|
8239
|
+
Every failed request answers with the same envelope. The two fields to read first are \`error.type\` and, when present, \`error.code\`:
|
|
8306
8240
|
|
|
8307
|
-
|
|
8308
|
-
credential, alter the request and change the target environment simultaneously.
|
|
8309
|
-
Confirm whether the request received an HTTP response at all, then classify the
|
|
8310
|
-
first failing boundary.
|
|
8241
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#errorEnvelopeMarkdown}}
|
|
8311
8242
|
|
|
8312
|
-
|
|
8313
|
-
flowchart TD
|
|
8314
|
-
accTitle: Diagnose an API request without hiding the original failure
|
|
8315
|
-
accDescr: The operator preserves evidence, separates transport from HTTP response, then checks request contract, authentication, authority, visibility, conflict or server failure before applying one bounded correction and re-proving the business result.
|
|
8316
|
-
failure["Preserve one failed attempt"] --> response{"HTTP response received?"}
|
|
8317
|
-
response -->|No| transport["Check environment, DNS, TLS and timeout"]
|
|
8318
|
-
response -->|Yes| class{"Which response class?"}
|
|
8319
|
-
class -->|400| contract["Validate exact operation contract"]
|
|
8320
|
-
class -->|401| identity["Validate credential and environment"]
|
|
8321
|
-
class -->|403 or 404| scope["Validate authority, organization and visibility"]
|
|
8322
|
-
class -->|409| conflict["Read and reconcile current state"]
|
|
8323
|
-
class -->|429 or 5xx| retry["Follow documented delay and safety rule"]
|
|
8324
|
-
transport --> prove["Apply one correction and repeat the proof"]
|
|
8325
|
-
contract --> prove
|
|
8326
|
-
identity --> prove
|
|
8327
|
-
scope --> prove
|
|
8328
|
-
conflict --> prove
|
|
8329
|
-
retry --> prove
|
|
8330
|
-
~~~
|
|
8243
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#errorStatusMarkdown}}
|
|
8331
8244
|
|
|
8332
|
-
##
|
|
8245
|
+
## No HTTP response at all
|
|
8333
8246
|
|
|
8334
|
-
|
|
8335
|
-
| --- | --- | --- |
|
|
8336
|
-
| No HTTP response | Network, DNS, TLS or server availability | Target environment, resolved host, TLS result and timeout |
|
|
8337
|
-
| **400** or validation error | Request does not match the operation contract | Content type, required fields, enum values and parameter placement |
|
|
8338
|
-
| **401** | Credential was not accepted | Header transport, expiry, revocation, credential type and environment |
|
|
8339
|
-
| **403** | Identity lacks authority for the operation | Active organization, membership, role and operation requirements |
|
|
8340
|
-
| **404** | Resource is absent or invisible in this scope | Identifier, organization and resource visibility |
|
|
8341
|
-
| **409** | Current state conflicts with the requested transition | Read authoritative state and resolve the business conflict |
|
|
8342
|
-
| **429** | Caller exceeded an enforced limit | Stop immediate retries and follow the documented delay |
|
|
8343
|
-
| **5xx** | Server could not complete the request | Preserve correlation evidence; retry only when the operation is safe |
|
|
8247
|
+
A connection refusal, a DNS failure, a TLS error and a client timeout are different problems, and none of them is an application answer.
|
|
8344
8248
|
|
|
8345
|
-
|
|
8249
|
+
- Compare the base URL with the **Servers** list in the API reference; a URL copied from another environment is the usual cause.
|
|
8250
|
+
- Test from the network the integration runs on, not only from a laptop.
|
|
8251
|
+
- A timeout on a read can be repeated. A timeout on a write is an ambiguous outcome: follow [Recover from a lost response](/integrations/rest/write-and-reconcile#recover-from-a-lost-response) before resending.
|
|
8346
8252
|
|
|
8347
|
-
|
|
8253
|
+
## 401: the credential was not accepted
|
|
8348
8254
|
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
status. Do not switch to a different environment merely to obtain a response.
|
|
8255
|
+
- The \`Authorization\` header must carry the whole key, with no \`Bearer\` prefix in front of an API key and no key inside a URL or a cookie. {{CONSUMER_FACT:source:consumer-fact:access-api-keys#markdown}}
|
|
8256
|
+
- Check the key's status in the application: an \`inactive\` or \`expired\` key is refused. [API key lifecycle](/access-and-identity/api-keys-lifecycle) explains each status.
|
|
8257
|
+
- Check the environment: a key minted in one environment does not work in another.
|
|
8353
8258
|
|
|
8354
|
-
|
|
8259
|
+
Do not fix a 401 by borrowing an administrator's key. That hides the defect and widens what the integration can do.
|
|
8355
8260
|
|
|
8356
|
-
|
|
8357
|
-
exact operation in the API reference. Validate the request before sending it
|
|
8358
|
-
again. Do not remove unknown fields one at a time in production; build a minimal
|
|
8359
|
-
schema-valid reproduction in a safe organization.
|
|
8261
|
+
## 403: valid credential, refused operation
|
|
8360
8262
|
|
|
8361
|
-
|
|
8263
|
+
The application knows who is calling and refuses this operation in this scope.
|
|
8362
8264
|
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
defect and expands the blast radius.
|
|
8265
|
+
- Compare the roles on the key with the roles the operation lists in the API reference.
|
|
8266
|
+
- A collection under an organization the key is not a member of is refused with 403; confirm the organization identifier.
|
|
8267
|
+
- \`FEATURE_NOT_AVAILABLE\` and \`FEATURE_LIMIT_EXCEEDED\` are plan refusals, not role refusals; see [Plans and access](/billing-and-subscriptions/plans-and-access).
|
|
8367
8268
|
|
|
8368
|
-
|
|
8269
|
+
## 404: the record is not visible here
|
|
8369
8270
|
|
|
8370
|
-
|
|
8371
|
-
scope. Confirm active organization membership, role and operation authority.
|
|
8372
|
-
For **404**, confirm the identifier and organization visibility before deciding
|
|
8373
|
-
the record does not exist; an inaccessible record can intentionally be
|
|
8374
|
-
indistinguishable from a missing one.
|
|
8271
|
+
Either the identifier is wrong or the record belongs to an organization this key cannot see. The application does not distinguish the two on purpose. Copy the identifier from the application again and confirm the organization in the path.
|
|
8375
8272
|
|
|
8376
|
-
|
|
8273
|
+
## 409 and 422: the current state refuses the change
|
|
8377
8274
|
|
|
8378
|
-
|
|
8379
|
-
honour the documented delay and stop parallel callers from immediately filling
|
|
8380
|
-
the same limit again. For **5xx** or transport loss, determine whether the
|
|
8381
|
-
operation is safe to repeat. A read can usually be repeated; a write first needs
|
|
8382
|
-
authoritative reconciliation.
|
|
8275
|
+
Read the record, then read \`error.code\` and \`error.customMessageReference\`. A stale \`if-match\` carries \`error.details.versionConflict\` with the current version. [Write and reconcile changes](/integrations/rest/write-and-reconcile) shows what to do for each case.
|
|
8383
8276
|
|
|
8384
|
-
##
|
|
8277
|
+
## 429: too many requests
|
|
8385
8278
|
|
|
8386
|
-
|
|
8387
|
-
organization, status, stable error code and correlation identifier. Replace
|
|
8388
|
-
payload values with a minimal redacted reproduction. Never paste an API key,
|
|
8389
|
-
Bearer token or unrestricted personal data into a ticket.
|
|
8279
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#rateLimitMarkdown}}
|
|
8390
8280
|
|
|
8391
|
-
|
|
8392
|
-
repeating it. The absence of a client response is an **ambiguous outcome**, not
|
|
8393
|
-
an automatic retry instruction.
|
|
8281
|
+
Wait the full \`Retry-After\`, then resume where you stopped. If several workers share one key they share its counters; spread the load or use one key per worker.
|
|
8394
8282
|
|
|
8395
|
-
##
|
|
8283
|
+
## 5xx: the application failed
|
|
8396
8284
|
|
|
8397
|
-
|
|
8398
|
-
success and one expected refusal, then verify the business result in the
|
|
8399
|
-
intended organization. If the correction involved a credential or permission,
|
|
8400
|
-
also confirm that access outside the approved scope remains refused.
|
|
8285
|
+
Keep \`error.correlationId\`, the operation path and the time. Reads can be retried with a short back-off. For a write, read the record before resending.
|
|
8401
8286
|
|
|
8402
|
-
|
|
8403
|
-
the correlation identifier and what authoritative state currently shows. State
|
|
8404
|
-
whether a write may already have applied. That gives support enough context to
|
|
8405
|
-
investigate without asking for credentials or a full personal-data payload.
|
|
8287
|
+
## Prove the fix
|
|
8406
8288
|
|
|
8407
|
-
|
|
8289
|
+
Resend the smallest request with exactly one change. Then run the two negative checks from [Send your first API request](/integrations/rest/send-request#step-3-prove-the-boundary-holds) again: a wrong key is still refused, and another organization is still invisible. A fix that opened the boundary is not a fix.
|
|
8408
8290
|
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
retrying a mutation with an uncertain outcome.
|
|
8413
|
-
- Return to [REST API](/integrations/rest-api) to review the four shared
|
|
8414
|
-
integration boundaries.`,
|
|
8291
|
+
## Ask for help with the right evidence
|
|
8292
|
+
|
|
8293
|
+
Quote the operation path, the HTTP status, \`error.type\`, \`error.code\`, \`error.correlationId\` and the timestamp. Say whether a write may already have applied. Never paste the key, a bearer token or a full response body.`,
|
|
8415
8294
|
},
|
|
8416
8295
|
{
|
|
8417
8296
|
managedPath: 'integrations/webhooks.md',
|
|
8418
8297
|
unitRef: 'technical-documentation:unit/webhooks',
|
|
8419
|
-
sourceRefs: [
|
|
8298
|
+
sourceRefs: [
|
|
8299
|
+
'saas-technical-doc:engine-content/webhooks',
|
|
8300
|
+
'source:companion-projection:application-integration',
|
|
8301
|
+
'source:consumer-fact:outbound-webhooks',
|
|
8302
|
+
],
|
|
8420
8303
|
markdown: `# Webhooks
|
|
8421
8304
|
|
|
8422
|
-
|
|
8423
|
-
an event. The application sends a signed HTTP request to your receiver. Your
|
|
8424
|
-
receiver decides whether the request is authentic, applies the business effect
|
|
8425
|
-
once and returns an HTTP result.
|
|
8305
|
+
A webhook lets the application call your system when something happens, instead of your system polling for it. Each event becomes one signed \`POST\` to every enabled endpoint you configured. Your receiver verifies the signature, records the delivery once, answers quickly, and does the real work afterwards.
|
|
8426
8306
|
|
|
8427
|
-
|
|
8428
|
-
| --- | --- |
|
|
8429
|
-
| Know which published event starts the work, who owns the receiving system, where its public HTTPS endpoint runs and how duplicate work will be prevented | Authentic deliveries are accepted quickly, each event produces the business effect at most once, and failed or ambiguous work can be reconciled |
|
|
8307
|
+
## What the application sends
|
|
8430
8308
|
|
|
8431
|
-
|
|
8309
|
+
- **One \`POST\` per event per endpoint.** The body is the same JSON the operation that fired the event returns to an API caller, so the record you receive has the shape documented for that operation in the [API reference](/api).
|
|
8310
|
+
- **A signature in every request.** {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#signatureMarkdown}}
|
|
8311
|
+
- **Retries on your behalf.** {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#retryMarkdown}}
|
|
8432
8312
|
|
|
8433
|
-
|
|
8434
|
-
| --- | --- | --- |
|
|
8435
|
-
| Another system must react soon after a published event | Webhook | The application initiates a delivery instead of making the receiver poll |
|
|
8436
|
-
| Another system needs the current state now | [REST API](/integrations/rest-api) | A request retrieves authoritative state at the moment it is needed |
|
|
8437
|
-
| The receiver must recover after missing or delaying an event | Webhook plus REST reconciliation | The event starts the work; an authoritative read confirms the final state |
|
|
8438
|
-
| No published event represents the business change | REST or a different supported integration | A webhook endpoint cannot invent application events |
|
|
8439
|
-
|
|
8440
|
-
A webhook is a **notification**, not a remote command and not a permanent copy
|
|
8441
|
-
of application state. Design the receiver so the event starts bounded work and
|
|
8442
|
-
the application remains the authority for facts that can change afterwards.
|
|
8443
|
-
|
|
8444
|
-
## Understand the records involved
|
|
8445
|
-
|
|
8446
|
-
- An **endpoint** identifies the receiver, its enabled state and the events it
|
|
8447
|
-
is configured to receive.
|
|
8448
|
-
- An **event** is the notification payload produced by one application
|
|
8449
|
-
operation. One notification can create a separate delivery for each matching
|
|
8450
|
-
endpoint.
|
|
8451
|
-
- A **delivery** tracks that notification for one endpoint and carries the
|
|
8452
|
-
signed identity the receiver uses for deduplication.
|
|
8453
|
-
- A **delivery attempt** records one HTTP exchange and its result. Retries are
|
|
8454
|
-
additional attempts for the same delivery, not new business events.
|
|
8455
|
-
|
|
8456
|
-
This distinction is why the receiver deduplicates with the signed delivery
|
|
8457
|
-
identity and why operators investigate delivery state separately from the
|
|
8458
|
-
downstream business effect.
|
|
8313
|
+
## The events this application publishes
|
|
8459
8314
|
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
accDescr: The application signs the raw body and sends it to the receiver. The receiver verifies the token and body hash, atomically deduplicates the delivery identifier, queues the business work, and returns a success response.
|
|
8464
|
-
participant App as Application
|
|
8465
|
-
participant Receiver as Your receiver
|
|
8466
|
-
participant Store as Deduplication store
|
|
8467
|
-
participant Worker as Your worker
|
|
8468
|
-
App->>Receiver: POST raw body plus signature
|
|
8469
|
-
Receiver->>Receiver: Verify ES256 token, claims, expiry and raw-body SHA-256
|
|
8470
|
-
Receiver->>Store: Atomically claim signed delivery identifier jti
|
|
8471
|
-
Store-->>Receiver: New or already processed
|
|
8472
|
-
Receiver->>Worker: Queue new business work
|
|
8473
|
-
Receiver-->>App: 2xx after safe acceptance
|
|
8474
|
-
~~~
|
|
8315
|
+
{{APPLICATION_INTEGRATION:webhookEvents}}
|
|
8316
|
+
|
|
8317
|
+
## What you configure
|
|
8475
8318
|
|
|
8476
|
-
|
|
8477
|
-
response means the application observed a 2xx response; it does not independently
|
|
8478
|
-
prove what checks your receiver performed.
|
|
8319
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#endpointMarkdown}}
|
|
8479
8320
|
|
|
8480
|
-
|
|
8321
|
+
Which operations fire an event is decided by the application, per resource and operation; the \`resourceIdentifier\` and \`operationIdentifier\` claims on each delivery tell you which one did. There is one configuration per organization, managed by organization administrators, and one for the application as a whole, managed by application administrators.
|
|
8481
8322
|
|
|
8482
|
-
|
|
8323
|
+
## What your receiver must do
|
|
8324
|
+
|
|
8325
|
+
| Step | Why it cannot be skipped |
|
|
8483
8326
|
| --- | --- |
|
|
8484
|
-
|
|
|
8485
|
-
|
|
|
8486
|
-
|
|
|
8487
|
-
|
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
- [Configure and test an endpoint](/integrations/webhooks/configure-and-test)
|
|
8497
|
-
before enabling production delivery.
|
|
8498
|
-
- [Verify a webhook delivery](/integrations/webhooks/verify-delivery) using the
|
|
8499
|
-
raw bytes, public key and signed claims.
|
|
8500
|
-
- [Operate webhook deliveries](/integrations/webhooks/operate-deliveries) with
|
|
8501
|
-
fast acknowledgement, durable work and reconciliation.
|
|
8502
|
-
- [Troubleshoot webhook deliveries](/integrations/webhooks/troubleshoot) from
|
|
8503
|
-
delivery state and response meaning.
|
|
8504
|
-
|
|
8505
|
-
The event and API references remain authoritative for the exact events,
|
|
8506
|
-
payloads and configuration operations exposed by this application. Before
|
|
8507
|
-
production, prove a valid delivery, an invalid signature, a duplicate delivery,
|
|
8508
|
-
a receiver timeout and recovery from a failed downstream effect.`,
|
|
8327
|
+
| Keep the raw request bytes | The signature covers the exact bytes; a JSON parser that reformats them breaks the check |
|
|
8328
|
+
| Verify the token and the body hash before reading the body | Anything before verification is an unauthenticated write into your system |
|
|
8329
|
+
| Claim \`jti\` atomically before doing work | Retries carry the same \`jti\`; two concurrent attempts must produce one effect |
|
|
8330
|
+
| Answer within the timeout, then do the work | A slow receiver is retried, then marked failed, while the work may have half-run |
|
|
8331
|
+
| Reconcile against the application for anything money- or access-related | A \`delivered\` row proves a 2xx, not that your worker finished |
|
|
8332
|
+
|
|
8333
|
+
## The four pages of this section
|
|
8334
|
+
|
|
8335
|
+
1. [Configure and test an endpoint](/integrations/webhooks/configure-and-test): register a receiver, run the built-in test, enable it.
|
|
8336
|
+
2. [Verify a webhook delivery](/integrations/webhooks/verify-delivery): the signature, the claims and a working receiver in Node.js.
|
|
8337
|
+
3. [Operate webhook deliveries](/integrations/webhooks/operate-deliveries): delivery statuses, the retry ladder, monitoring and manual retry.
|
|
8338
|
+
4. [Troubleshoot webhook deliveries](/integrations/webhooks/troubleshoot): from a failed or missing delivery to the boundary that broke.`,
|
|
8509
8339
|
},
|
|
8510
8340
|
{
|
|
8511
8341
|
managedPath: 'integrations/webhooks/configure-and-test.md',
|
|
8512
8342
|
unitRef: 'technical-documentation:unit/configure-and-test-webhook-endpoint',
|
|
8513
|
-
sourceRefs: [
|
|
8343
|
+
sourceRefs: [
|
|
8344
|
+
'saas-technical-doc:engine-content/configure-and-test-webhook-endpoint',
|
|
8345
|
+
'source:consumer-fact:outbound-webhooks',
|
|
8346
|
+
],
|
|
8514
8347
|
markdown: `# Configure and test a webhook endpoint
|
|
8515
8348
|
|
|
8516
|
-
|
|
8517
|
-
and can return quickly. Use HTTPS for production. The endpoint URL must not
|
|
8518
|
-
contain embedded credentials, and the application does not follow redirects.
|
|
8519
|
-
|
|
8520
|
-
This task is for the administrator who controls the application endpoint and
|
|
8521
|
-
the engineer who controls the receiving service. Complete it together: a green
|
|
8522
|
-
HTTP result proves reachability, while the receiver's own evidence proves that
|
|
8523
|
-
it verified the signed request before acknowledging it.
|
|
8524
|
-
|
|
8525
|
-
| Before you begin | Successful result |
|
|
8526
|
-
| --- | --- |
|
|
8527
|
-
| Choose one organization or application scope, one final HTTPS receiver URL, one published event and an owner for receiver incidents; prepare signature verification, deduplication and durable queueing | A disabled endpoint accepts a signed synthetic test, rejects an invalid request, then processes one controlled real event exactly once after staged enablement |
|
|
8528
|
-
|
|
8529
|
-
## Choose the endpoint boundary
|
|
8349
|
+
Register the receiver disabled, prove it with the built-in test, then enable it. This page is shared by the administrator who owns the application's webhook settings and the engineer who owns the receiver; each has half of the evidence.
|
|
8530
8350
|
|
|
8531
|
-
|
|
8532
|
-
enablement, ownership or recovery. Give it a description that identifies the
|
|
8533
|
-
receiving system and environment without putting credentials or personal data
|
|
8534
|
-
in the label. The stored endpoint identifier remains the correlation point for
|
|
8535
|
-
delivery history even if its URL changes later.
|
|
8351
|
+
## Before you register
|
|
8536
8352
|
|
|
8537
|
-
|
|
8538
|
-
route that handles the request: **3xx redirects are terminal failures**, so a
|
|
8539
|
-
login redirect, HTTP-to-HTTPS redirect or trailing-slash rewrite prevents a
|
|
8540
|
-
successful delivery.
|
|
8353
|
+
The receiver must:
|
|
8541
8354
|
|
|
8542
|
-
|
|
8355
|
+
- be reachable from the internet over HTTPS at its final URL. The application does not follow redirects, so an HTTP-to-HTTPS redirect, a login page or a trailing-slash rewrite ends every delivery as failed;
|
|
8356
|
+
- not carry credentials in the URL and not resolve to a private or loopback address; such URLs are refused with the \`not_sent\` outcome;
|
|
8357
|
+
- keep the raw request bytes, read the signature from the header and verify it before parsing. [Verify a webhook delivery](/integrations/webhooks/verify-delivery) has a complete receiver you can start from;
|
|
8358
|
+
- store the delivery identifier \`jti\` before doing any work, so a retry cannot repeat the work.
|
|
8543
8359
|
|
|
8544
|
-
|
|
8360
|
+
## Register the endpoint
|
|
8545
8361
|
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
- implement atomic deduplication using the signed delivery identifier;
|
|
8551
|
-
- queue longer business work instead of performing it before responding;
|
|
8552
|
-
- return a small, non-sensitive response body.
|
|
8362
|
+
1. Open the webhook settings of the organization (or of the application, for application-wide events).
|
|
8363
|
+
2. Copy \`applicationPublicKeyPem\` from the configuration into your receiver's secret store. This is the key that verifies every signature.
|
|
8364
|
+
3. Add an endpoint with the final HTTPS URL and a description that names the receiving system and environment. Leave it **disabled**.
|
|
8365
|
+
4. Save. The endpoint's \`id\` is the identifier every delivery of it will carry; note it.
|
|
8553
8366
|
|
|
8554
|
-
|
|
8555
|
-
sequenceDiagram
|
|
8556
|
-
accTitle: Prove a webhook endpoint before production traffic is enabled
|
|
8557
|
-
accDescr: An administrator registers a disabled final URL and sends a synthetic signed test. The receiver verifies and deduplicates it, durably accepts the test, responds quickly, and exposes non-secret evidence that the administrator checks before enabling a controlled real event.
|
|
8558
|
-
participant Admin as Application administrator
|
|
8559
|
-
participant App as Application
|
|
8560
|
-
participant Receiver as Your receiver
|
|
8561
|
-
participant Queue as Durable queue
|
|
8562
|
-
Admin->>App: Save disabled final HTTPS endpoint
|
|
8563
|
-
Admin->>App: Send endpoint test
|
|
8564
|
-
App->>Receiver: Signed synthetic request
|
|
8565
|
-
Receiver->>Receiver: Verify raw bytes, signature and synthetic claim
|
|
8566
|
-
Receiver->>Queue: Record safe acceptance
|
|
8567
|
-
Receiver-->>App: 2xx with small response
|
|
8568
|
-
App-->>Admin: Accepted plus correlation evidence
|
|
8569
|
-
Admin->>Receiver: Confirm verification and queue evidence
|
|
8570
|
-
Admin->>App: Enable and trigger one controlled real event
|
|
8571
|
-
~~~
|
|
8572
|
-
|
|
8573
|
-
The important handoff is between the application outcome and the receiver
|
|
8574
|
-
evidence. **Accepted** means a 2xx was observed; it cannot prove which checks the
|
|
8575
|
-
receiver performed internally.
|
|
8576
|
-
|
|
8577
|
-
## Register the endpoint safely
|
|
8578
|
-
|
|
8579
|
-
1. Select the intended organization or application scope. Do not reuse an
|
|
8580
|
-
endpoint configured for another tenant or environment.
|
|
8581
|
-
2. Save the final HTTPS URL, a recognizable description and the endpoint in a
|
|
8582
|
-
disabled state. A disabled endpoint can still be tested deliberately.
|
|
8583
|
-
3. Read the published verification key through the documented application
|
|
8584
|
-
configuration surface and pin the expected application environment in the
|
|
8585
|
-
receiver.
|
|
8586
|
-
4. Confirm the receiver preserves the raw request bytes before any JSON parser,
|
|
8587
|
-
proxy rewrite or middleware normalization.
|
|
8588
|
-
5. Prepare a durable deduplication record keyed by the signed delivery identity
|
|
8589
|
-
and a queue for work that should continue after the response.
|
|
8590
|
-
|
|
8591
|
-
## Test before enabling delivery
|
|
8592
|
-
|
|
8593
|
-
The endpoint test uses the same signing and transport path as a real delivery.
|
|
8594
|
-
It can test a disabled endpoint with a synthetic signed payload and does not
|
|
8595
|
-
create a normal delivery-log row.
|
|
8367
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#endpointMarkdown}}
|
|
8596
8368
|
|
|
8597
|
-
|
|
8369
|
+
The same operations are available to automation through the organization webhook configuration in the [API reference](/api); the endpoint test is its own operation there.
|
|
8598
8370
|
|
|
8599
|
-
|
|
8600
|
-
| --- | --- | --- |
|
|
8601
|
-
| **Accepted** | Receiver returned a 2xx response | Confirm receiver logs prove signature and body verification occurred before the response |
|
|
8602
|
-
| **Rejected** | Receiver returned a non-2xx HTTP response | Inspect receiver validation and response status |
|
|
8603
|
-
| **Unreachable** | No HTTP response was obtained | Check public reachability, DNS, TLS and receiver timeout |
|
|
8604
|
-
| **Not sent** | Local signing or configuration prevented the request | Correct application configuration before testing the receiver |
|
|
8605
|
-
|
|
8606
|
-
Redirect responses are terminal configuration failures. Update the endpoint to
|
|
8607
|
-
the final receiver URL rather than relying on a 3xx hop.
|
|
8608
|
-
|
|
8609
|
-
The synthetic request carries a signed indication that it is a test. Do not
|
|
8610
|
-
turn it into a real order, notification or account change. Retain its signed
|
|
8611
|
-
delivery identity long enough to prove that a duplicate test would not repeat
|
|
8612
|
-
the business effect.
|
|
8371
|
+
## Run the endpoint test
|
|
8613
8372
|
|
|
8614
|
-
|
|
8373
|
+
The test sends one synthetic, signed request through the same signing and transport path as a real delivery, to the endpoint you choose, even while it is disabled. It does not create a delivery record. Read the outcome precisely:
|
|
8615
8374
|
|
|
8616
|
-
-
|
|
8617
|
-
- a request with a missing or invalid signature is rejected before parsing or
|
|
8618
|
-
business processing;
|
|
8619
|
-
- a repeated signed delivery identity does not enqueue a second effect;
|
|
8620
|
-
- a receiver delay or outage produces a non-success outcome that operators can
|
|
8621
|
-
locate in both systems.
|
|
8375
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#testOutcomesMarkdown}}
|
|
8622
8376
|
|
|
8623
|
-
|
|
8377
|
+
The synthetic request is recognisable from its signed claims: \`synthetic\` is \`true\` and \`operationIdentifier\` is \`testEndpoint\`. Your receiver must verify it like any other request and must not turn it into a business effect; branch on those claims, never on the body text.
|
|
8624
8378
|
|
|
8625
|
-
|
|
8626
|
-
and verify the complete receiver path. Only then enable broader traffic. Retain
|
|
8627
|
-
the endpoint identity, test time and outcome without retaining the signature or
|
|
8628
|
-
synthetic payload unnecessarily.
|
|
8379
|
+
Before enabling, make the receiver pass four cases:
|
|
8629
8380
|
|
|
8630
|
-
|
|
8381
|
+
- the test is \`accepted\`, and the receiver's own log shows verification ran before it answered;
|
|
8382
|
+
- a request with a missing or altered signature is refused before the body is parsed;
|
|
8383
|
+
- the same \`jti\` sent twice produces one effect;
|
|
8384
|
+
- a receiver that answers slowly produces \`unreachable\`, and both teams can find that attempt.
|
|
8631
8385
|
|
|
8632
|
-
|
|
8633
|
-
2. signature and raw-body integrity checks ran before the payload was trusted;
|
|
8634
|
-
3. the signed delivery identity was claimed exactly once;
|
|
8635
|
-
4. the application recorded a successful delivery attempt;
|
|
8636
|
-
5. the queued worker produced the intended downstream business result.
|
|
8386
|
+
## Enable and confirm the first real event
|
|
8637
8387
|
|
|
8638
|
-
|
|
8639
|
-
evidence. Correct the failed boundary, test again while disabled, and repeat one
|
|
8640
|
-
controlled event before restoring ordinary traffic.
|
|
8388
|
+
Enable the endpoint, trigger one event you can recognise (for example, invite a test member), and follow it end to end: the delivery row shows \`delivered\`, the receiver logged the \`jti\`, and the downstream result exists. Only then let ordinary traffic flow.
|
|
8641
8389
|
|
|
8642
|
-
|
|
8390
|
+
If any step fails, disable the endpoint, keep the delivery and test evidence, fix the one boundary that failed, and repeat the test before re-enabling.
|
|
8643
8391
|
|
|
8644
|
-
|
|
8645
|
-
host, test time, outcome, HTTP status, duration and signed delivery identifier
|
|
8646
|
-
used for correlation. Do not retain the signature token, verification private
|
|
8647
|
-
material, unrestricted payload or sensitive receiver response in an ordinary
|
|
8648
|
-
ticket.
|
|
8649
|
-
|
|
8650
|
-
## Continue safely
|
|
8392
|
+
## Change an endpoint later
|
|
8651
8393
|
|
|
8652
|
-
|
|
8653
|
-
complete receiver-side trust boundary.
|
|
8654
|
-
- [Operate webhook deliveries](/integrations/webhooks/operate-deliveries) before
|
|
8655
|
-
enabling production volume or automatic recovery.
|
|
8656
|
-
- [Troubleshoot webhook deliveries](/integrations/webhooks/troubleshoot) when a
|
|
8657
|
-
test or controlled event does not complete.`,
|
|
8394
|
+
Changing the URL affects future deliveries only; past delivery rows keep the URL they were sent to. After a URL or receiver deployment change, run the test again and follow one real event before trusting it. Disabling an endpoint stops new deliveries to it; it does not cancel work your receiver already accepted.`,
|
|
8658
8395
|
},
|
|
8659
8396
|
{
|
|
8660
8397
|
managedPath: 'integrations/webhooks/verify-delivery.md',
|
|
8661
8398
|
unitRef: 'technical-documentation:unit/verify-webhook-delivery',
|
|
8662
|
-
sourceRefs: [
|
|
8399
|
+
sourceRefs: [
|
|
8400
|
+
'saas-technical-doc:engine-content/verify-webhook-delivery',
|
|
8401
|
+
'source:consumer-fact:outbound-webhooks',
|
|
8402
|
+
],
|
|
8663
8403
|
markdown: `# Verify a webhook delivery
|
|
8664
8404
|
|
|
8665
|
-
Treat every incoming request as untrusted until
|
|
8666
|
-
the exact body bytes have been verified. Parse JSON only after this boundary.
|
|
8667
|
-
|
|
8668
|
-
The signature answers **who signed these exact bytes, for which delivery and
|
|
8669
|
-
context, and for how long the claim is valid**. It does not decide whether your
|
|
8670
|
-
business process should act on the event. Keep cryptographic verification,
|
|
8671
|
-
deduplication, payload validation and business authorization as separate checks.
|
|
8672
|
-
|
|
8673
|
-
| Before you begin | Successful result |
|
|
8674
|
-
| --- | --- |
|
|
8675
|
-
| Load the verification key from the intended application environment, preserve the raw request bytes, define the expected audience and event route, and prepare an atomic store for signed delivery identities | A valid request is trusted and queued once; invalid signatures, changed bytes, unexpected claims, expired tokens and duplicate delivery identities cannot create a business effect |
|
|
8676
|
-
|
|
8677
|
-
## Preserve the bytes before parsing
|
|
8405
|
+
Treat every incoming request as untrusted until the signature and the exact body bytes are verified, then deduplicate on the delivery identifier, and only then parse the body. This page gives the contract and a receiver you can run.
|
|
8678
8406
|
|
|
8679
|
-
|
|
8680
|
-
the exact byte sequence received on the wire before that parser runs. Changing
|
|
8681
|
-
whitespace, character encoding or field order and then serializing the object
|
|
8682
|
-
again produces different bytes and must fail the body-hash check.
|
|
8407
|
+
## The signature
|
|
8683
8408
|
|
|
8684
|
-
|
|
8685
|
-
duplicated value according to your HTTP framework's safe header handling; never
|
|
8686
|
-
accept a signature copied into the body or query string.
|
|
8409
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#signatureMarkdown}}
|
|
8687
8410
|
|
|
8688
|
-
|
|
8411
|
+
A decoded token payload looks like this:
|
|
8689
8412
|
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
3. Pin **ES256** and validate the expected issuer, audience, key identifier,
|
|
8693
|
-
issued time and expiry. Allow only a small, deliberate clock tolerance.
|
|
8694
|
-
4. Compute SHA-256 over the **raw request bytes** and compare it with the signed
|
|
8695
|
-
body hash using a constant-time comparison.
|
|
8696
|
-
5. Validate the signed event channel, resource and operation claims against the
|
|
8697
|
-
receiver route you expected.
|
|
8698
|
-
6. Atomically claim the signed **jti** before applying a business effect.
|
|
8699
|
-
7. Parse and validate the JSON payload against the published event contract.
|
|
8700
|
-
|
|
8701
|
-
~~~mermaid
|
|
8702
|
-
flowchart TD
|
|
8703
|
-
accTitle: Reject a webhook until identity, integrity and replay checks pass
|
|
8704
|
-
accDescr: The receiver validates the signature token, recomputes the raw-body hash, checks expected event claims, and atomically claims the jti before parsing and processing the payload.
|
|
8705
|
-
request["Incoming request"] --> token{"Token valid and expected?"}
|
|
8706
|
-
token -->|No| reject["Reject without processing"]
|
|
8707
|
-
token -->|Yes| hash{"Raw-body SHA-256 matches?"}
|
|
8708
|
-
hash -->|No| reject
|
|
8709
|
-
hash -->|Yes| claims{"Event claims expected?"}
|
|
8710
|
-
claims -->|No| reject
|
|
8711
|
-
claims -->|Yes| dedup{"jti claimed atomically?"}
|
|
8712
|
-
dedup -->|Already seen| acknowledge["Return the established duplicate outcome"]
|
|
8713
|
-
dedup -->|New| parse["Parse, validate and queue work"]
|
|
8413
|
+
~~~json
|
|
8414
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#claimsExampleJson}}
|
|
8714
8415
|
~~~
|
|
8715
8416
|
|
|
8716
|
-
|
|
8417
|
+
The endpoint test adds \`"synthetic": true\` and sets \`operationIdentifier\` to \`testEndpoint\`; treat such a delivery as verified but never act on it.
|
|
8717
8418
|
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
| **jti** | This delivery identity has or has not already been accepted by the receiver |
|
|
8419
|
+
## The verification sequence
|
|
8420
|
+
|
|
8421
|
+
1. Read the signature header. No header, or more than one, is a refusal.
|
|
8422
|
+
2. Verify the token with \`applicationPublicKeyPem\` from the webhook configuration, pinning the algorithm, the issuer and the audience above. Let your JWT library check \`exp\` and \`iat\`; allow a few seconds of clock tolerance, not minutes.
|
|
8423
|
+
3. Hash the raw request bytes with the algorithm in \`bodyHashAlg\` and compare the hex digest with \`bodyHash\` using a constant-time comparison.
|
|
8424
|
+
4. Claim \`jti\` in your durable store atomically with recording the work. A second request with the same \`jti\` is a retry: answer 2xx and do nothing.
|
|
8425
|
+
5. Parse the body and hand it to a queue. Answer the application before the work runs.
|
|
8726
8426
|
|
|
8727
|
-
|
|
8728
|
-
environment, audience or event handler is still unacceptable here.
|
|
8427
|
+
Refuse with a plain non-2xx status and no explanation of which check failed. Log the time, the route, the reason category and the \`jti\`; never the token or the body.
|
|
8729
8428
|
|
|
8730
|
-
|
|
8731
|
-
string can also change across contexts. Use **jti** as the deduplication key for
|
|
8732
|
-
the same delivery and keep that claim stable for every retry of its delivery row.
|
|
8429
|
+
## A receiver in Node.js
|
|
8733
8430
|
|
|
8734
|
-
|
|
8735
|
-
the work. A separate “check then insert” sequence allows two concurrent attempts
|
|
8736
|
-
to pass before either writes the record. Retain the claim for at least as long
|
|
8737
|
-
as the delivery can be retried or manually replayed under your operating policy.
|
|
8431
|
+
Express and the \`jsonwebtoken\` package, with the raw body preserved:
|
|
8738
8432
|
|
|
8739
|
-
|
|
8433
|
+
~~~javascript
|
|
8434
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#verifierSampleNode}}
|
|
8435
|
+
~~~
|
|
8740
8436
|
|
|
8741
|
-
|
|
8742
|
-
explain which cryptographic comparison failed to an unauthenticated caller, and
|
|
8743
|
-
do not log the complete token or sensitive body. Retain the time, destination
|
|
8744
|
-
route, reason category and non-secret correlation evidence needed for support.
|
|
8437
|
+
\`claimDeliveryOnce\` must be an atomic insert keyed by \`jti\` in the store that also records the work, such as a unique index in a database table, so two concurrent attempts cannot both pass. \`enqueue\` hands the event to a worker; the response must not wait for the worker.
|
|
8745
8438
|
|
|
8746
|
-
|
|
8747
|
-
chosen without applying the business effect again. If the first acceptance is
|
|
8748
|
-
still processing, the duplicate must not start parallel work.
|
|
8439
|
+
Any language works the same way: an ES256 JWT verifier with issuer and audience pinned, a SHA-256 over the raw bytes, and a unique key on \`jti\`.
|
|
8749
8440
|
|
|
8750
|
-
##
|
|
8441
|
+
## Route by claims, not by body
|
|
8751
8442
|
|
|
8752
|
-
|
|
8753
|
-
token's key identifier, issuer and audience with the verification key loaded
|
|
8754
|
-
from the same application environment. Refresh through the documented key
|
|
8755
|
-
surface; never fetch a key from an untrusted location supplied by the incoming
|
|
8756
|
-
request. Keep the previous accepted key only when the application's published
|
|
8757
|
-
rotation contract explicitly requires an overlap.
|
|
8443
|
+
Every enabled endpoint receives every event on its channel. Use the \`resourceIdentifier\` and \`operationIdentifier\` claims to decide which handler runs or whether to ignore the event, before you parse the body. The body is the operation's response document as described in the [API reference](/api) for that resource.
|
|
8758
8444
|
|
|
8759
|
-
|
|
8445
|
+
## Prove it before production
|
|
8760
8446
|
|
|
8761
|
-
|
|
8762
|
-
- one changed raw byte is rejected;
|
|
8763
|
-
- one expired or wrong-audience token is rejected;
|
|
8764
|
-
- one unexpected event claim is rejected by the route;
|
|
8765
|
-
- the same **jti** received concurrently produces one accepted business effect.
|
|
8447
|
+
Run these five cases against the receiver and keep the results with the endpoint's \`id\`:
|
|
8766
8448
|
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8449
|
+
- a valid delivery reaches the queue and is answered 2xx within a second;
|
|
8450
|
+
- one changed byte in the body is refused;
|
|
8451
|
+
- a token with the wrong audience or an expired \`exp\` is refused;
|
|
8452
|
+
- the same \`jti\` delivered twice, concurrently, produces one queued job;
|
|
8453
|
+
- the endpoint test from the application is \`accepted\` and is not acted on.
|
|
8770
8454
|
|
|
8771
|
-
##
|
|
8455
|
+
## When keys or deployments change
|
|
8772
8456
|
|
|
8773
|
-
|
|
8774
|
-
before enabling a receiver that has not passed the negative cases.
|
|
8775
|
-
- [Operate webhook deliveries](/integrations/webhooks/operate-deliveries) to
|
|
8776
|
-
separate verified acceptance from longer business work.
|
|
8777
|
-
- [Troubleshoot webhook deliveries](/integrations/webhooks/troubleshoot) when
|
|
8778
|
-
the application and receiver disagree about the result.`,
|
|
8457
|
+
The application's webhook key is the one on the configuration. The token header's \`kid\` is the thumbprint of that key, not a fixed name: a \`kid\` you have not seen means the key rotated, and the remedy is to reload \`applicationPublicKeyPem\` from the configuration. If verification still fails, confirm \`iss\` and \`aud\` match what your receiver pins. Never disable verification, accept every algorithm, or re-serialise the JSON to make a failing check pass.`,
|
|
8779
8458
|
},
|
|
8780
8459
|
{
|
|
8781
8460
|
managedPath: 'integrations/webhooks/operate-deliveries.md',
|
|
8782
8461
|
unitRef: 'technical-documentation:unit/operate-webhook-deliveries',
|
|
8783
|
-
sourceRefs: [
|
|
8462
|
+
sourceRefs: [
|
|
8463
|
+
'saas-technical-doc:engine-content/operate-webhook-deliveries',
|
|
8464
|
+
'source:consumer-fact:outbound-webhooks',
|
|
8465
|
+
],
|
|
8784
8466
|
markdown: `# Operate webhook deliveries
|
|
8785
8467
|
|
|
8786
|
-
|
|
8787
|
-
row for each enabled endpoint, attempts the request and records the outcome.
|
|
8788
|
-
Your receiver should likewise separate safe acceptance from longer business
|
|
8789
|
-
processing.
|
|
8468
|
+
Delivery is asynchronous. For every event the application creates one delivery row per enabled endpoint, attempts the request, and records what came back. Operating webhooks means reading those rows correctly and keeping your receiver's side honest.
|
|
8790
8469
|
|
|
8791
|
-
|
|
8792
|
-
the application to your receiver, and the business work performed after your
|
|
8793
|
-
receiver accepts it. A green delivery does not automatically prove the second
|
|
8794
|
-
lifecycle completed.
|
|
8470
|
+
## Delivery statuses
|
|
8795
8471
|
|
|
8796
|
-
|
|
8797
|
-
| --- | --- |
|
|
8798
|
-
| Assign an owner for the endpoint and receiver queue; define deduplication retention, alert thresholds, downstream reconciliation and the conditions for an explicit retry | Operators can distinguish waiting, active, delivered and terminally failed rows, recover without duplicate effects, and prove the downstream business result separately from HTTP delivery |
|
|
8472
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#deliveryStatesMarkdown}}
|
|
8799
8473
|
|
|
8800
|
-
|
|
8474
|
+
The delivery log is a resource in the [API reference](/api): list it, search it by status, read one row, or retry a failed one. Each row carries the endpoint \`id\`, the \`httpUrl\` it was sent to, \`attemptCount\`, \`nextAttemptAt\`, the last \`responseStatus\`, the first kilobytes of the response body, a \`failureReason\` when terminal, and \`signatureJti\`, which is the \`jti\` your receiver stored.
|
|
8801
8475
|
|
|
8802
|
-
|
|
8803
|
-
stateDiagram-v2
|
|
8804
|
-
accTitle: Webhook delivery lifecycle
|
|
8805
|
-
accDescr: A pending delivery becomes in flight. A successful 2xx response marks it delivered. A transient failure schedules another pending attempt, while a terminal failure marks it failed. An administrator can explicitly retry a failed delivery.
|
|
8806
|
-
[*] --> Pending
|
|
8807
|
-
Pending --> InFlight
|
|
8808
|
-
InFlight --> Delivered: 2xx response
|
|
8809
|
-
InFlight --> Pending: transient failure and attempts remain
|
|
8810
|
-
InFlight --> Failed: terminal failure or attempts exhausted
|
|
8811
|
-
Failed --> Pending: explicit retry
|
|
8812
|
-
Delivered --> [*]
|
|
8813
|
-
~~~
|
|
8476
|
+
## The retry ladder
|
|
8814
8477
|
|
|
8815
|
-
|
|
8478
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#retryMarkdown}}
|
|
8816
8479
|
|
|
8817
|
-
|
|
8818
|
-
| --- | --- | --- |
|
|
8819
|
-
| **Pending** | The row is waiting for its first attempt or a scheduled retry | Compare the next-attempt time with the retry schedule; do not start a parallel manual sender |
|
|
8820
|
-
| **In flight** | One worker currently holds the delivery for an HTTP attempt | Allow the bounded request to complete; investigate only if the state remains inconsistent with worker health |
|
|
8821
|
-
| **Delivered** | The application observed a 2xx response | Confirm the receiver accepted this **jti** durably, then reconcile the downstream result |
|
|
8822
|
-
| **Failed** | A terminal response occurred or the attempt budget was exhausted | Correct the failed boundary, prove deduplication, then decide whether an explicit retry is safe |
|
|
8823
|
-
|
|
8824
|
-
Transient failures include network failure, timeout, 408, 429 and 5xx. Delivery
|
|
8825
|
-
uses at most five attempts with delays of approximately one minute, five
|
|
8826
|
-
minutes, thirty minutes and two hours. Each request has a thirty-second timeout.
|
|
8827
|
-
Redirects and other terminal 4xx responses are not retried automatically.
|
|
8828
|
-
|
|
8829
|
-
That schedule belongs to the application delivery row. Do not add an immediate
|
|
8830
|
-
parallel retry loop at the receiver or an external monitor: it defeats the
|
|
8831
|
-
bounded delay, increases load during an outage and can race the same business
|
|
8832
|
-
effect.
|
|
8480
|
+
The ladder belongs to the application. Do not add your own immediate retry loop on the receiver side or from a monitor: it defeats the back-off, multiplies load during an outage, and races the same \`jti\`.
|
|
8833
8481
|
|
|
8834
8482
|
## Design the receiver for retries
|
|
8835
8483
|
|
|
8836
|
-
- Claim
|
|
8837
|
-
-
|
|
8838
|
-
-
|
|
8839
|
-
-
|
|
8840
|
-
- Reconcile the receiver's accepted deliveries with resulting business records.
|
|
8484
|
+
- Claim \`jti\` atomically before any effect; the same \`jti\` returns on every retry of one delivery.
|
|
8485
|
+
- Answer 2xx only after the request is verified and durably queued, and answer well inside the timeout.
|
|
8486
|
+
- Do the work in a worker, idempotently, as a second line of defence.
|
|
8487
|
+
- Return a short, non-sensitive body; it is recorded on the delivery row and read by operators.
|
|
8841
8488
|
|
|
8842
|
-
|
|
8843
|
-
Return a short diagnostic that contains no credential, personal data or internal
|
|
8844
|
-
stack trace. A delivery marked **Delivered** means a 2xx was observed; it does
|
|
8845
|
-
not prove that your asynchronous business work later completed.
|
|
8489
|
+
A \`delivered\` row proves that your receiver answered 2xx. It cannot see whether your worker finished. For anything that moves money or access, reconcile your side against the application with a read.
|
|
8846
8490
|
|
|
8847
|
-
## Monitor
|
|
8491
|
+
## Monitor
|
|
8848
8492
|
|
|
8849
|
-
|
|
8493
|
+
On the application side, watch:
|
|
8850
8494
|
|
|
8851
|
-
- pending
|
|
8852
|
-
-
|
|
8853
|
-
-
|
|
8854
|
-
- the attempt count approaching the five-attempt limit;
|
|
8495
|
+
- \`pending\` rows whose \`nextAttemptAt\` is in the past for longer than a minute: the worker is not draining;
|
|
8496
|
+
- \`failed\` rows by endpoint and \`responseStatus\`: a burst of one status names one broken boundary;
|
|
8497
|
+
- \`attemptCount\` reaching 4 on many rows: the receiver is slow or flapping;
|
|
8855
8498
|
- endpoint changes around the first failure time.
|
|
8856
8499
|
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
- signature or body-integrity rejection by reason category;
|
|
8860
|
-
- duplicate **jti** claims and whether the existing work is complete;
|
|
8861
|
-
- queue age, failed worker jobs and downstream dependency health;
|
|
8862
|
-
- accepted deliveries that have no resulting business record;
|
|
8863
|
-
- repeated business records that indicate a broken atomic deduplication boundary.
|
|
8864
|
-
|
|
8865
|
-
Use the delivery identifier, endpoint identifier, signed **jti**, event context
|
|
8866
|
-
and timestamps to join the two views. Do not use the payload body or signature
|
|
8867
|
-
token as a monitoring label.
|
|
8868
|
-
|
|
8869
|
-
## Retry deliberately
|
|
8870
|
-
|
|
8871
|
-
An explicit retry resets a failed delivery for a fresh attempt. Before using it,
|
|
8872
|
-
confirm that the receiver will recognize the same **jti** and that the earlier
|
|
8873
|
-
attempt did not already create the business effect.
|
|
8874
|
-
|
|
8875
|
-
Use this decision sequence:
|
|
8876
|
-
|
|
8877
|
-
1. Read the failed delivery's endpoint snapshot, event context, attempt count,
|
|
8878
|
-
response status and failure reason.
|
|
8879
|
-
2. Check the receiver using **jti**. Determine whether it never received the
|
|
8880
|
-
request, rejected it, accepted it without completing work, or completed the
|
|
8881
|
-
business effect despite a lost response.
|
|
8882
|
-
3. Correct the actual cause. For a terminal 3xx or 4xx, test the final route or
|
|
8883
|
-
verification repair before retrying real traffic.
|
|
8884
|
-
4. Confirm that deduplication retention still covers the original **jti** and
|
|
8885
|
-
that downstream processing is safe to resume.
|
|
8886
|
-
5. Retry one row. Observe the resulting application state and receiver queue
|
|
8887
|
-
before retrying a group of failures.
|
|
8888
|
-
6. Reconcile the authoritative downstream record and close the incident with
|
|
8889
|
-
both delivery and business evidence.
|
|
8500
|
+
On the receiver side, watch signature rejections by reason, duplicate \`jti\` claims, queue age and jobs with no downstream record. Join the two views on the endpoint \`id\`, \`signatureJti\` and the timestamps; never on the body.
|
|
8890
8501
|
|
|
8891
|
-
|
|
8892
|
-
turn the delivery row green. Record the mismatch and reconcile it through the
|
|
8893
|
-
appropriate operating process.
|
|
8502
|
+
## Retry a failed delivery
|
|
8894
8503
|
|
|
8895
|
-
|
|
8504
|
+
The retry operation re-queues one \`failed\` row for a fresh attempt with the same \`jti\` and the same body. Before using it:
|
|
8896
8505
|
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
or
|
|
8900
|
-
|
|
8901
|
-
restoring ordinary volume.
|
|
8506
|
+
1. Read the row: \`httpUrl\`, \`attemptCount\`, \`responseStatus\`, \`failureReason\`.
|
|
8507
|
+
2. Search your receiver's store for the \`jti\`. Decide whether the receiver never saw it, refused it, accepted it without finishing, or finished the work despite a lost response.
|
|
8508
|
+
3. Fix the cause. For a **3xx** or a **4xx** other than 408 and 429, the same bytes would be refused again, so test the endpoint first.
|
|
8509
|
+
4. Retry one row and watch it become \`delivered\`, then retry the rest.
|
|
8902
8510
|
|
|
8903
|
-
|
|
8904
|
-
stops new matching deliveries from being created for that destination; it is
|
|
8905
|
-
not proof that existing downstream work has been cancelled or reconciled.
|
|
8511
|
+
If the receiver already did the work, do not retry to turn the row green; record the mismatch and reconcile through your own process.
|
|
8906
8512
|
|
|
8907
|
-
##
|
|
8513
|
+
## Endpoint changes and outages
|
|
8908
8514
|
|
|
8909
|
-
|
|
8910
|
-
attempt count, state transitions, response status, bounded non-sensitive
|
|
8911
|
-
diagnostic and downstream reconciliation result. Apply the organization's
|
|
8912
|
-
retention and access policy to payloads because delivery records can contain
|
|
8913
|
-
business or personal data.
|
|
8515
|
+
A URL change affects future deliveries; existing rows keep the \`httpUrl\` they were created with. After any change to the URL or the receiver deployment, run the endpoint test and follow one real event. When the receiver cannot safely accept traffic, disable the endpoint: no new rows are created for it, and rows already \`pending\` keep retrying until they succeed or run out of attempts.
|
|
8914
8516
|
|
|
8915
|
-
##
|
|
8517
|
+
## Retention
|
|
8916
8518
|
|
|
8917
|
-
|
|
8918
|
-
locate the failed boundary before an explicit retry.
|
|
8919
|
-
- [Verify a webhook delivery](/integrations/webhooks/verify-delivery) when
|
|
8920
|
-
signature, raw-body or duplicate handling is in doubt.
|
|
8921
|
-
- Return to [Webhooks](/integrations/webhooks) to review the complete event,
|
|
8922
|
-
delivery, attempt and downstream-work model.`,
|
|
8519
|
+
A delivery row stores the full event body it sent, its hash, and the first kilobytes of your receiver's response. Both can contain personal data. Keep responses short, restrict who may read the delivery log, and apply your organization's retention policy to it; the purge operation in the API reference removes old rows.`,
|
|
8923
8520
|
},
|
|
8924
8521
|
{
|
|
8925
8522
|
managedPath: 'integrations/webhooks/troubleshoot.md',
|
|
8926
8523
|
unitRef: 'technical-documentation:unit/troubleshoot-webhook-deliveries',
|
|
8927
|
-
sourceRefs: [
|
|
8524
|
+
sourceRefs: [
|
|
8525
|
+
'saas-technical-doc:engine-content/troubleshoot-webhook-deliveries',
|
|
8526
|
+
'source:consumer-fact:outbound-webhooks',
|
|
8527
|
+
],
|
|
8928
8528
|
markdown: `# Troubleshoot webhook deliveries
|
|
8929
8529
|
|
|
8930
|
-
Start from the
|
|
8931
|
-
disable verification or repeatedly change the endpoint while investigating;
|
|
8932
|
-
that removes the evidence needed to find the failed boundary.
|
|
8530
|
+
Start from the delivery row. Its status, \`attemptCount\`, \`responseStatus\`, \`failureReason\` and the recorded response body place the failure on one side of the wire. Fix that side, prove it with the endpoint test, then retry the row.
|
|
8933
8531
|
|
|
8934
|
-
|
|
8935
|
-
| --- | --- |
|
|
8936
|
-
| Preserve the delivery identifier, endpoint, signed delivery identity, event context, state, attempts, timestamps and HTTP evidence; locate the matching receiver record without copying secrets | The failure is assigned to application signing, transport, receiver verification, safe acceptance or downstream processing; one bounded repair is proven and recovery creates no duplicate effect |
|
|
8532
|
+
## Find the row
|
|
8937
8533
|
|
|
8938
|
-
|
|
8534
|
+
Open the delivery log for the organization (or the application) and filter by endpoint and time, or search it through the API. The \`signatureJti\` on the row is the \`jti\` your receiver logged; it is the key that joins the two systems.
|
|
8939
8535
|
|
|
8940
|
-
|
|
8941
|
-
flowchart TD
|
|
8942
|
-
accTitle: Locate a webhook failure from application delivery to business result
|
|
8943
|
-
accDescr: The operator starts from the delivery record, checks whether a request was sent and answered, then follows receiver verification, atomic acceptance, queued work and authoritative downstream state before deciding whether retry is safe.
|
|
8944
|
-
row["Preserve delivery and attempt evidence"] --> sent{"Request sent?"}
|
|
8945
|
-
sent -->|No| signing["Check signing and application configuration"]
|
|
8946
|
-
sent -->|Yes| answered{"HTTP response received?"}
|
|
8947
|
-
answered -->|No| transport["Check final URL, DNS, TLS, reachability and timeout"]
|
|
8948
|
-
answered -->|Yes| success{"2xx observed?"}
|
|
8949
|
-
success -->|No| receiver["Inspect receiver rejection or capacity"]
|
|
8950
|
-
success -->|Yes| claimed{"jti durably claimed?"}
|
|
8951
|
-
claimed -->|No| acceptance["Repair acknowledgement boundary"]
|
|
8952
|
-
claimed -->|Yes| work{"Business work complete?"}
|
|
8953
|
-
work -->|No| downstream["Recover queue or downstream dependency"]
|
|
8954
|
-
work -->|Yes| reconcile["Close with delivery and business proof"]
|
|
8955
|
-
~~~
|
|
8536
|
+
## Diagnose by what the row shows
|
|
8956
8537
|
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8538
|
+
| Row shows | Where it broke | What to check |
|
|
8539
|
+
| --- | --- | --- |
|
|
8540
|
+
| No row at all | The event was not published, or webhooks are disabled | The master \`enabled\` switch, that the endpoint is enabled, and that the operation you performed is one the application publishes |
|
|
8541
|
+
| \`failed\`, \`failureReason\` mentions signing | The application could not sign | Application-side configuration; nothing on the receiver can help |
|
|
8542
|
+
| \`failed\`, no \`responseStatus\` | No HTTP answer within the timeout | Public reachability, DNS, TLS, and the receiver's own processing time |
|
|
8543
|
+
| \`responseStatus\` **3xx** | The URL is not the final receiver | Register the redirect target itself; redirects are never followed |
|
|
8544
|
+
| \`responseStatus\` **400** or **401** | Your receiver refused the signature, the body hash or the claims | Raw-body capture, the header name, the pinned algorithm, issuer and audience, the public key, and clock skew |
|
|
8545
|
+
| \`responseStatus\` **404** | Wrong path or wrong deployment | The exact route the receiver serves |
|
|
8546
|
+
| \`responseStatus\` **408**, **429** or **5xx** | The receiver is overloaded or failing | Capacity and errors on the receiver; the row keeps retrying on the ladder |
|
|
8547
|
+
| \`delivered\` but no downstream result | The receiver answered 2xx before durable acceptance, or the worker failed | The receiver's \`jti\` store, its queue and the worker's errors |
|
|
8548
|
+
| The effect happened twice | The receiver did not claim \`jti\` atomically | The uniqueness constraint on \`jti\` and the transaction around it |
|
|
8960
8549
|
|
|
8961
|
-
|
|
8550
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#retryMarkdown}}
|
|
8962
8551
|
|
|
8963
|
-
|
|
8964
|
-
| --- | --- | --- |
|
|
8965
|
-
| Test is **not sent** | Application configuration or signing | Enabled configuration, published key and local error evidence |
|
|
8966
|
-
| Test or delivery is **unreachable** | Network, DNS, TLS or receiver availability | Final public URL, certificate, resolved host and receiver timeout |
|
|
8967
|
-
| **3xx** response | Endpoint is not the final receiver URL | Configure the redirect destination directly |
|
|
8968
|
-
| **400** or **401** from receiver | Signature, claims, body bytes or payload validation | Raw-body capture, signature header, algorithm, key, clock and body hash |
|
|
8969
|
-
| **404** from receiver | Wrong route or deployment | Exact configured path and environment |
|
|
8970
|
-
| **408**, **429** or **5xx** | Transient receiver failure | Capacity, timeout and retry schedule; do not start a parallel retry loop |
|
|
8971
|
-
| Delivery is **Delivered**, but work is missing | Receiver acknowledged before durable acceptance or downstream work failed | Receiver queue, deduplication record and business reconciliation |
|
|
8972
|
-
| Repeated business effect | Receiver did not atomically deduplicate **jti** | Deduplication transaction and retention period |
|
|
8973
|
-
|
|
8974
|
-
## Investigate without changing several variables
|
|
8975
|
-
|
|
8976
|
-
### Nothing was sent
|
|
8977
|
-
|
|
8978
|
-
For **Not sent** or a delivery that failed during signing, check the published
|
|
8979
|
-
verification-key configuration and signing evidence for the intended
|
|
8980
|
-
environment. The receiver cannot repair a request that never left the
|
|
8981
|
-
application. Correct the application-side configuration, then use the endpoint
|
|
8982
|
-
test before retrying real traffic.
|
|
8983
|
-
|
|
8984
|
-
### The receiver did not answer
|
|
8985
|
-
|
|
8986
|
-
Confirm the configured URL is the final public route. Check DNS, TLS, firewall
|
|
8987
|
-
or allow-list rules, deployment health and the receiver's thirty-second request
|
|
8988
|
-
window. A timeout does not prove the receiver saw nothing: search receiver logs
|
|
8989
|
-
and the deduplication store by **jti** before allowing another delivery.
|
|
8990
|
-
|
|
8991
|
-
### The receiver answered with non-2xx
|
|
8992
|
-
|
|
8993
|
-
Preserve the status and bounded response evidence. For 3xx, configure the final
|
|
8994
|
-
destination directly. For 400 or 401, inspect raw-body capture, the signature
|
|
8995
|
-
header, pinned algorithm, key identifier, clock, audience, event claims and body
|
|
8996
|
-
hash. For 404, compare the exact configured route and deployment. For 408, 429
|
|
8997
|
-
or 5xx, stabilize receiver capacity and let the scheduled retry proceed unless
|
|
8998
|
-
the delivery has already become terminal.
|
|
8999
|
-
|
|
9000
|
-
### Delivery is green but business work is missing
|
|
9001
|
-
|
|
9002
|
-
A **Delivered** row proves only that the receiver returned 2xx. Check whether
|
|
9003
|
-
the receiver atomically stored **jti** and durably queued the work before that
|
|
9004
|
-
response. Then inspect the worker, downstream dependency and authoritative
|
|
9005
|
-
business record. Recover the queued job or reconcile the business effect; do not
|
|
9006
|
-
retry the application delivery when the receiver already accepted it unless the
|
|
9007
|
-
receiver's deduplication contract makes that recovery deliberate and safe.
|
|
9008
|
-
|
|
9009
|
-
### The business effect happened more than once
|
|
9010
|
-
|
|
9011
|
-
Stop or disable the affected receiver path to contain further duplicates.
|
|
9012
|
-
Compare the repeated records with one signed **jti**. Repair the atomic claim and
|
|
9013
|
-
work-enqueue boundary, define how duplicate business records will be reconciled,
|
|
9014
|
-
and prove concurrent receipt of the same **jti** creates one effect before
|
|
9015
|
-
restoring traffic.
|
|
9016
|
-
|
|
9017
|
-
## Preserve a minimal investigation record
|
|
9018
|
-
|
|
9019
|
-
Keep the delivery identifier, endpoint identity, event channel, attempt count,
|
|
9020
|
-
state, response status, timestamps and correlation information. Redact the
|
|
9021
|
-
signature, sensitive payload fields and receiver response content before
|
|
9022
|
-
sharing evidence.
|
|
9023
|
-
|
|
9024
|
-
If verification fails after a key or deployment change, compare the key
|
|
9025
|
-
identifier and expected application environment first. Never accept every
|
|
9026
|
-
algorithm or skip body-integrity verification as a fallback.
|
|
9027
|
-
|
|
9028
|
-
## Prove and close the repair
|
|
9029
|
-
|
|
9030
|
-
1. Test the endpoint while disabled and confirm both the application outcome and
|
|
9031
|
-
receiver verification evidence.
|
|
9032
|
-
2. Exercise one negative case relevant to the incident—invalid signature,
|
|
9033
|
-
changed bytes, duplicate **jti**, receiver timeout or failed downstream work.
|
|
9034
|
-
3. Trigger one controlled real event and follow it through delivery, durable
|
|
9035
|
-
acceptance and authoritative business state.
|
|
9036
|
-
4. Retry one failed delivery only when the earlier effect is absent or safely
|
|
9037
|
-
deduplicated.
|
|
9038
|
-
5. Record the cause, correction, affected scope, delivery identities and final
|
|
9039
|
-
reconciliation without storing secrets or unrestricted payloads.
|
|
9040
|
-
|
|
9041
|
-
Escalate with the delivery and endpoint identifiers, **jti**, event context,
|
|
9042
|
-
attempt timeline, response class and redacted receiver correlation. Say
|
|
9043
|
-
explicitly whether the receiver may already have accepted or completed the
|
|
9044
|
-
work. That prevents support from treating an ambiguous business outcome as a
|
|
9045
|
-
simple transport retry.
|
|
8552
|
+
## Signature failures after a change
|
|
9046
8553
|
|
|
9047
|
-
|
|
8554
|
+
If deliveries began failing with **401** right after a deployment or a key change, reload \`applicationPublicKeyPem\` from the webhook configuration: the token header's \`kid\` is the thumbprint of the signing key, so a new \`kid\` means the key rotated. Then confirm \`iss\` and \`aud\` still match what the receiver pins. [Verify a webhook delivery](/integrations/webhooks/verify-delivery) lists every pinned value. Do not relax the verifier to make it pass.
|
|
8555
|
+
|
|
8556
|
+
## Duplicates
|
|
8557
|
+
|
|
8558
|
+
A retry of the same delivery carries the same \`jti\`; a different delivery of the same operation carries a different one, even when the body is identical. If your effect happened twice with one \`jti\`, the atomic claim is broken. If it happened twice with two \`jti\` values, two events really occurred; compare the bodies.
|
|
8559
|
+
|
|
8560
|
+
## Prove the repair and retry
|
|
9048
8561
|
|
|
9049
|
-
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
8562
|
+
1. Run the endpoint test and confirm it is \`accepted\` with verification logged on the receiver.
|
|
8563
|
+
2. Search the receiver's store for the failed row's \`jti\` to know whether the work already ran.
|
|
8564
|
+
3. Retry that one row from the delivery log and watch it become \`delivered\`.
|
|
8565
|
+
4. Retry the remaining failed rows for the same endpoint.
|
|
8566
|
+
|
|
8567
|
+
## Ask for help with the right evidence
|
|
8568
|
+
|
|
8569
|
+
Quote the endpoint \`id\`, the delivery row identifier, \`signatureJti\`, \`attemptCount\`, \`responseStatus\`, \`failureReason\` and the timestamps, and say whether the receiver may already have done the work. Never paste the token or the event body.`,
|
|
9055
8570
|
},
|
|
9056
8571
|
{
|
|
9057
8572
|
managedPath: 'integrations/mcp.md',
|
|
9058
8573
|
unitRef: 'technical-documentation:unit/mcp-integrations',
|
|
9059
|
-
sourceRefs: [
|
|
8574
|
+
sourceRefs: [
|
|
8575
|
+
'saas-technical-doc:engine-content/mcp-integrations',
|
|
8576
|
+
'source:companion-projection:application-connection',
|
|
8577
|
+
'source:companion-projection:application-integration',
|
|
8578
|
+
],
|
|
9060
8579
|
markdown: `# Connect an MCP client
|
|
9061
8580
|
|
|
9062
8581
|
Use **Model Context Protocol (MCP)** when an AI client should discover a set of
|
|
@@ -9068,6 +8587,16 @@ result, not a universal list of everything the application can do.
|
|
|
9068
8587
|
| --- | --- |
|
|
9069
8588
|
| Choose the exact application environment, one MCP-capable client, a machine or delegated identity with the MCP audience, one non-production organization and one harmless expected tool | The client authenticates when challenged, receives only its caller-specific catalogue, completes one schema-valid read, refuses an unavailable or unauthorized call and can reconcile an uncertain mutation before retrying |
|
|
9070
8589
|
|
|
8590
|
+
## Connect to the endpoint
|
|
8591
|
+
|
|
8592
|
+
{{APPLICATION_CONNECTION:mcpEndpoint}}
|
|
8593
|
+
|
|
8594
|
+
Use it with the base URL of the environment you chose; the client configuration guides for [Cursor](/integrations/ai-tools/cursor), [Codex](/integrations/ai-tools/codex) and [Claude Code](/integrations/ai-tools/claude-code) show where each tool expects that address.
|
|
8595
|
+
|
|
8596
|
+
## The tools this application publishes
|
|
8597
|
+
|
|
8598
|
+
{{APPLICATION_INTEGRATION:mcpTools}}
|
|
8599
|
+
|
|
9071
8600
|
## Decide who authorizes the client
|
|
9072
8601
|
|
|
9073
8602
|
Use a dedicated machine identity when a service owns the work. Use delegated
|
|
@@ -9143,7 +8672,7 @@ catalogue request. In normal operation, let the MCP client or SDK create these
|
|
|
9143
8672
|
headers and keep the credential in its protected store.
|
|
9144
8673
|
|
|
9145
8674
|
~~~http
|
|
9146
|
-
POST
|
|
8675
|
+
POST {{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}} HTTP/1.1
|
|
9147
8676
|
Authorization: Bearer <ACCESS_TOKEN_FOR_THIS_MCP_SERVER>
|
|
9148
8677
|
Content-Type: application/json
|
|
9149
8678
|
Accept: application/json, text/event-stream
|
|
@@ -9310,7 +8839,10 @@ operations the current identity can actually invoke.
|
|
|
9310
8839
|
{
|
|
9311
8840
|
managedPath: 'integrations/a2a.md',
|
|
9312
8841
|
unitRef: 'technical-documentation:unit/a2a-integrations',
|
|
9313
|
-
sourceRefs: [
|
|
8842
|
+
sourceRefs: [
|
|
8843
|
+
'saas-technical-doc:engine-content/a2a-integrations',
|
|
8844
|
+
'source:companion-projection:application-connection',
|
|
8845
|
+
],
|
|
9314
8846
|
markdown: `# Connect an A2A agent
|
|
9315
8847
|
|
|
9316
8848
|
Use **Agent-to-Agent (A2A)** when another agent should exchange messages with
|
|
@@ -9325,6 +8857,8 @@ MCP catalogue.
|
|
|
9325
8857
|
|
|
9326
8858
|
## Read the agent card before sending work
|
|
9327
8859
|
|
|
8860
|
+
{{APPLICATION_CONNECTION:a2aAgentCard}}
|
|
8861
|
+
|
|
9328
8862
|
The public card describes the selected agent and the skills this application
|
|
9329
8863
|
publishes for it. Confirm the card belongs to the intended environment and named
|
|
9330
8864
|
agent instance. Treat it as discovery metadata: task and message operations
|
|
@@ -9379,8 +8913,8 @@ similar to this redacted example:
|
|
|
9379
8913
|
{
|
|
9380
8914
|
"protocolVersion": "0.2.5",
|
|
9381
8915
|
"supportedInterfaces": [
|
|
9382
|
-
{ "url": "
|
|
9383
|
-
{ "url": "
|
|
8916
|
+
{ "url": "{{APPLICATION_CONNECTION_VALUE:agentCardUrl}}", "transport": "JSONRPC", "version": "1.0" },
|
|
8917
|
+
{ "url": "{{APPLICATION_CONNECTION_VALUE:agentCardUrl}}", "transport": "JSONRPC", "version": "0.2.5" }
|
|
9384
8918
|
],
|
|
9385
8919
|
"name": "<APPLICATION_AGENT_NAME>",
|
|
9386
8920
|
"capabilities": {
|