@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.
Files changed (64) hide show
  1. package/dist/esm/companion/application-documentation/application-administration-documentation.d.ts +52 -0
  2. package/dist/esm/companion/application-documentation/application-administration-documentation.d.ts.map +1 -0
  3. package/dist/esm/companion/application-documentation/application-administration-documentation.js +58 -0
  4. package/dist/esm/companion/application-documentation/application-administration-documentation.js.map +1 -0
  5. package/dist/esm/companion/application-documentation/application-authentication-documentation.d.ts +76 -0
  6. package/dist/esm/companion/application-documentation/application-authentication-documentation.d.ts.map +1 -0
  7. package/dist/esm/companion/application-documentation/application-authentication-documentation.js +116 -0
  8. package/dist/esm/companion/application-documentation/application-authentication-documentation.js.map +1 -0
  9. package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts +87 -0
  10. package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts.map +1 -0
  11. package/dist/esm/companion/application-documentation/application-connection-documentation.js +138 -0
  12. package/dist/esm/companion/application-documentation/application-connection-documentation.js.map +1 -0
  13. package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts +48 -0
  14. package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts.map +1 -0
  15. package/dist/esm/companion/application-documentation/application-integration-documentation.js +63 -0
  16. package/dist/esm/companion/application-documentation/application-integration-documentation.js.map +1 -0
  17. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts +21 -3
  18. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts.map +1 -1
  19. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js +166 -10
  20. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js.map +1 -1
  21. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts +8 -0
  22. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts.map +1 -1
  23. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js +8 -1
  24. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js.map +1 -1
  25. package/dist/esm/companion/index.d.ts +4 -0
  26. package/dist/esm/companion/index.d.ts.map +1 -1
  27. package/dist/esm/companion/index.js +4 -0
  28. package/dist/esm/companion/index.js.map +1 -1
  29. package/dist/esm/companion/openapi-generator.d.ts.map +1 -1
  30. package/dist/esm/companion/openapi-generator.js +9 -7
  31. package/dist/esm/companion/openapi-generator.js.map +1 -1
  32. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.d.ts.map +1 -1
  33. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js +10 -2
  34. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js.map +1 -1
  35. package/dist/esm/companion/rendering/technical-documentation-markdown-links.d.ts +28 -0
  36. package/dist/esm/companion/rendering/technical-documentation-markdown-links.d.ts.map +1 -0
  37. package/dist/esm/companion/rendering/technical-documentation-markdown-links.js +52 -0
  38. package/dist/esm/companion/rendering/technical-documentation-markdown-links.js.map +1 -0
  39. package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.d.ts.map +1 -1
  40. package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.js +7 -2
  41. package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.js.map +1 -1
  42. package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts +1 -0
  43. package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts.map +1 -1
  44. package/dist/esm/companion/rendering/technical-documentation-render-model.js +91 -84
  45. package/dist/esm/companion/rendering/technical-documentation-render-model.js.map +1 -1
  46. package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts +74 -69
  47. package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts.map +1 -1
  48. package/dist/esm/content/application-consumer-documentation-content.techdoc.js +654 -1120
  49. package/dist/esm/content/application-consumer-documentation-content.techdoc.js.map +1 -1
  50. package/dist/esm/runtime/decode-jwt-claims.d.ts +7 -4
  51. package/dist/esm/runtime/decode-jwt-claims.d.ts.map +1 -1
  52. package/dist/esm/runtime/decode-jwt-claims.js +7 -4
  53. package/dist/esm/runtime/decode-jwt-claims.js.map +1 -1
  54. package/dist/esm/runtime/docs-auth-session.schemas.d.ts +16 -5
  55. package/dist/esm/runtime/docs-auth-session.schemas.d.ts.map +1 -1
  56. package/dist/esm/runtime/docs-auth-session.schemas.js +16 -5
  57. package/dist/esm/runtime/docs-auth-session.schemas.js.map +1 -1
  58. package/dist/esm/runtime/use-docs-auth-session.d.ts +9 -7
  59. package/dist/esm/runtime/use-docs-auth-session.d.ts.map +1 -1
  60. package/dist/esm/runtime/use-docs-auth-session.js +9 -7
  61. package/dist/esm/runtime/use-docs-auth-session.js.map +1 -1
  62. package/dist/tsconfig.build.tsbuildinfo +1 -1
  63. package/package.json +5 -5
  64. 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: ['saas-technical-doc:engine-content/application-orientation'],
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 **use, administer, integrate or operate
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
- | Your goal | Begin here | You will learn |
28
+ | You need to | Start here | You will find |
29
29
  | --- | --- | --- |
30
- | Sign in, complete a second factor or recover access | [Access and identity](/access-and-identity/overview) | Which sign-in method belongs to a person, a service or a delegated client—and how to distinguish authentication from permission problems |
31
- | Invite a person, change access or remove access | [User administration](/user-administration) | How manual membership, roles, organization units, single sign-on and SCIM work together |
32
- | Connect another system or automate a process | [Integrations](/integrations) | When to use REST, webhooks, an automation platform or an agent connection |
33
- | Build a workflow in n8n, Zapier or Make | [Automation platforms](/integrations/automation-platforms) | How to connect through the published API, protect the credential and verify the business result |
34
- | Investigate a change or export security evidence | [Security and audit](/security-and-audit) | How to read the authoritative audit trail, follow correlation and operate SIEM delivery safely |
35
- | Understand a subscription, invoice or access delay | Look for **Billing and subscriptions** in the navigation | How billing state becomes application access and how to reconcile an asynchronous change |
36
- | Implement an exact request | [API reference](/api) | The current operation, field, role, request and response contract |
37
-
38
- If **AI coding tools** appears under Integrations, the application has published
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
- 1. **Environment and organization** — verify that you are working in the
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 useful first journeys
42
+ ## Three things to know before you change anything
79
43
 
80
- ### Help a person gain the right access
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
- Start with [User administration](/user-administration). Choose manual
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
- ### Connect another system safely
89
-
90
- Start with [Integrations](/integrations). Choose the connection surface from
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
- Provide the environment, organization, approximate time, operation or task,
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 a sign-in screen may offer
107
+ ## What {{APPLICATION_NAME}} accepts to sign in
177
108
 
178
- {{CONSUMER_FACT:source:consumer-fact:access-authentication-methods#markdown}}
109
+ {{APPLICATION_AUTHENTICATION:enabledSignInMethods}}
179
110
 
180
- This inventory is not an availability promise. The sign-in screen and
181
- its available-method response are authoritative for the current person and
182
- organization.
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 support inventory that is absent here is not available for this attempt.
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: ['saas-technical-doc:engine-content/mfa-enrollment-and-recovery'],
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 application's support inventory is not necessarily
525
- enabled for this person and organization. The current sign-in screen is the
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
- {{CONSUMER_FACT:source:consumer-fact:organization-membership-administration#markdown}}
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
- {{CONSUMER_FACT:source:consumer-fact:organization-membership-administration#markdown}}
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. If the
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
- {{CONSUMER_FACT:source:consumer-fact:organization-membership-administration#markdown}}
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
- {{CONSUMER_FACT:source:consumer-fact:organization-membership-administration#markdown}}
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
- {{CONSUMER_FACT:source:consumer-fact:organization-membership-administration#markdown}}
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\` such as
3716
- \`invalidFilter\`, \`uniqueness\`, \`invalidSyntax\`, \`invalidPath\` or
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": "organization-member",
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: ['saas-technical-doc:engine-content/investigate-audit-event'],
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: ['saas-technical-doc:engine-content/operate-and-troubleshoot-siem-delivery'],
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
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#subscriptionStatusesMarkdown}}
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 the Business plan, completes the hosted
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
- An organization has a 50-run plan and needs 20 additional runs immediately.
5948
- Before confirming an add-on, record:
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
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#subscriptionStatusesMarkdown}}
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: ['saas-technical-doc:engine-content/ai-coding-tools'],
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
- **<server-origin>/api/v1/mcp**;
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: ['saas-technical-doc:engine-content/connect-cursor'],
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": "<server-origin>/api/v1/mcp"
7184
+ "url": "{{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}"
7186
7185
  }
7187
7186
  }
7188
7187
  }
7189
7188
  ~~~
7190
7189
 
7191
- Replace **<server-origin>** with the server origin for the intended application
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: ['saas-technical-doc:engine-content/connect-codex'],
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 = "<server-origin>/api/v1/mcp"
7322
+ url = "{{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}"
7322
7323
  default_tools_approval_mode = "prompt"
7323
7324
  ~~~
7324
7325
 
7325
- Replace **<server-origin>** with the server origin of the intended application
7326
- environment. Keep **/api/v1/mcp** exactly once. The prompt approval mode means
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: ['saas-technical-doc:engine-content/connect-claude-code'],
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 <server-origin>/api/v1/mcp
7459
+ claude mcp add --transport http application {{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}
7456
7460
  ~~~
7457
7461
 
7458
- Replace **<server-origin>** with the server origin for the intended application
7459
- environment and keep **/api/v1/mcp** exactly once. Add **--scope project** or
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 "<server-origin><documented-operation-path>" \
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: ['saas-technical-doc:engine-content/common-rest-api'],
7868
+ sourceRefs: [
7869
+ 'saas-technical-doc:engine-content/common-rest-api',
7870
+ ],
7864
7871
  markdown: `# REST API
7865
7872
 
7866
- Use the REST API when another system needs to read or change application data
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
- | Before you begin | Successful result |
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
- ## Decide whether REST matches the work
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
- | Need | Use | Why |
7881
+ ## Choose the right tool first
7882
+
7883
+ | You need to | Use | Why |
7878
7884
  | --- | --- | --- |
7879
- | Read current application state now | REST read operation | The caller receives an immediate representation of what it is allowed to see |
7880
- | Ask the application to make a change now | REST write operation | The caller receives an immediate success, refusal or ambiguous transport outcome |
7881
- | React when the application publishes an event | **Webhooks** (published under Integrations when the application exposes an outbound delivery surface) | The application initiates delivery after the event; polling is not required |
7882
- | Let an AI tool discover and invoke approved tools | **MCP** (published under Integrations when the application exposes an MCP tool surface) | Tool discovery and invocation are different from a fixed REST integration |
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
- REST describes the request/response transport. It does not decide who should
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
- ## Follow the request lifecycle
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
- ~~~mermaid
7892
- flowchart LR
7893
- accTitle: Build and reconcile a REST API request
7894
- accDescr: The integration selects an environment and operation, authenticates a dedicated identity, validates the response, then either commits the result or reconciles an ambiguous outcome.
7895
- choose["Select environment and documented operation"] --> auth["Authenticate the intended identity"]
7896
- auth --> send["Send a schema-valid request"]
7897
- send --> result{"Unambiguous result?"}
7898
- result -->|Yes| commit["Record outcome and correlation"]
7899
- result -->|No| read["Read authoritative state"]
7900
- read --> reconcile["Reconcile before any retry"]
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
- The most important branch is the last one. A timeout means that **the client did
7904
- not receive a result**; it does not prove that the server made no change.
7905
-
7906
- ## Keep four boundaries visible
7907
-
7908
- 1. **Environment:** the server origin identifies where the request goes. Never
7909
- infer production from a path copied from another deployment.
7910
- 2. **Identity:** the operation states which credential types it accepts. Use a
7911
- dedicated workload credential for unattended work.
7912
- 3. **Organization and visibility:** a valid identity can still be unable to see
7913
- a resource outside its organization or authorized unit scope.
7914
- 4. **Operation contract:** request fields, enum values, roles, response shape
7915
- and failure meanings are specific to the chosen operation.
7916
-
7917
- An HTTP success is transport evidence, not the final business proof. Verify the
7918
- returned identity and scope for reads, and re-read authoritative state after a
7919
- write when the business result matters.
7920
-
7921
- ## Choose the next task
7922
-
7923
- - [Send your first API request](/integrations/rest/send-request) to establish
7924
- environment, authentication and scope.
7925
- - [Read collections reliably](/integrations/rest/read-collections) to handle
7926
- pagination, filtering and visibility without losing or duplicating work.
7927
- - [Write and reconcile changes](/integrations/rest/write-and-reconcile) before
7928
- adding automatic retries to mutations.
7929
- - [Troubleshoot an API request](/integrations/rest/troubleshoot) using the
7930
- response class and correlation evidence.
7931
-
7932
- ## Keep the guide and reference in their roles
7933
-
7934
- This guide explains **why and in what order** to act. The API reference tells
7935
- you **what this application publishes**. Never copy an operation from another
7936
- application or infer a writable field from a response example. Start with
7937
- [one safe request](/integrations/rest/send-request), not with retries, bulk
7938
- processing or a production mutation.`,
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
- Your first request should prove four things and nothing more: you reached the
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
- | Before you begin | Successful result |
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
- ~~~mermaid
7959
- flowchart TD
7960
- accTitle: Prove an API connection from environment to business scope
7961
- accDescr: The integration owner selects a harmless documented read, targets one environment, sends an accepted credential, validates the response schema and organization scope, then confirms an expected refusal before allowing any write.
7962
- operation["Choose one harmless documented read"] --> environment["Set the exact environment origin"]
7963
- environment --> credential["Send one accepted dedicated credential"]
7964
- credential --> response{"Documented success response?"}
7965
- response -->|No| diagnose["Diagnose transport, authentication, authority or visibility"]
7966
- response -->|Yes| schema["Validate schema and intended organization"]
7967
- schema --> refusal["Confirm one expected refusal"]
7968
- refusal --> ready["Record proof and prepare one controlled write"]
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
- ## What you need
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
- - the server origin from this application's API reference;
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
- Choose a request that is easy for a human to recognize afterwards. A list or
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
- Copy the operation path and parameters from the API reference. For an API key,
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 "<server-origin>/<documented-path>" \
7989
- {{CONSUMER_FACT:source:consumer-fact:access-api-keys#curlRequestHeader}} \
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
- Replace only the placeholders shown by the exact API reference. Do not add a
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
- ## Verify the result
8054
+ ## Step 3: prove the boundary holds
8000
8055
 
8001
- Do not stop at “the request returned JSON.” Confirm:
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
- 1. the status is the documented success status;
8004
- 2. the response conforms to the operation schema;
8005
- 3. the returned organization and resource are the ones you intended;
8006
- 4. the credential can be identified and revoked without affecting another workload;
8007
- 5. logs contain correlation and outcome data but not the credential.
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 run one **negative proof**: request an operation this identity is
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 the request fails, use the status class before changing anything: 401 points
8015
- to authentication, 403 to authority, and 404 to identity, scope or resource
8016
- selection. Do not create a more privileged credential until you know which
8017
- decision failed.
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
- ## Before the first write
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
- Repeat the read from the deployment environment, then revoke or rotate the test
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
- Record the environment, operation identity, credential's non-secret identifier,
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: ['saas-technical-doc:engine-content/work-with-api-collections'],
8034
- markdown: `# Read collections reliably
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 the caller see **in its
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
- | Before you begin | Successful result |
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
- ## Begin with the business question
8096
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#collectionQueryMarkdown}}
8047
8097
 
8048
- Write down what the scan is meant to answer before choosing parameters. “Read
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
- - reconcile records changed during a defined operating window;
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
- Then open the exact operation in the API reference. Confirm the response item,
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
- ## Read one page at a time
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
- When the API reference declares page and limit parameters, the response carries
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
- ~~~mermaid
8070
- flowchart TD
8071
- accTitle: Consume a paginated collection without skipping work
8072
- accDescr: The integration requests one documented page, validates and processes it, records progress, and advances only while returned pagination metadata says another page exists.
8073
- request["Request documented page"] --> validate["Validate response and scope"]
8074
- validate --> process["Process page idempotently"]
8075
- process --> checkpoint["Persist progress"]
8076
- checkpoint --> more{"Returned metadata shows another page?"}
8077
- more -->|Yes| request
8078
- more -->|No| done["Collection pass complete"]
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
- Follow that lifecycle in order:
8122
+ Three rules keep a scan correct:
8082
8123
 
8083
- 1. Request the first documented page with an explicit, supported limit and only
8084
- the filters needed for the business question.
8085
- 2. Validate the response envelope and each item before using its fields. Check
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
- ## Continue safely
8128
+ ## Search and filter
8152
8129
 
8153
- - [Write and reconcile changes](/integrations/rest/write-and-reconcile) before
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
- A write is complete only when the integration knows the authoritative outcome.
8167
- An HTTP method does not by itself make a mutation safe to repeat, and a client
8168
- timeout does not prove that nothing changed.
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
- | Before you begin | Successful result |
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
- ## Define the business identity of the change
8141
+ ~~~text
8142
+ ?createdAt[startDate]=2026-09-01T00:00:00Z&createdAt[endDate]=2026-09-30T23:59:59Z
8143
+ ~~~
8175
8144
 
8176
- Before sending the request, decide which stable application or external
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
- Separate three identifiers where the business process has them:
8147
+ ## Counts and summaries
8183
8148
 
8184
- - the **request attempt**, which can happen more than once;
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
- Do not invent an idempotency header or client token unless the exact operation
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
- ## Prepare one controlled write
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
- 1. Read the target resource and record the fields that form the expected
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
- ~~~mermaid
8208
- stateDiagram-v2
8209
- accTitle: Reconcile a state-changing API request
8210
- accDescr: A prepared change is sent once. A confirmed response records success or refusal. A timeout or lost response enters an ambiguous state that must be resolved by reading authoritative application state before retrying.
8211
- [*] --> Prepared
8212
- Prepared --> Sent
8213
- Sent --> Confirmed: documented success
8214
- Sent --> Refused: unambiguous failure
8215
- Sent --> Ambiguous: timeout or connection loss
8216
- Ambiguous --> Confirmed: authoritative state shows applied
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
- The **Ambiguous** state is operationally important. It means the client cannot
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
- ## Reconcile an ambiguous outcome
8170
+ ## Send the change
8228
8171
 
8229
- When the connection closes, times out or loses the response:
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
- 1. Stop automatic retries for that business change.
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
- For create operations, reconcile using a documented stable business key or a
8242
- search that can identify the new record. If no reliable lookup exists, the
8243
- operation is not yet safe for unattended retries.
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
- ## Treat failures by meaning
8189
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#concurrencyMarkdown}}
8246
8190
 
8247
- - **Validation failure:** correct the request; do not retry unchanged.
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
- A successful write can still be the wrong business result—for example, it may
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
- ## Protect concurrent work
8195
+ A write the application will not perform answers with the standard error envelope. Three cases matter for a writer:
8262
8196
 
8263
- If people or other integrations can change the same record, read-modify-write
8264
- logic needs a conflict strategy. Use the operation's documented version,
8265
- precondition or conflict semantics when available. Otherwise minimize the
8266
- fields written, re-read before overwriting, and route conflicting state for
8267
- review. A blind retry after **409** can erase another actor's valid change.
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
- ## Preserve useful evidence
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
- Retain the intended change identity, operation, target scope, attempt number,
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
- The production workflow is ready only when operators can answer: **What did we
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
- ## Continue safely
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
- - [Read collections reliably](/integrations/rest/read-collections) when writes
8284
- are driven from a collection or need a later reconciliation pass.
8285
- - [Troubleshoot an API request](/integrations/rest/troubleshoot) to classify a
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: ['saas-technical-doc:engine-content/troubleshoot-api-request'],
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
- Begin with what the application actually returned. Replacing credentials,
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
- | Before you begin | Successful result |
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
- ## Keep the original failure intact
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
- First copy the non-secret evidence from the failed attempt. Do not rotate the
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
- ~~~mermaid
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
- ## Diagnose one boundary at a time
8245
+ ## No HTTP response at all
8333
8246
 
8334
- | Symptom | Most likely boundary | First checks |
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
- ## Check the boundaries in order
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
- ### 1. Environment and transport
8253
+ ## 401: the credential was not accepted
8348
8254
 
8349
- Confirm the server origin belongs to the intended deployment. Preserve the DNS,
8350
- TLS and timeout result. A connection refusal, certificate failure and client
8351
- timeout are different observations even though none returns an application
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
- ### 2. Request contract
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
- Compare the method, content type, parameters, body and enum values with the
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
- ### 3. Credential and identity
8263
+ The application knows who is calling and refuses this operation in this scope.
8362
8264
 
8363
- For **401**, confirm the header scheme, credential type, expiry or revocation
8364
- state, and that the credential belongs to this environment. Do not test by
8365
- substituting an administrator's credential: that hides the workload identity
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
- ### 4. Authority and visibility
8269
+ ## 404: the record is not visible here
8369
8270
 
8370
- For **403**, the identity is known but cannot perform this operation in this
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
- ### 5. Current state and retry safety
8273
+ ## 409 and 422: the current state refuses the change
8377
8274
 
8378
- For **409**, read the resource and resolve the conflicting state. For **429**,
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
- ## Build a safe investigation record
8277
+ ## 429: too many requests
8385
8278
 
8386
- Capture the time, environment, operation identity, caller identity reference,
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
- For a write with no response, investigate the authoritative resource before
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
- ## Prove the repair
8283
+ ## 5xx: the application failed
8396
8284
 
8397
- Repeat the smallest safe request with one change only. Confirm the expected
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
- Escalate with the redacted investigation record, the exact operation reference,
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
- ## Continue safely
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
- - [Send your first API request](/integrations/rest/send-request) to rebuild the
8410
- connection proof from a harmless read.
8411
- - [Write and reconcile changes](/integrations/rest/write-and-reconcile) before
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: ['saas-technical-doc:engine-content/webhooks'],
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
- Use a webhook when another system should react after this application publishes
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
- | Before you begin | Successful result |
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
- ## Decide whether an event should drive the work
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
- | Situation | Better starting point | Reason |
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
- ~~~mermaid
8461
- sequenceDiagram
8462
- accTitle: Receive and apply a webhook delivery safely
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
- The receiver must verify before trusting or parsing the event. A success
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
- ## Split responsibilities deliberately
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
- | Application sends | Receiver must do |
8323
+ ## What your receiver must do
8324
+
8325
+ | Step | Why it cannot be skipped |
8483
8326
  | --- | --- |
8484
- | Signed raw payload and delivery identity | Verify the signature and body hash before trusting fields |
8485
- | Delivery attempts with bounded retry behavior | Atomically recognize a previously accepted delivery |
8486
- | HTTP result and limited response evidence | Return quickly after durable acceptance and process longer work asynchronously |
8487
- | Delivery status for operations | Reconcile the downstream business effect after ambiguous processing |
8488
-
8489
- Do not acknowledge before the delivery is durably safe to process. Do not keep
8490
- the application connection open while performing a long external workflow.
8491
- Durable queueing between those two moments makes fast acknowledgement and safe
8492
- recovery compatible.
8493
-
8494
- ## Follow the journey
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: ['saas-technical-doc:engine-content/configure-and-test-webhook-endpoint'],
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
- Create a receiver that is publicly reachable, preserves the raw request body
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
- Create a separate endpoint for each receiver lifecycle that needs independent
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
- Keep test and production receivers separate. Confirm that the URL is the final
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
- ## Prepare the receiver
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
- Before adding the endpoint:
8360
+ ## Register the endpoint
8545
8361
 
8546
- - accept the documented POST content type and retain the exact body bytes;
8547
- - read the signature from **x-wildo-webhook-signature**;
8548
- - load the application's published webhook verification key through the
8549
- documented configuration surface;
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
- ~~~mermaid
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
- Interpret the test outcome precisely:
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
- | Outcome | Meaning | Next action |
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
- Run four checks before enablement:
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
- - a valid signed test is durably accepted and answered with 2xx;
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
- ## Enable in stages
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
- Enable one endpoint in a non-production organization, trigger one known event
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
- For the first real event, confirm all of the following:
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
- 1. the delivery belongs to the intended scope and published event;
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
- If any check fails, disable the endpoint while preserving the test and delivery
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
- ## Retain safe operating evidence
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
- Keep the endpoint's non-secret identifier and description, scope, destination
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
- - [Verify a webhook delivery](/integrations/webhooks/verify-delivery) for the
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: ['saas-technical-doc:engine-content/verify-webhook-delivery'],
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 both the signature token and
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
- Many web servers parse JSON automatically. Configure the webhook route to retain
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
- Read the signature only from **x-wildo-webhook-signature**. Reject a missing or
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
- ## Verification sequence
8411
+ A decoded token payload looks like this:
8689
8412
 
8690
- 1. Read **x-wildo-webhook-signature** and reject a missing or malformed value.
8691
- 2. Verify the token with the application's published webhook public key.
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
- ## Know what each signed value proves
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
- | Value | Receiver decision |
8719
- | --- | --- |
8720
- | Signature, algorithm and key identifier | The token was produced by the expected application signing key under the pinned **ES256** algorithm |
8721
- | Issuer and audience | The token belongs to the expected application trust boundary and this receiver purpose |
8722
- | Issued time and expiry | The attempt is within the deliberately accepted time window |
8723
- | Body hash and hash algorithm | The raw bytes received are the bytes that were signed for this attempt |
8724
- | Event channel, resource and operation | The notification belongs on the receiver route and handler selected for it |
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
- Validate all required claims as one policy. A correctly signed token for another
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
- The body hash identifies bytes; it is not the delivery identity. The token
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
- Claim **jti** atomically in the same acceptance boundary that records or queues
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
- ## Reject without creating a second vulnerability
8433
+ ~~~javascript
8434
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#verifierSampleNode}}
8435
+ ~~~
8740
8436
 
8741
- Return a generic non-2xx response for invalid signature, hash or claims. Do not
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
- For a duplicate **jti**, return the stable outcome your receiver design has
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
- ## Handle key and deployment changes deliberately
8441
+ ## Route by claims, not by body
8751
8442
 
8752
- When verification starts failing after a deployment or key change, compare the
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
- Test verification with controlled cases:
8445
+ ## Prove it before production
8760
8446
 
8761
- - one valid signed delivery reaches the durable queue;
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
- Do not weaken algorithm checks, skip expiry or parse and reserialize JSON to
8768
- repair a verification failure. Those changes remove the security boundary
8769
- rather than diagnosing it.
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
- ## Continue safely
8455
+ ## When keys or deployments change
8772
8456
 
8773
- - [Configure and test an endpoint](/integrations/webhooks/configure-and-test)
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: ['saas-technical-doc:engine-content/operate-webhook-deliveries'],
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
- Webhook delivery is asynchronous. The application creates one durable delivery
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
- Operating webhooks therefore means reconciling **two lifecycles**: delivery from
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
- | Before you begin | Successful result |
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
- ## Understand the delivery states
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
- ~~~mermaid
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
- Read each state as an operating instruction:
8478
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#retryMarkdown}}
8816
8479
 
8817
- | State | What it means | What an operator should do |
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 the signed **jti** atomically before creating the business effect.
8837
- - Return 2xx only after the request is verified and safely accepted.
8838
- - Move slow work to a durable queue.
8839
- - Make downstream processing idempotent as a second line of protection.
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
- The application records only a bounded response body, currently up to 4 KiB.
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 both sides of acceptance
8491
+ ## Monitor
8848
8492
 
8849
- For the application delivery lifecycle, monitor:
8493
+ On the application side, watch:
8850
8494
 
8851
- - pending age and whether the next-attempt time continues to advance;
8852
- - terminal failures by endpoint and response class;
8853
- - a sudden increase in 408, 429 or 5xx outcomes;
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
- For the receiver lifecycle, monitor:
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
- If the receiver already completed the business effect, do not retry merely to
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
- ## Treat endpoint changes as new operating risk
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
- Changing an endpoint URL affects future delivery attempts, while historical
8898
- rows retain the destination snapshot used when they were created. After a URL
8899
- or receiver deployment change, test the configured endpoint, trigger one
8900
- controlled event and monitor both delivery and downstream completion before
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
- Disable an endpoint when the receiver cannot safely accept traffic. Disabling
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
- ## Retain evidence without retaining secrets
8513
+ ## Endpoint changes and outages
8908
8514
 
8909
- Keep the scope, endpoint and delivery identifiers, **jti**, event context,
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
- ## Continue safely
8517
+ ## Retention
8916
8518
 
8917
- - [Troubleshoot webhook deliveries](/integrations/webhooks/troubleshoot) to
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: ['saas-technical-doc:engine-content/troubleshoot-webhook-deliveries'],
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 recorded delivery state, attempt count and HTTP outcome. Do not
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
- | Before you begin | Successful result |
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
- ## Locate the first failed boundary
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
- ~~~mermaid
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
- Work from left to right. A later symptom does not erase an earlier failure. For
8958
- example, manually creating the downstream record can repair the business state,
8959
- but it does not explain why the receiver returned 2xx before durable acceptance.
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
- ## Diagnose by symptom
8550
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#retryMarkdown}}
8962
8551
 
8963
- | Symptom | Likely boundary | What to inspect |
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
- ## Continue safely
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
- - [Configure and test an endpoint](/integrations/webhooks/configure-and-test)
9050
- after changing a URL, deployment or signing configuration.
9051
- - [Verify a webhook delivery](/integrations/webhooks/verify-delivery) to repair
9052
- the raw-byte, claim or deduplication boundary.
9053
- - [Operate webhook deliveries](/integrations/webhooks/operate-deliveries) before
9054
- explicit retries or restoring ordinary traffic.`,
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: ['saas-technical-doc:engine-content/mcp-integrations'],
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 <MCP_SERVER_URL> HTTP/1.1
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: ['saas-technical-doc:engine-content/a2a-integrations'],
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": "<APPLICATION_AGENT_URL>", "transport": "JSONRPC", "version": "1.0" },
9383
- { "url": "<APPLICATION_AGENT_URL>", "transport": "JSONRPC", "version": "0.2.5" }
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": {