@wildo-ai/saas-technical-doc 1.1.1 → 1.1.3

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 (123) 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 +111 -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 +165 -0
  12. package/dist/esm/companion/application-documentation/application-connection-documentation.js.map +1 -0
  13. package/dist/esm/companion/application-documentation/application-domain-documentation.d.ts +133 -0
  14. package/dist/esm/companion/application-documentation/application-domain-documentation.d.ts.map +1 -0
  15. package/dist/esm/companion/application-documentation/application-domain-documentation.js +243 -0
  16. package/dist/esm/companion/application-documentation/application-domain-documentation.js.map +1 -0
  17. package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts +48 -0
  18. package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts.map +1 -0
  19. package/dist/esm/companion/application-documentation/application-integration-documentation.js +86 -0
  20. package/dist/esm/companion/application-documentation/application-integration-documentation.js.map +1 -0
  21. package/dist/esm/companion/application-documentation/application-organization-role-documentation.d.ts +30 -0
  22. package/dist/esm/companion/application-documentation/application-organization-role-documentation.d.ts.map +1 -1
  23. package/dist/esm/companion/application-documentation/application-organization-role-documentation.js +38 -0
  24. package/dist/esm/companion/application-documentation/application-organization-role-documentation.js.map +1 -1
  25. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts +82 -4
  26. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts.map +1 -1
  27. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js +412 -219
  28. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js.map +1 -1
  29. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts +10 -0
  30. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts.map +1 -1
  31. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js +9 -1
  32. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js.map +1 -1
  33. package/dist/esm/companion/application-documentation/technical-documentation-publication-policy.js +1 -1
  34. package/dist/esm/companion/application-documentation/technical-documentation-publication-policy.js.map +1 -1
  35. package/dist/esm/companion/index.d.ts +6 -1
  36. package/dist/esm/companion/index.d.ts.map +1 -1
  37. package/dist/esm/companion/index.js +6 -1
  38. package/dist/esm/companion/index.js.map +1 -1
  39. package/dist/esm/companion/manual-controller-route-projection.d.ts +112 -0
  40. package/dist/esm/companion/manual-controller-route-projection.d.ts.map +1 -0
  41. package/dist/esm/companion/manual-controller-route-projection.js +249 -0
  42. package/dist/esm/companion/manual-controller-route-projection.js.map +1 -0
  43. package/dist/esm/companion/openapi-generator.d.ts +16 -0
  44. package/dist/esm/companion/openapi-generator.d.ts.map +1 -1
  45. package/dist/esm/companion/openapi-generator.js +497 -32
  46. package/dist/esm/companion/openapi-generator.js.map +1 -1
  47. package/dist/esm/companion/operation-projection.schemas.d.ts +44 -0
  48. package/dist/esm/companion/operation-projection.schemas.d.ts.map +1 -1
  49. package/dist/esm/companion/operation-projection.schemas.js +37 -0
  50. package/dist/esm/companion/operation-projection.schemas.js.map +1 -1
  51. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.d.ts +7 -1
  52. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.d.ts.map +1 -1
  53. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js +44 -20
  54. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js.map +1 -1
  55. package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.d.ts.map +1 -1
  56. package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.js +8 -1
  57. package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.js.map +1 -1
  58. package/dist/esm/companion/rendering/technical-documentation-markdown-links.d.ts +28 -0
  59. package/dist/esm/companion/rendering/technical-documentation-markdown-links.d.ts.map +1 -0
  60. package/dist/esm/companion/rendering/technical-documentation-markdown-links.js +52 -0
  61. package/dist/esm/companion/rendering/technical-documentation-markdown-links.js.map +1 -0
  62. package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.d.ts +0 -9
  63. package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.d.ts.map +0 -1
  64. package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.js +0 -106
  65. package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.js.map +0 -1
  66. package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts +1 -0
  67. package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts.map +1 -1
  68. package/dist/esm/companion/rendering/technical-documentation-render-model.js +112 -88
  69. package/dist/esm/companion/rendering/technical-documentation-render-model.js.map +1 -1
  70. package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.d.ts +10 -0
  71. package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.d.ts.map +1 -1
  72. package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.js +10 -15
  73. package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.js.map +1 -1
  74. package/dist/esm/companion/technical-documentation-capture-execution-port.d.ts +27 -1
  75. package/dist/esm/companion/technical-documentation-capture-execution-port.d.ts.map +1 -1
  76. package/dist/esm/companion/technical-documentation-capture-execution-port.js.map +1 -1
  77. package/dist/esm/companion/technical-documentation-diagram-definitions.d.ts +33 -0
  78. package/dist/esm/companion/technical-documentation-diagram-definitions.d.ts.map +1 -0
  79. package/dist/esm/companion/technical-documentation-diagram-definitions.js +54 -0
  80. package/dist/esm/companion/technical-documentation-diagram-definitions.js.map +1 -0
  81. package/dist/esm/companion/technical-documentation-diagram-materializer.d.ts +10 -18
  82. package/dist/esm/companion/technical-documentation-diagram-materializer.d.ts.map +1 -1
  83. package/dist/esm/companion/technical-documentation-diagram-materializer.js +9 -39
  84. package/dist/esm/companion/technical-documentation-diagram-materializer.js.map +1 -1
  85. package/dist/esm/config/wildo-tech-doc-config.schemas.d.ts +8 -4
  86. package/dist/esm/config/wildo-tech-doc-config.schemas.d.ts.map +1 -1
  87. package/dist/esm/config/wildo-tech-doc-config.schemas.js +8 -4
  88. package/dist/esm/config/wildo-tech-doc-config.schemas.js.map +1 -1
  89. package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts +105 -122
  90. package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts.map +1 -1
  91. package/dist/esm/content/application-consumer-documentation-content.techdoc.js +934 -2161
  92. package/dist/esm/content/application-consumer-documentation-content.techdoc.js.map +1 -1
  93. package/dist/esm/runtime/DocsAuthContext.d.ts +16 -1
  94. package/dist/esm/runtime/DocsAuthContext.d.ts.map +1 -1
  95. package/dist/esm/runtime/DocsAuthContext.js +18 -2
  96. package/dist/esm/runtime/DocsAuthContext.js.map +1 -1
  97. package/dist/esm/runtime/decode-jwt-claims.d.ts +7 -4
  98. package/dist/esm/runtime/decode-jwt-claims.d.ts.map +1 -1
  99. package/dist/esm/runtime/decode-jwt-claims.js +7 -4
  100. package/dist/esm/runtime/decode-jwt-claims.js.map +1 -1
  101. package/dist/esm/runtime/docs-auth-session.schemas.d.ts +16 -5
  102. package/dist/esm/runtime/docs-auth-session.schemas.d.ts.map +1 -1
  103. package/dist/esm/runtime/docs-auth-session.schemas.js +16 -5
  104. package/dist/esm/runtime/docs-auth-session.schemas.js.map +1 -1
  105. package/dist/esm/runtime/frontend-provider-registry.techdoc.d.ts +35 -13
  106. package/dist/esm/runtime/frontend-provider-registry.techdoc.d.ts.map +1 -1
  107. package/dist/esm/runtime/frontend-provider-registry.techdoc.js +28 -19
  108. package/dist/esm/runtime/frontend-provider-registry.techdoc.js.map +1 -1
  109. package/dist/esm/runtime/index.d.ts +1 -0
  110. package/dist/esm/runtime/index.d.ts.map +1 -1
  111. package/dist/esm/runtime/index.js +1 -0
  112. package/dist/esm/runtime/index.js.map +1 -1
  113. package/dist/esm/runtime/use-docs-auth-session.d.ts +9 -7
  114. package/dist/esm/runtime/use-docs-auth-session.d.ts.map +1 -1
  115. package/dist/esm/runtime/use-docs-auth-session.js +9 -7
  116. package/dist/esm/runtime/use-docs-auth-session.js.map +1 -1
  117. package/dist/esm/runtime/use-docs-provider-sdks.d.ts +21 -0
  118. package/dist/esm/runtime/use-docs-provider-sdks.d.ts.map +1 -0
  119. package/dist/esm/runtime/use-docs-provider-sdks.js +49 -0
  120. package/dist/esm/runtime/use-docs-provider-sdks.js.map +1 -0
  121. package/dist/tsconfig.build.tsbuildinfo +1 -1
  122. package/package.json +6 -5
  123. package/dist/esm/.builder.pid +0 -9
@@ -7,130 +7,113 @@
7
7
  * generated application may consume them.
8
8
  *
9
9
  * The fragments describe customer-facing product domains and integration
10
- * journeys, not a generic “technical guides” catalogue. Application-domain
11
- * use cases are deliberately absent until the app-creator flow owns their
12
- * authoring and acceptance.
10
+ * journeys, not a generic “technical guides” catalogue.
11
+ *
12
+ * This header used to say application-domain use cases were "deliberately absent until the
13
+ * app-creator flow owns their authoring and acceptance". That sentence is retired, and it is worth
14
+ * keeping the reason: stated as policy, the largest gap in the whole site read as a decision
15
+ * rather than a defect, and survived every review for exactly that long. A customer could learn
16
+ * how to verify a webhook signature before learning what the product manages.
17
+ *
18
+ * `what-this-application-manages.md` closes it WITHOUT anyone authoring per-application prose,
19
+ * which is what the original reservation was protecting: the page is engine-authored and every
20
+ * fact in it is projected from sources the application already accepted — its own resource
21
+ * registry, its declared API-reference categories, and each specification's purpose and lifecycle.
22
+ *
23
+ * What remains genuinely deferred is a page PER resource. Those need a variable unit set, and the
24
+ * renderer's route table is deliberately fixed (see `pathForUnit`), so an application-owned unit
25
+ * still fails closed rather than acquiring an implicit route.
13
26
  */
14
27
  export const APPLICATION_CONSUMER_DOCUMENTATION_AUTHORED_CONTENT_V1 = [
15
28
  {
16
29
  managedPath: 'get-started.md',
17
30
  unitRef: 'technical-documentation:unit/application-orientation',
18
- sourceRefs: ['saas-technical-doc:engine-content/application-orientation'],
31
+ sourceRefs: [
32
+ 'saas-technical-doc:engine-content/application-orientation',
33
+ 'source:companion-projection:application-connection',
34
+ ],
19
35
  markdown: `# Get started
20
36
 
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.
37
+ 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
38
 
26
39
  ## What do you need to do?
27
40
 
28
- | Your goal | Begin here | You will learn |
41
+ | You need to | Start here | You will find |
29
42
  | --- | --- | --- |
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.
43
+ | Understand what this application is for | [What {{APPLICATION_NAME}} manages](/what-this-application-manages) | The things it manages, in its own words, and who may act on each |
44
+ | 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 |
45
+ | 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 |
46
+ | 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 |
47
+ | Be notified when something changes | [Webhooks](/integrations/webhooks) | Signed deliveries, a receiver you can run, retries and operations |
48
+ | Build in n8n, Zapier or Make | [Automation platforms](/integrations/automation-platforms) | The exact settings for each tool |
49
+ | 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 |
50
+ | Investigate who did what, or export security evidence | [Security and audit](/security-and-audit) | The audit trail, exports and delivery to your SIEM |
51
+ | Look up one operation, field or role | [API reference](/api) | The exact contract of every operation, generated from the application itself |
62
52
 
63
- ## Before you change anything
53
+ 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.
64
54
 
65
- Confirm four things before an administrative or integration write:
55
+ ## Three things to know before you change anything
66
56
 
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.
57
+ 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.
58
+ 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.
59
+ 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.
77
60
 
78
- ## Three useful first journeys
61
+ ## How the documentation is organised
79
62
 
80
- ### Help a person gain the right access
63
+ - **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.
64
+ - **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.
65
+ - Every guide links to the reference it relies on, and no guide repeats a value the reference owns.
81
66
 
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.
87
-
88
- ### Connect another system safely
67
+ ## When you ask for help
89
68
 
90
- Start with [Integrations](/integrations). Choose the connection surface from
91
- the direction and shape of the work:
69
+ 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.`,
70
+ },
71
+ {
72
+ managedPath: 'what-this-application-manages.md',
73
+ unitRef: 'technical-documentation:unit/what-this-application-manages',
74
+ sourceRefs: [
75
+ 'saas-technical-doc:engine-content/what-this-application-manages',
76
+ 'source:companion-projection:application-connection',
77
+ 'source:companion-projection:application-domain',
78
+ ],
79
+ markdown: `# What {{APPLICATION_NAME}} manages
92
80
 
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.
81
+ Every other page here explains how to reach this application, administer it, integrate with it or
82
+ pay for it. This one says what it is FOR.
99
83
 
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.
84
+ Everything below is the application's own: the things it stores, the words it uses for them, and
85
+ who may act on each. None of it is written by the framework — it is read from the application's own
86
+ resource definitions, so it says what this deployment actually publishes rather than what a product
87
+ of this kind usually does.
102
88
 
103
- ### Investigate an unexpected result
89
+ ## What it manages
104
90
 
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.
91
+ {{APPLICATION_DOMAIN:domainOverview}}
111
92
 
112
- ## Guides and references have different jobs
93
+ ## Each one in detail
113
94
 
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.
95
+ {{APPLICATION_DOMAIN:domainResourceDetails}}
121
96
 
122
- ## When you ask for help
97
+ ## Where to go next
123
98
 
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.`,
99
+ | You need to | Go to |
100
+ | --- | --- |
101
+ | Read or change any of this from your own code | [Send your first API request](/integrations/rest/send-request) |
102
+ | Look up the exact fields, arguments and responses | [API reference](/api) |
103
+ | Be notified when one of these changes | [Webhooks](/integrations/webhooks) |
104
+ | Let an AI assistant work with them | [AI coding tools](/integrations/ai-coding-tools) |
105
+ | Change who may act on them | [Roles in this application](/user-administration/roles-and-permissions) |
106
+
107
+ The names above are the API's own, so a name you read here is the one you will send, the one an
108
+ error message will quote back, and the one a webhook payload will carry.`,
128
109
  },
129
110
  {
130
111
  managedPath: 'access-and-identity/overview.md',
131
112
  unitRef: 'technical-documentation:unit/authentication',
132
113
  sourceRefs: [
133
114
  'saas-technical-doc:engine-content/authentication',
115
+ 'source:companion-projection:application-authentication',
116
+ 'source:companion-projection:application-connection',
134
117
  'source:consumer-fact:access-authentication-methods',
135
118
  ],
136
119
  markdown: `# Access and identity
@@ -173,13 +156,13 @@ receive a refusal.
173
156
  | Run an unattended workload | [API keys](/access-and-identity/api-keys) | A person’s browser session |
174
157
  | 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
158
 
176
- ## What a sign-in screen may offer
159
+ ## What {{APPLICATION_NAME}} accepts to sign in
177
160
 
178
- {{CONSUMER_FACT:source:consumer-fact:access-authentication-methods#markdown}}
161
+ {{APPLICATION_AUTHENTICATION:enabledSignInMethods}}
179
162
 
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.
163
+ ### The full catalogue, for comparison
164
+
165
+ {{CONSUMER_FACT:source:consumer-fact:access-authentication-methods#markdown}}
183
166
 
184
167
  ## What happens after a credential is presented
185
168
 
@@ -211,6 +194,7 @@ Use the generated API reference for the exact operation-level contract.`,
211
194
  unitRef: 'technical-documentation:unit/sign-in-and-mfa',
212
195
  sourceRefs: [
213
196
  'saas-technical-doc:engine-content/sign-in-and-mfa',
197
+ 'source:companion-projection:application-authentication',
214
198
  'source:consumer-fact:access-authentication-methods',
215
199
  ],
216
200
  markdown: `# Sign in and multifactor authentication
@@ -250,6 +234,22 @@ authorization are separate. A correct password, passkey or provider response
250
234
  can start a session while the selected organization or role still refuses the
251
235
  person's intended work.
252
236
 
237
+ ## What this application requires of you
238
+
239
+ {{APPLICATION_AUTHENTICATION:enabledSignInMethods}}
240
+
241
+ A second factor is a separate decision from the method that starts the sign-in:
242
+
243
+ {{APPLICATION_AUTHENTICATION:multifactorPolicy}}
244
+
245
+ If a password is one of the methods above, it must satisfy these rules:
246
+
247
+ {{APPLICATION_AUTHENTICATION:passwordRules}}
248
+
249
+ ## How long a session lasts, and what failed attempts cost
250
+
251
+ {{APPLICATION_AUTHENTICATION:sessionAndLockout}}
252
+
253
253
  ## Follow the sign-in journey
254
254
 
255
255
  | Your next task | Start here | Successful result |
@@ -327,7 +327,8 @@ follow the order presented for this attempt.
327
327
  1. Open the sign-in page for the intended environment and verify the expected
328
328
  application identity before entering a credential.
329
329
  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.
330
+ from the catalogue that is absent from the sign-in screen is not available for
331
+ this attempt.
331
332
  3. Complete the first factor or organization SSO journey using the person's own
332
333
  credential and device.
333
334
  4. Complete a required second-factor challenge. If enrollment is requested,
@@ -361,7 +362,10 @@ recovery value, full provider response or session cookie in support evidence.`,
361
362
  {
362
363
  managedPath: 'access-and-identity/mfa-enrollment-and-recovery.md',
363
364
  unitRef: 'technical-documentation:unit/mfa-enrollment-and-recovery',
364
- sourceRefs: ['saas-technical-doc:engine-content/mfa-enrollment-and-recovery'],
365
+ sourceRefs: [
366
+ 'saas-technical-doc:engine-content/mfa-enrollment-and-recovery',
367
+ 'source:companion-projection:application-authentication',
368
+ ],
365
369
  markdown: `# Enroll and recover multifactor authentication
366
370
 
367
371
  Use multifactor authentication with a factor controlled by the person signing
@@ -375,6 +379,10 @@ here.
375
379
  | --- | --- |
376
380
  | 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
381
 
382
+ ## What this application asks for, and what enrolment gives you
383
+
384
+ {{APPLICATION_AUTHENTICATION:multifactorPolicy}}
385
+
378
386
  ~~~mermaid
379
387
  flowchart TD
380
388
  accTitle: Enroll a factor and preserve a safe recovery path
@@ -521,9 +529,8 @@ organization.
521
529
  organization-scope** problem.
522
530
  - A resource that appears missing can be genuinely absent or outside the
523
531
  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.
532
+ - A method listed in the catalogue is not necessarily enabled for this person and
533
+ organization. The current sign-in screen is the availability evidence.
527
534
 
528
535
  Never use an administrator account, another person's session or a machine
529
536
  credential to make a user action succeed. That hides the real problem and
@@ -1708,6 +1715,7 @@ value or a screenshot containing them.`,
1708
1715
  unitRef: 'technical-documentation:unit/oauth-provider',
1709
1716
  sourceRefs: [
1710
1717
  'saas-technical-doc:engine-content/oauth-provider',
1718
+ 'source:companion-projection:application-connection',
1711
1719
  'source:consumer-fact:access-oauth-clients',
1712
1720
  ],
1713
1721
  markdown: `# OAuth provider and delegated access
@@ -1784,12 +1792,16 @@ token, key, user-information, revocation and logout endpoints belong to the API
1784
1792
  issuer. They are deliberately not all under one hand-constructed path.
1785
1793
 
1786
1794
  ~~~bash
1787
- APPLICATION_OAUTH_ISSUER="https://api.example.invalid"
1788
-
1789
1795
  curl --fail --silent --show-error \
1790
- "$APPLICATION_OAUTH_ISSUER/.well-known/oauth-authorization-server"
1796
+ "{{APPLICATION_CONNECTION_VALUE:oauthMetadataUrl}}"
1791
1797
  ~~~
1792
1798
 
1799
+ That URL is not the issuer with the well-known segment appended, and the difference matters. RFC
1800
+ 8414 inserts \`/.well-known/oauth-authorization-server\` **between the host and the issuer's path**,
1801
+ while OpenID Connect discovery appends its suffix to the issuer — the opposite construction. This
1802
+ application's issuer is \`{{APPLICATION_CONNECTION_VALUE:oauthIssuer}}\`, and it serves the same
1803
+ metadata document at both locations, so a client that derives either way finds it.
1804
+
1793
1805
  Check the returned \`issuer\` exactly. Then read the advertised grants,
1794
1806
  \`code_challenge_methods_supported\`, token authentication methods, registration
1795
1807
  endpoint and resource-indicator support before configuring the client. A
@@ -2299,7 +2311,6 @@ complete authorization response.`,
2299
2311
  unitRef: 'technical-documentation:unit/user-administration',
2300
2312
  sourceRefs: [
2301
2313
  'saas-technical-doc:engine-content/user-administration',
2302
- 'source:consumer-fact:organization-membership-administration',
2303
2314
  ],
2304
2315
  markdown: `# User administration
2305
2316
 
@@ -2326,7 +2337,7 @@ flowchart LR
2326
2337
 
2327
2338
  ## Choose how your organization manages users
2328
2339
 
2329
- {{CONSUMER_FACT:source:consumer-fact:organization-membership-administration#markdown}}
2340
+ 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
2341
 
2331
2342
  Choose **manual management** when an administrator should invite people, choose
2332
2343
  their access, pause access, or remove them directly in the application. Each
@@ -2453,7 +2464,6 @@ credentials into tickets or audit notes.`,
2453
2464
  unitRef: 'technical-documentation:unit/invite-and-onboard-user',
2454
2465
  sourceRefs: [
2455
2466
  'saas-technical-doc:engine-content/invite-and-onboard-user',
2456
- 'source:consumer-fact:organization-membership-administration',
2457
2467
  ],
2458
2468
  markdown: `# Invite and onboard a user
2459
2469
 
@@ -2525,7 +2535,7 @@ Then confirm all three choices with the person’s manager or access owner:
2525
2535
 
2526
2536
  ## Send and follow the invitation
2527
2537
 
2528
- {{CONSUMER_FACT:source:consumer-fact:organization-membership-administration#markdown}}
2538
+ 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
2539
 
2530
2540
  After you send it, find the person in the member list and check that the state
2531
2541
  is **Invited**. At this point, the person has not received active organization
@@ -2534,7 +2544,10 @@ email filtering rules, then use the published resend action for the pending
2534
2544
  invitation. Resending replaces the earlier acceptance link; do not create a
2535
2545
  second invitation for the same person just to send another email.
2536
2546
 
2537
- The invitation link is valid for **seven days** and can be used once. If the
2547
+ The invitation link is valid for **seven days** and can be used once; resend
2548
+ it to issue a fresh link. An invitation that is neither accepted nor revoked
2549
+ is removed automatically after **30 days**, so a person who never responds
2550
+ does not stay listed as invited indefinitely. If the
2538
2551
  link is expired, revoked, already consumed, or no longer matches an invited
2539
2552
  membership, the acceptance must fail rather than activating a different state.
2540
2553
 
@@ -2583,6 +2596,7 @@ offer when the underlying access decision changes.`,
2583
2596
  unitRef: 'technical-documentation:unit/organization-roles',
2584
2597
  sourceRefs: [
2585
2598
  'saas-technical-doc:engine-content/organization-roles',
2599
+ 'source:companion-projection:application-administration',
2586
2600
  'source:companion-projection:organization-roles',
2587
2601
  'source:consumer-fact:organization-membership-administration',
2588
2602
  ],
@@ -2628,6 +2642,13 @@ The role descriptions below explain their intended use and boundary. A
2628
2642
  specific operation may impose a narrower rule, so the API reference remains
2629
2643
  authoritative when you need to know exactly who may call one operation.
2630
2644
 
2645
+ ## Who may administer membership here
2646
+
2647
+ The roles above say what each role is for. This says which of them may perform
2648
+ each membership action this application publishes:
2649
+
2650
+ {{APPLICATION_ADMINISTRATION:memberActions}}
2651
+
2631
2652
  ## Roles available in this application
2632
2653
 
2633
2654
  {{APPLICATION_ORGANIZATION_ROLES}}
@@ -2706,7 +2727,6 @@ different credential to bypass the refusal.`,
2706
2727
  unitRef: 'technical-documentation:unit/change-user-access',
2707
2728
  sourceRefs: [
2708
2729
  'saas-technical-doc:engine-content/change-user-access',
2709
- 'source:consumer-fact:organization-membership-administration',
2710
2730
  ],
2711
2731
  markdown: `# Change a user's access
2712
2732
 
@@ -2785,7 +2805,7 @@ flowchart TD
2785
2805
 
2786
2806
  ## How membership state affects authority
2787
2807
 
2788
- {{CONSUMER_FACT:source:consumer-fact:organization-membership-administration#markdown}}
2808
+ 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
2809
 
2790
2810
  Only an **Active** membership contributes organization authority. A role is not
2791
2811
  a job title: it is a named permission set evaluated in the selected
@@ -2866,6 +2886,7 @@ credential to bypass the refusal.`,
2866
2886
  unitRef: 'technical-documentation:unit/manage-organization-units',
2867
2887
  sourceRefs: [
2868
2888
  'saas-technical-doc:engine-content/manage-organization-units',
2889
+ 'source:companion-projection:application-administration',
2869
2890
  'source:companion-projection:organization-unit-aware-resources',
2870
2891
  'source:consumer-fact:organization-units',
2871
2892
  ],
@@ -2882,6 +2903,10 @@ organization settings. If it does not, use the **Organization units API
2882
2903
  reference** linked from this page. This guide explains the business decisions;
2883
2904
  the reference supplies the exact operations the application publishes.
2884
2905
 
2906
+ ## Who may administer organization units here
2907
+
2908
+ {{APPLICATION_ADMINISTRATION:organizationUnitActions}}
2909
+
2885
2910
  > **Start with the business decision.** Name the information or action that a
2886
2911
  > department must control before you build the hierarchy. If nothing in the
2887
2912
  > application is unit-aware, a unit is only a label and grants no useful access.
@@ -3160,7 +3185,6 @@ result with a representative member’s own account.`,
3160
3185
  unitRef: 'technical-documentation:unit/suspend-or-remove-user',
3161
3186
  sourceRefs: [
3162
3187
  'saas-technical-doc:engine-content/suspend-or-remove-user',
3163
- 'source:consumer-fact:organization-membership-administration',
3164
3188
  ],
3165
3189
  markdown: `# Suspend or remove a user
3166
3190
 
@@ -3215,7 +3239,7 @@ flowchart LR
3215
3239
  | 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
3240
  | The person has left this organization or must permanently lose this organization access | Remove the membership | End this organization relationship and its roles |
3217
3241
 
3218
- {{CONSUMER_FACT:source:consumer-fact:organization-membership-administration#markdown}}
3242
+ 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
3243
 
3220
3244
  ## Protect organization administration first
3221
3245
 
@@ -3283,7 +3307,6 @@ transition is not valid from the current membership state.`,
3283
3307
  unitRef: 'technical-documentation:unit/review-users-and-access',
3284
3308
  sourceRefs: [
3285
3309
  'saas-technical-doc:engine-content/review-users-and-access',
3286
- 'source:consumer-fact:organization-membership-administration',
3287
3310
  ],
3288
3311
  markdown: `# Review users and access
3289
3312
 
@@ -3329,7 +3352,7 @@ Start broad so an invited, suspended or inactive record is not omitted simply
3329
3352
  because it cannot currently authorize an operation. Then narrow the review to
3330
3353
  the access that carries the most business or administrative impact.
3331
3354
 
3332
- {{CONSUMER_FACT:source:consumer-fact:organization-membership-administration#markdown}}
3355
+ 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
3356
 
3334
3357
  ## Prepare the review
3335
3358
 
@@ -3558,6 +3581,7 @@ same membership or unit assignment.`,
3558
3581
  unitRef: 'technical-documentation:unit/scim-protocol-and-payloads',
3559
3582
  sourceRefs: [
3560
3583
  'saas-technical-doc:engine-content/scim-protocol-and-payloads',
3584
+ 'source:companion-projection:application-connection',
3561
3585
  'source:consumer-fact:organization-scim-provisioning',
3562
3586
  ],
3563
3587
  markdown: `# SCIM protocol and payload reference
@@ -3633,11 +3657,10 @@ and does not prove that the person can complete SSO.
3633
3657
 
3634
3658
  ## Understand the base URL
3635
3659
 
3636
- The configuration resource supplies the API-relative path
3637
- \`/api/v1/scim/v2\`. A provider needs the complete public URL:
3660
+ This is the complete base URL a provider needs. Everything below hangs off it:
3638
3661
 
3639
3662
  ~~~text
3640
- https://api.example.com/api/v1/scim/v2
3663
+ {{APPLICATION_CONNECTION_VALUE:scimBaseUrl}}
3641
3664
  ~~~
3642
3665
 
3643
3666
  The organization is not encoded in that URL. The bearer token identifies the
@@ -3712,9 +3735,8 @@ list—not a 404:
3712
3735
  }
3713
3736
  ~~~
3714
3737
 
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,
3738
+ Errors use the SCIM error media type and may include a \`scimType\` of
3739
+ \`invalidSyntax\`, \`invalidValue\`, \`uniqueness\` or \`tooMany\`. Preserve the HTTP status, SCIM type, redacted detail, operation,
3718
3740
  provider job identifier and time. Never preserve the bearer token.
3719
3741
 
3720
3742
  ## Standards used by this profile
@@ -3741,6 +3763,7 @@ application’s discovery response to understand the implemented profile.`,
3741
3763
  unitRef: 'technical-documentation:unit/scim-configure',
3742
3764
  sourceRefs: [
3743
3765
  'saas-technical-doc:engine-content/scim-configure',
3766
+ 'source:companion-projection:application-connection',
3744
3767
  'source:consumer-fact:organization-scim-provisioning',
3745
3768
  ],
3746
3769
  markdown: `# Configure SCIM provisioning
@@ -3800,7 +3823,7 @@ using it for automatic provisioning.
3800
3823
  "autoCreateUsers": true,
3801
3824
  "autoVerifyEmail": true,
3802
3825
  "autoDeactivateUsers": false,
3803
- "defaultRole": "organization-member",
3826
+ "defaultRole": "ORG_MEMBER",
3804
3827
  "attributeMapping": {
3805
3828
  "email": "emails[primary eq true].value",
3806
3829
  "firstName": "name.givenName",
@@ -4490,6 +4513,8 @@ is not misread as self-service.
4490
4513
  from retries and dead-letter state.
4491
4514
  - [Protect and retain audit evidence](/security-and-audit/protect-evidence)
4492
4515
  without turning a diagnostic copy into an uncontrolled data store.
4516
+ - [Respond to a leaked credential](/security-and-audit/respond-to-leaked-credential)
4517
+ in the order an incident requires: contain, then assess, then recover.
4493
4518
 
4494
4519
  Close each task with the question, organization/environment, filters and time
4495
4520
  boundary, event and correlation identifiers, current-state comparison, reviewer
@@ -4499,7 +4524,10 @@ unrestricted evidence export in an ordinary support ticket.`,
4499
4524
  {
4500
4525
  managedPath: 'security-and-audit/investigate-event.md',
4501
4526
  unitRef: 'technical-documentation:unit/investigate-audit-event',
4502
- sourceRefs: ['saas-technical-doc:engine-content/investigate-audit-event'],
4527
+ sourceRefs: [
4528
+ 'saas-technical-doc:engine-content/investigate-audit-event',
4529
+ 'source:consumer-fact:organization-audit-trail',
4530
+ ],
4503
4531
  markdown: `# Investigate an audit event
4504
4532
 
4505
4533
  Begin with a question, not with the entire event stream: “Why was this request
@@ -4507,6 +4535,17 @@ refused?”, “Who changed this person's role?” or “What happened after thi
4507
4535
  credential was used?” Choose the smallest time range and organization that can
4508
4536
  answer it.
4509
4537
 
4538
+ ## What you can narrow the trail by
4539
+
4540
+ {{CONSUMER_FACT:source:consumer-fact:organization-audit-trail#queryMarkdown}}
4541
+
4542
+ Every event carries a category and a severity. Both are closed lists, so they are
4543
+ the two filters that reliably cut a broad question down:
4544
+
4545
+ {{CONSUMER_FACT:source:consumer-fact:organization-audit-trail#categoriesMarkdown}}
4546
+
4547
+ {{CONSUMER_FACT:source:consumer-fact:organization-audit-trail#severitiesMarkdown}}
4548
+
4510
4549
  | Before you begin | Successful result |
4511
4550
  | --- | --- |
4512
4551
  | 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 +5376,10 @@ delivery can coexist with a broken mapping or detection.`,
5337
5376
  {
5338
5377
  managedPath: 'security-and-audit/operate-siem.md',
5339
5378
  unitRef: 'technical-documentation:unit/operate-and-troubleshoot-siem-delivery',
5340
- sourceRefs: ['saas-technical-doc:engine-content/operate-and-troubleshoot-siem-delivery'],
5379
+ sourceRefs: [
5380
+ 'saas-technical-doc:engine-content/operate-and-troubleshoot-siem-delivery',
5381
+ 'source:consumer-fact:organization-siem-export',
5382
+ ],
5341
5383
  markdown: `# Operate and troubleshoot SIEM delivery
5342
5384
 
5343
5385
  The organization audit record and the SIEM copy have different health signals.
@@ -5363,6 +5405,10 @@ flowchart TD
5363
5405
  captured -->|Authorized purge| purge["Remove recovery row without retry"]
5364
5406
  ~~~
5365
5407
 
5408
+ ## When a destination keeps failing
5409
+
5410
+ {{CONSUMER_FACT:source:consumer-fact:organization-siem-export#deliveryReliabilityMarkdown}}
5411
+
5366
5412
  ## Start from the failure boundary
5367
5413
 
5368
5414
  | Symptom | First checks |
@@ -5425,6 +5471,89 @@ Close the incident with source event ID, organization/environment, destination,
5425
5471
  filter decision, failure class, attempts, configuration correction, retry/purge
5426
5472
  decision, receiver lookup and correlation evidence. Exclude authentication
5427
5473
  secrets, raw Authorization headers and unnecessary event personal data.`,
5474
+ },
5475
+ {
5476
+ managedPath: 'security-and-audit/respond-to-leaked-credential.md',
5477
+ unitRef: 'technical-documentation:unit/respond-to-leaked-credential',
5478
+ sourceRefs: ['saas-technical-doc:engine-content/respond-to-leaked-credential'],
5479
+ markdown: `# Respond to a leaked credential
5480
+
5481
+ A credential of this application has been exposed — an API key in a public repository, a token in a
5482
+ support ticket or a log, an OAuth client secret in a screenshot, or a person's password reused on a
5483
+ service that was breached.
5484
+
5485
+ Every other page in this section explains ONE capability well. This one exists because an incident
5486
+ is not one capability: it is an ORDER. The costly mistakes here are not doing the wrong thing, they
5487
+ are doing the right things in the wrong sequence — investigating before containing, or revoking
5488
+ before you know what the credential reached.
5489
+
5490
+ **Contain first. Assess second. Recover third.** Work down the page.
5491
+
5492
+ ## Before you start
5493
+
5494
+ Write down the exposure time you will use as the window's lower bound — when the credential first
5495
+ became readable by someone who should not have it, NOT when you found out. If you cannot establish
5496
+ it, use the credential's creation time. Every step below is bounded by that instant, and widening it
5497
+ later means redoing the assessment.
5498
+
5499
+ ## 1 · Contain
5500
+
5501
+ Do this before anything else, including before you finish reading the rest of this page. A credential
5502
+ you are still investigating is a credential still being used.
5503
+
5504
+ | What leaked | Do this |
5505
+ | --- | --- |
5506
+ | An API key | Revoke it — see [API key lifecycle](/access-and-identity/api-keys-lifecycle). Revocation takes effect for new requests; it does not retract a request already in flight |
5507
+ | An OAuth client secret, or a token issued to a client | [Operate and revoke delegated access](/access-and-identity/oauth-operate-and-revoke), and revoke the client's outstanding grants, not only the secret |
5508
+ | A person's password | Suspend the account — see [Suspend or remove a user](/user-administration/suspend-or-remove-user). Suspending ends their sessions; a password reset alone does not tell you whether someone else is already signed in |
5509
+ | A SCIM or directory token | Rotate at the identity provider, then in [SCIM configuration](/user-administration/scim-configure). Until both sides carry the new value, provisioning is stopped — that is the correct trade during containment |
5510
+
5511
+ **Do not delete anything yet.** Deleting the API key, the client or the user removes rows the next
5512
+ step reads. Revoke and suspend, which stop use while keeping the record.
5513
+
5514
+ If you do not yet know which credential leaked, suspend the narrowest thing that certainly covers it
5515
+ and widen only if the assessment says so. An over-broad containment is recoverable in minutes; a
5516
+ missed one runs for as long as you are investigating.
5517
+
5518
+ ## 2 · Assess
5519
+
5520
+ Only now, and only inside the window you wrote down.
5521
+
5522
+ 1. **What did it reach?** [Investigate an audit event](/security-and-audit/investigate-event),
5523
+ filtered to the credential's own identifier rather than to a person — a leaked key acts under its
5524
+ own identity, and filtering by the human who created it will not show you its requests.
5525
+ 2. **Did it change access?** [Review access and privileged changes](/security-and-audit/review-access-changes).
5526
+ This is the question that decides whether containment is finished: a credential that granted a
5527
+ role, added a member or registered a client has left something behind that revoking it does not
5528
+ remove.
5529
+ 3. **Take the evidence out.** [Export a bounded audit window](/security-and-audit/export-audit-records)
5530
+ for the window, before retention or an investigation of your own moves it. Handle the export as
5531
+ the sensitive artefact it is — [Protect and retain audit evidence](/security-and-audit/protect-evidence).
5532
+
5533
+ If step 2 found a change, treat every artefact it created as also compromised and return to
5534
+ **Contain** for each one. That loop is the point of the ordering: a leaked key that minted a second
5535
+ key is only half-contained when the first is revoked.
5536
+
5537
+ ## 3 · Recover
5538
+
5539
+ - Issue the replacement credential and store it where the leaked one was not —
5540
+ [Create and store an API key](/access-and-identity/api-keys-create-and-store) states where a
5541
+ secret may and may not live.
5542
+ - Give the replacement the SMALLEST role that works. An incident is the cheapest moment to correct
5543
+ an over-broad credential, because whatever breaks is being watched.
5544
+ - Lift the containment you widened, narrowest first, confirming after each one.
5545
+ - If the account is a person's, require a second factor before returning it to service —
5546
+ [Multifactor enrollment and recovery](/access-and-identity/mfa-enrollment-and-recovery).
5547
+
5548
+ ## What this application cannot tell you
5549
+
5550
+ The audit trail records what reached THIS application. A credential leaked in one place is usually
5551
+ leaked for others: if the same secret, or a password reused from it, opens anything else you run,
5552
+ this page's window is a lower bound on the incident, not its boundary.
5553
+
5554
+ Nor can the trail prove absence. A window with no suspicious request means nothing suspicious
5555
+ **reached this application** during it — not that the credential was unused, and not that your lower
5556
+ bound was early enough.`,
5428
5557
  },
5429
5558
  {
5430
5559
  managedPath: 'security-and-audit/protect-evidence.md',
@@ -5525,1102 +5654,203 @@ change the application's authoritative audit retention.
5525
5654
  - keep credentials and unrelated personal data out of the evidence package.`,
5526
5655
  },
5527
5656
  {
5528
- managedPath: 'billing-and-subscriptions/billing.md',
5529
- unitRef: 'technical-documentation:unit/billing',
5530
- sourceRefs: [
5531
- 'saas-technical-doc:engine-content/billing',
5532
- 'source:consumer-fact:billing-lifecycle',
5533
- ],
5534
- markdown: `# Billing and subscriptions
5535
-
5536
- Use this section when you are responsible for an organization's plan, invoices,
5537
- usage or access after a billing change. You do not need to know which billing
5538
- provider the application uses. You do need to distinguish three records:
5657
+ managedPath: 'integrations.md',
5658
+ unitRef: 'technical-documentation:unit/integrations',
5659
+ sourceRefs: ['saas-technical-doc:engine-content/integrations'],
5660
+ markdown: `# Integrations
5539
5661
 
5540
- - the **product and price** describe what can be bought and on which terms;
5541
- - the **subscription** records the current recurring relationship;
5542
- - the application's **billing-derived access** records which features and limits
5543
- the current subscription actually grants.
5662
+ An integration connects this application to another system so that people do
5663
+ not have to copy information or repeat the same action by hand. Start with the
5664
+ outcome you need, then choose the smallest contract that can deliver it safely.
5544
5665
 
5545
5666
  | Before you begin | Successful result |
5546
5667
  | --- | --- |
5547
- | Know the organization being billed, who approved the commercial decision, the current product and the application access expected afterwards | The intended billing record reaches a known state, the resulting access is verified in the same organization, and payment evidence remains protected |
5548
-
5549
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#markdown}}
5668
+ | Name one business outcome, the source of truth, the organization affected, the acting person or workload and who owns failures | The chosen connection proves one bounded allowed outcome, one expected refusal and a recovery path that does not expose credentials or repeat a business effect |
5550
5669
 
5551
- ## Keep the four billing views separate
5670
+ ## Which integration should you choose?
5552
5671
 
5553
- | View | Question it answers | Do not use it as proof of |
5672
+ | You need to… | Use | Why |
5554
5673
  | --- | --- | --- |
5555
- | Product and price | What is offered, at what amount and interval, with which intended grants? | A purchase or active access |
5556
- | Checkout or billing portal | Where are payment details, tax information and provider documents handled? | The final application subscription state |
5557
- | Subscription and invoice summary | What recurring relationship and payment evidence did the application record? | Every feature currently available to the organization |
5558
- | Resolved application access | Which features and limits can the organization actually use now? | Why that access exists without checking billing and other allowed sources |
5559
-
5560
- For organization billing, the standard management operations accept
5561
- **Organization Owner** and **Organization Admin** authority. A custom role may
5562
- inherit effective authority in an application-specific role graph; review
5563
- [Roles and permissions](/user-administration/roles-and-permissions) and the
5564
- exact operation contract rather than granting a broad role merely to expose the
5565
- Billing area.
5674
+ | Read or change application data on demand | [REST API](/integrations/rest-api) | Your system sends a request and receives an immediate response |
5675
+ | Build a workflow in n8n, Zapier or Make | [Automation platforms](/integrations/automation-platforms) | The platform calls the REST API or receives a published event |
5676
+ | React when the application publishes an event | The Webhooks section, when available below | The application pushes a signed delivery to your receiver |
5677
+ | Connect Cursor, Codex or Claude Code | The AI coding tools section, when available below | The client discovers MCP tools available to its authenticated identity |
5678
+ | Give another AI client callable tools or exchange agent tasks | The Agent integrations section below | MCP covers tools; A2A covers messages and task lifecycles |
5679
+
5680
+ Do not choose a protocol because its name is familiar. Choose it because its
5681
+ interaction model matches the business outcome. A scheduled REST poll is not a
5682
+ replacement for an event when timeliness matters; a webhook is not a command
5683
+ channel; an MCP tool call is not an A2A conversation.
5566
5684
 
5567
5685
  ~~~mermaid
5568
- flowchart LR
5569
- accTitle: From a billing decision to usable application access
5570
- accDescr: An authorized administrator selects a product and price, completes hosted checkout, waits for verified billing state, then confirms the subscription and billing-derived access in the application. Invoice and usage records remain separate evidence.
5571
- choice["Choose product, price and billed scope"] --> checkout["Complete hosted checkout"]
5572
- checkout --> verify["Application verifies billing result"]
5573
- verify --> subscription["Subscription state is updated"]
5574
- subscription --> access["Features and limits are recomputed"]
5575
- verify --> invoice["Invoice and payment state is recorded"]
5576
- usage["Metered application usage"] --> invoice
5686
+ flowchart TD
5687
+ accTitle: Choose an integration contract from the intended outcome
5688
+ accDescr: Immediate request and response work uses REST. Event notification uses webhooks. Tool discovery and invocation uses MCP. Agent conversations and tasks use A2A.
5689
+ outcome["What must the other system accomplish?"] --> immediate{"Immediate request and response?"}
5690
+ immediate -->|Yes| rest["REST API"]
5691
+ immediate -->|No| event{"React to an application event?"}
5692
+ event -->|Yes| webhook["Webhook"]
5693
+ event -->|No| agent{"AI or agent interaction?"}
5694
+ agent -->|Call advertised tools| mcp["MCP"]
5695
+ agent -->|Exchange messages and tasks| a2a["A2A"]
5577
5696
  ~~~
5578
5697
 
5579
- The important boundary is after checkout: **returning to the application is not
5580
- proof that access changed**. Read the current subscription and verify the feature
5581
- or limit you expected before telling people the change is complete.
5582
-
5583
- The diagram has two evidence branches for a reason. Invoice/payment state
5584
- explains the commercial event, while subscription state drives the billing
5585
- access recomputation. A paid invoice and an unavailable feature can therefore
5586
- both be true during a mismatch that still needs reconciliation.
5587
-
5588
- ## Choose the task you need
5698
+ The diagram is a starting point, not a permission decision. The identity used
5699
+ by the integration still needs access to the intended organization, resources
5700
+ and operations.
5589
5701
 
5590
- | Your task | Start here | You are finished when |
5591
- | --- | --- | --- |
5592
- | Decide what to buy | [Understand plans, prices and access](/billing-and-subscriptions/plans-and-access) | The billed scope, price terms and expected access are explicit before approval |
5593
- | Create the recurring relationship | [Start a subscription](/billing-and-subscriptions/start-subscription) | The application records Active or Trialing state and the intended access works |
5594
- | Upgrade, downgrade, add capacity or end access | [Change, resume or end a subscription](/billing-and-subscriptions/change-or-end-subscription) | Effective timing, charge/credit and access consequences match the approved decision |
5595
- | Review a charge, payment state, document or measured quantity | [Billing records and usage](/billing-and-subscriptions/billing-records) | The question is routed to its owning record and matched to the correct scope and period |
5596
- | Resolve a mismatch | [Reconcile billing and application access](/billing-and-subscriptions/reconcile-access) | Provider evidence, application records and the original application action agree |
5597
- | Recover from an unclear result | [Troubleshoot a billing change](/billing-and-subscriptions/troubleshoot) | The failing layer is identified without a duplicate purchase or hidden access grant |
5598
-
5599
- ## Close every billing task with evidence
5600
-
5601
- Retain the billed organization, product/price identity, subscription or invoice
5602
- identifier, observed state, effective period, approval reference and correlation
5603
- information needed for support. Do not retain card details, payment-method data,
5604
- provider secrets, complete invoice documents or capability-style invoice URLs in
5605
- ordinary logs and tickets.`,
5606
- },
5607
- {
5608
- managedPath: 'billing-and-subscriptions/plans-and-access.md',
5609
- unitRef: 'technical-documentation:unit/understand-billing-plans-and-access',
5610
- sourceRefs: [
5611
- 'saas-technical-doc:engine-content/understand-billing-plans-and-access',
5612
- 'source:consumer-fact:billing-lifecycle',
5613
- ],
5614
- markdown: `# Understand plans, prices and access
5702
+ ## Keep the source of truth explicit
5615
5703
 
5616
- Before approving a purchase, identify **what is being bought, who will be
5617
- billed, when the charge repeats and which application access should change**.
5618
- The current Billing area is authoritative for the products and prices offered by
5619
- this application. The supported product-kind inventory is not proof that every
5620
- kind is offered here.
5704
+ Decide which system owns the final fact before writing synchronization logic.
5705
+ REST can retrieve or change current application state; a webhook announces that
5706
+ an event occurred; MCP invokes an advertised operation; A2A carries a message or
5707
+ task. None of those contracts automatically makes the receiving copy
5708
+ authoritative.
5621
5709
 
5622
- | Before you begin | Successful result |
5623
- | --- | --- |
5624
- | Have the intended organization, business owner, expected users or workload, required features/capacity and approved spending boundary | One current product and price satisfy the outcome without unnecessary capacity, and the expected access can be verified after purchase |
5710
+ After a timeout, duplicate or conflicting change, reconcile against the system
5711
+ that owns the fact. If ownership is shared or unclear, define a human decision
5712
+ path rather than letting the last retry silently win.
5625
5713
 
5626
- ## Start from the work, not the plan name
5714
+ ## Before you connect anything
5627
5715
 
5628
- Write down the outcome before comparing offers—for example, “the Support
5629
- organization needs ten additional automated runs this month.” Then ask:
5716
+ - Name the business outcome and the system that owns the authoritative state.
5717
+ - Use a separate identity and credential for each deployed workload.
5718
+ - Begin in a non-production organization with the least privilege required.
5719
+ - Decide how you will reconcile timeouts, duplicates and partially completed work.
5720
+ - Record correlation information without copying credentials or restricted data.
5630
5721
 
5631
- - Is the requirement a continuing base capability, optional recurring capacity,
5632
- measured use, a one-time item or prepaid credits?
5633
- - Does the purchase belong to this organization, and will everybody who needs
5634
- it operate in that same scope?
5635
- - Which exact feature or limit should change, and what current application
5636
- action will prove it?
5637
- - When should the commercial and access change take effect?
5638
- - Who owns renewal, usage review and cancellation?
5722
+ ## Prove the boundary before production
5639
5723
 
5640
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#productTypesMarkdown}}
5724
+ Start with a harmless action whose correct result a person can recognize. Prove
5725
+ the intended environment, identity, organization and response shape. Then prove
5726
+ one expected refusal: another organization, an unavailable operation, an
5727
+ invalid signature or insufficient authority must remain blocked.
5641
5728
 
5642
- The table lists product kinds the application can support. Only products and
5643
- prices shown in this application's current Billing area are purchasable offers.
5644
- Do not ask support to create a missing kind from this list.
5729
+ Only after those checks should you add a mutation, automatic retry, schedule or
5730
+ production volume. For a state-changing path, deliberately simulate a lost
5731
+ response and show how authoritative state is read before another attempt.
5645
5732
 
5646
- ## Understand how quantity changes the amount
5733
+ Retain the integration owner, deployed workload, non-secret credential
5734
+ identifier, environment, organization, operation or event identity, outcome and
5735
+ correlation evidence. Define how the credential is rotated or revoked and who
5736
+ receives an alert when the business result cannot be reconciled.
5647
5737
 
5648
- The product kind explains **what you are buying**. The pricing model explains
5649
- **how the selected quantity becomes a charge**. Do not use the words “tiered”
5650
- and “graduated” interchangeably: they produce different totals.
5738
+ If you are building a new connection, begin with [Create your own integration](/integrations/create-your-own-integration). The API reference supplies exact paths and schemas after you have chosen the correct journey.`,
5739
+ },
5740
+ {
5741
+ managedPath: 'integrations/automation-platforms.md',
5742
+ unitRef: 'technical-documentation:unit/automation-platforms',
5743
+ sourceRefs: ['saas-technical-doc:engine-content/automation-platforms'],
5744
+ markdown: `# Automation platforms
5651
5745
 
5652
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#pricingModelsMarkdown}}
5746
+ Use an automation platform when a business process must move information or
5747
+ start work across systems without requiring a custom deployed service. This
5748
+ section covers **n8n**, **Zapier** and **Make**. The application does not need a
5749
+ platform-specific connector: each platform can call the published REST API,
5750
+ and it can receive application webhooks when a Webhooks section is available.
5653
5751
 
5654
- ### Worked example: 240 units
5752
+ ## Start from the business event
5655
5753
 
5656
- The following numbers are illustrative, not an offer from this application.
5657
- They show why the pricing model must be recorded with the amount:
5754
+ Describe the automation in one sentence before opening the workflow editor:
5755
+ “When an approved record changes, create or update its counterpart in the
5756
+ reporting system.” That sentence identifies the trigger, the intended effect
5757
+ and the system that owns the final state.
5658
5758
 
5659
- | Model | Illustrative terms | Result for 240 units |
5759
+ | The work starts when… | Begin with | Why |
5660
5760
  | --- | --- | --- |
5661
- | Flat | €100 for the line item | €100 |
5662
- | Per unit | €0.50 per unit | 240 × €0.50 = €120 |
5663
- | Volume tiered | Up to 100 at €0.50; above 100 at €0.40 for all units | 240 × €0.40 = €96 |
5664
- | Graduated | First 100 at €0.50; remaining units at €0.40 | (100 × €0.50) + (140 × €0.40) = €106 |
5665
- | Package | €10 per block of 25, rounded up | 10 packages × €10 = €100 |
5666
-
5667
- For a tiered or package offer, keep the tier boundaries, flat additions and
5668
- package size with the approval. A headline unit price is not enough to
5669
- reconstruct the expected invoice.
5670
-
5671
- ## Read a price as a complete offer
5672
-
5673
- Check the product name and description together with:
5674
-
5675
- - the billed scope—usually an organization, or a person only when the
5676
- application explicitly offers personal billing;
5677
- - currency and amount;
5678
- - monthly or yearly interval for recurring prices;
5679
- - whether tax is included in the displayed amount or added at checkout;
5680
- - trial duration and whether a payment method is required;
5681
- - included features, capacity limits and any metered usage;
5682
- - promotion eligibility and the date on which the offer expires.
5683
-
5684
- For a recurring price, read both the interval and its count. “Month” with an
5685
- interval count of 3 means every three months, not monthly:
5686
-
5687
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#billingIntervalsMarkdown}}
5688
-
5689
- Currency codes should be interpreted using the maintained
5690
- [ISO 4217 currency list](https://www.iso.org/iso-4217-currency-codes.html).
5691
- The displayed currency does not determine whether tax is included:
5692
-
5693
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#taxDisplayModesMarkdown}}
5694
-
5695
- If the hosted billing screen is Stripe, its maintained
5696
- [tax-behaviour guide](https://docs.stripe.com/tax/products-prices-tax-codes-tax-behavior)
5697
- explains the provider-side inclusive/exclusive distinction. The application
5698
- offer and checkout total remain the evidence for this purchase.
5699
-
5700
- An **add-on** changes the same subscription without replacing its primary plan.
5701
- A **credit pack** adds a consumable balance. Neither should be described as a
5702
- plan upgrade unless that is what the product screen actually presents.
5703
-
5704
- ## Trace an offer to application access
5761
+ | A schedule, form or another system produces input | An authenticated REST request | The workflow controls when the application is read or changed |
5762
+ | This application publishes a relevant event | A webhook receiver | The workflow starts from the event instead of repeatedly polling |
5763
+ | A person presses a workflow button | A safe read, followed by an explicit write | The person can confirm scope before a change is made |
5705
5764
 
5706
5765
  ~~~mermaid
5707
- flowchart TD
5708
- accTitle: Evaluate a billing offer from commercial terms to application access
5709
- accDescr: The administrator starts with a required business outcome, chooses an offered product and price for one billed scope, reviews trial, timing and usage terms, identifies the promised feature or limit, then records how that access will be verified after purchase.
5710
- outcome["Name the required business outcome"] --> scope["Confirm billed organization or person"]
5711
- scope --> offer["Choose one currently offered product and price"]
5712
- offer --> terms["Review interval, tax, trial, quantity and promotion"]
5713
- terms --> grants["Identify expected features, limits or credits"]
5714
- grants --> proof["Define one application action that proves access"]
5715
- proof --> approval["Record approval and lifecycle owner"]
5766
+ flowchart LR
5767
+ accTitle: Build an automation around one authoritative business outcome
5768
+ accDescr: A schedule, human action, or application event starts the workflow. The workflow validates the input, reads current application state, applies one intended effect, and records enough evidence to reconcile failures.
5769
+ trigger["Schedule, person or application event"] --> validate["Validate input and organization"]
5770
+ validate --> read["Read authoritative state"]
5771
+ read --> decide{"Change still required?"}
5772
+ decide -->|No| finish["Record no change"]
5773
+ decide -->|Yes| write["Apply one documented operation"]
5774
+ write --> confirm["Confirm result and retain correlation"]
5716
5775
  ~~~
5717
5776
 
5718
- The important handoff is from **grants** to **proof**. A product description can
5719
- state what should be included; the original application action verifies what is
5720
- actually usable after the billing state is processed.
5721
-
5722
- ## Understand how access is assembled
5723
-
5724
- One active plan supplies the base features and limits. Active add-ons can add
5725
- features or capacity. The application recomputes that billing-derived access
5726
- from subscriptions in **Active** or **Trialing** state. Other subscription states
5727
- do not contribute billing-derived access.
5728
-
5729
- Manual or broader-scope access can also exist. A feature remaining available
5730
- after a downgrade therefore does not, by itself, prove that billing
5731
- reconciliation failed. Compare the expected product grants with the resolved
5732
- application access for the billed scope.
5733
-
5734
- Base-plan and add-on limits can combine according to the offered product policy:
5735
- an add-on may add capacity or establish a higher limit. “Unlimited” capacity
5736
- remains unlimited when combined. Do not calculate the final limit from marketing
5737
- labels alone; read the resolved limit in the billed scope.
5738
-
5739
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#limitGrantModesMarkdown}}
5740
-
5741
- For example, a plan can establish 50 scheduled runs, while an add-on adds 20.
5742
- The expected result is 70. If instead the add-on establishes a limit of 100,
5743
- the expected result is 100—not 150. Verify the resolved value after purchase,
5744
- because access can also come from a broader scope or an approved manual source.
5745
-
5746
- ## Treat a promotion as conditional until checkout accepts it
5777
+ ## Choose your platform
5747
5778
 
5748
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#promotionVocabularyMarkdown}}
5779
+ - [Connect n8n](/integrations/automation/n8n) when you want a visual workflow
5780
+ that can also be self-hosted and extended with technical nodes.
5781
+ - [Connect Zapier](/integrations/automation/zapier) when the business workflow
5782
+ already lives in Zaps and should use an API or webhook step.
5783
+ - [Connect Make](/integrations/automation/make) when you want to map a scenario
5784
+ visually and control HTTP request fields in a dedicated module.
5749
5785
 
5750
- A promotion shown in Billing can still be refused for the selected purchase.
5751
- Before relying on it, verify its validity dates, remaining redemptions, eligible
5752
- products, discount currency when fixed, and duration. The checkout result is the
5753
- positive proof; visibility in a list is only a reason to evaluate it.
5786
+ ## Keep the first workflow deliberately small
5754
5787
 
5755
- ## Approve with a reviewable record
5788
+ Use a dedicated credential for one environment and one workflow. Prove one
5789
+ read operation, then one write only if the business outcome requires it. Store
5790
+ the credential in the platform's credential or connection store—not in a URL,
5791
+ ordinary field, workflow name, execution note or shared screenshot.
5756
5792
 
5757
- Record the selected product and price, currency, interval, quantity, tax/trial
5758
- terms, expected effective date, billed scope, expected grants and the person who
5759
- approved them. Keep payment instruments and provider credentials out of that
5760
- record. Continue to [Start a subscription](/billing-and-subscriptions/start-subscription)
5761
- only after the expected application proof is clear.`,
5793
+ Before enabling unattended runs, test invalid input, expired or revoked access,
5794
+ insufficient permission, a timeout after a write and a duplicate trigger. The
5795
+ workflow is production-ready only when an operator can determine whether the
5796
+ business effect occurred without blindly repeating it.`,
5762
5797
  },
5763
5798
  {
5764
- managedPath: 'billing-and-subscriptions/start-subscription.md',
5765
- unitRef: 'technical-documentation:unit/start-billing-subscription',
5799
+ managedPath: 'integrations/automation/n8n.md',
5800
+ unitRef: 'technical-documentation:unit/connect-n8n',
5766
5801
  sourceRefs: [
5767
- 'saas-technical-doc:engine-content/start-billing-subscription',
5768
- 'source:consumer-fact:billing-lifecycle',
5802
+ 'saas-technical-doc:engine-content/connect-n8n',
5803
+ 'source:companion-projection:application-connection',
5804
+ 'source:consumer-fact:access-api-keys',
5769
5805
  ],
5770
- markdown: `# Start a subscription
5771
-
5772
- Start a subscription only when you can approve billing for the selected scope.
5773
- For organization billing, organization owners and administrators are the
5774
- standard authorized roles. Confirm your application exposes Billing and the
5775
- intended product before beginning.
5806
+ markdown: `# Connect n8n
5776
5807
 
5777
- | Before you begin | Successful result |
5778
- | --- | --- |
5779
- | Have the approved organization, current product and price, expected access, spending approval, protected payment owner and a way to reconcile an unclear result | One checkout creates the intended subscription, the application records its current state and the expected feature or limit works in the billed scope |
5808
+ Use n8n when a visual workflow should read or change information in this
5809
+ application. There is no application-specific n8n node to install: the workflow
5810
+ uses n8n's standard **HTTP Request** node and the REST API published in this
5811
+ documentation.
5780
5812
 
5781
- > **One checkout attempt can complete even when the browser never returns.**
5782
- > Keep its identifiers and reconcile the application state before opening a
5783
- > replacement checkout.
5813
+ This guide first builds a manual, read-only workflow. By the end, one click in
5814
+ n8n will call one authenticated operation and show recognizable application
5815
+ data in the node output. Only then will you connect a trigger or add a write.
5784
5816
 
5785
- ## Before checkout
5817
+ > **If the application should start the workflow when something happens,**
5818
+ > complete the read-only connection first, then follow **Start from an
5819
+ > application event** below. Do not poll repeatedly when a published webhook
5820
+ > already represents the event you need.
5786
5821
 
5787
- 1. Select the intended organization before opening Billing. Do not rely on the
5788
- organization that happened to be active in a previous browser session.
5789
- 2. Confirm the product, price, currency, interval, quantity, tax display, trial
5790
- terms and expected access against the approval.
5791
- 3. Check whether the displayed promotion applies to this exact product and
5792
- price. Visibility of a promotion does not guarantee that every item accepts it.
5793
- 4. Record the approver, expected effective date and application action that will
5794
- prove the resulting access.
5795
- 5. Continue once to the hosted checkout presented by the application.
5822
+ ## What do you need before starting?
5796
5823
 
5797
- Payment details, billing address and tax identifiers belong on that hosted
5798
- billing surface. Do not send them through application support, an API metadata
5799
- field or a screenshot.
5824
+ Ask the application administrator or integration owner for:
5800
5825
 
5801
- When you enter a promotion code, stop if checkout refuses it. Do not compensate
5802
- by changing quantity, selecting another organization or asking support to copy
5803
- the discount manually. Recheck the product, validity period, redemption limit
5804
- and discount currency against the approved offer.
5826
+ - the server origin shown in this application's API reference;
5827
+ - one authenticated **GET** operation and its complete path;
5828
+ - the non-production organization and known data you may use for the test;
5829
+ - a dedicated API key with only the role accepted by that operation; and
5830
+ - an owner who can revoke the key if the workflow is abandoned or exposed.
5805
5831
 
5806
- ~~~mermaid
5807
- sequenceDiagram
5808
- accTitle: Start and verify a subscription
5809
- accDescr: The administrator opens checkout from the application, completes provider-hosted payment, returns to the application, then verifies the application subscription and resulting access instead of trusting the redirect alone.
5810
- participant A as Administrator
5811
- participant App as Application
5812
- participant B as Hosted billing
5813
- A->>App: Select product and price for the intended scope
5814
- App-->>A: Open authorized checkout
5815
- A->>B: Confirm payment and billing details
5816
- B-->>A: Return to application
5817
- App->>App: Verify billing result and update subscription
5818
- A->>App: Read subscription state and expected access
5819
- ~~~
5832
+ Use [Create and store an API key](/access-and-identity/api-keys-create-and-store)
5833
+ if the credential has not been prepared. Keep n8n and the application in the
5834
+ same environment throughout the test.
5820
5835
 
5821
- The browser return is only navigation. The application updates its billing
5822
- records from the verified provider result, then recomputes access. Closing the
5823
- browser, losing the redirect or receiving a timeout does not establish whether
5824
- the provider completed the checkout.
5836
+ ## Build the first read-only workflow
5825
5837
 
5826
- If the hosted screen identifies itself as Stripe, see Stripe's maintained
5827
- [subscription Checkout journey](https://docs.stripe.com/billing/subscriptions/build-subscriptions)
5828
- for the provider-side steps. That reference explains the hosted screen; the
5829
- application subscription and resulting access remain the completion evidence.
5838
+ ### 1. Create an explicit test trigger
5830
5839
 
5831
- ## Confirm success
5840
+ Create a new workflow and keep it inactive. Add a **Manual Trigger** so that the
5841
+ application is contacted only when you choose **Execute workflow**. Name the
5842
+ workflow after the business result and environment—for example, “Read approved
5843
+ records — test”—not after the credential.
5832
5844
 
5833
- Do not use the success URL as the success criterion. In the application, verify:
5845
+ ### 2. Add the application request
5834
5846
 
5835
- - a subscription exists for the intended billing account;
5836
- - its product, price and period are correct;
5837
- - its state is **Active** or **Trialing**;
5838
- - the expected feature or limit is available in the intended organization;
5839
- - any first invoice has the expected total and state.
5840
-
5841
- Use the maintained subscription meanings when reading the result:
5842
-
5843
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#subscriptionStatusesMarkdown}}
5844
-
5845
- If the state remains incomplete, past due, unpaid or absent, do not create a
5846
- second checkout immediately. Use the troubleshooting and reconciliation pages
5847
- to determine whether the first attempt is still being processed.
5848
-
5849
- ### Example: the browser closes after payment
5850
-
5851
- Suppose an administrator approves the Business plan, completes the hosted
5852
- payment, and the browser closes before returning. Reopen Billing in the same
5853
- organization and read the current subscription. If the approved plan is Active
5854
- and the expected limit works, record that result and close the task. If the
5855
- subscription is absent or Incomplete, preserve the first checkout identifier and
5856
- reconcile it; **do not start another purchase merely to obtain a success page**.
5857
-
5858
- ## Verify access, not only billing
5859
-
5860
- Repeat the application action identified before checkout. Confirm it in the
5861
- same organization and with an ordinary intended user—not only with the billing
5862
- administrator. Also verify one limit or unavailable action that should remain
5863
- unchanged, so the proof does not hide an unexpectedly broad grant.
5864
-
5865
- ## Retain safe completion evidence
5866
-
5867
- Keep the organization, product/price identity, checkout or subscription
5868
- identifier, visible status, period dates, first-invoice summary, approver,
5869
- correlation data and access-verification result. Do not retain card details,
5870
- payment-method data, complete invoice documents, provider secrets or the full
5871
- checkout URL.`,
5872
- },
5873
- {
5874
- managedPath: 'billing-and-subscriptions/change-or-end-subscription.md',
5875
- unitRef: 'technical-documentation:unit/change-or-end-billing-subscription',
5876
- sourceRefs: [
5877
- 'saas-technical-doc:engine-content/change-or-end-billing-subscription',
5878
- 'source:consumer-fact:billing-lifecycle',
5879
- ],
5880
- markdown: `# Change, resume or end a subscription
5881
-
5882
- A plan change is both a commercial decision and an access decision. Before
5883
- confirming it, review the new price, effective date, prorated charge or credit,
5884
- and the features or limits that will be added or removed.
5885
-
5886
- | Before you begin | Successful result |
5887
- | --- | --- |
5888
- | Have the current subscription, approved target product/add-on, desired effective timing, expected charge or credit, and named access consequences | The application subscription reflects the approved change, access changes at the intended time, and no scheduled cancellation or stale item remains unexplained |
5889
-
5890
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#changeTimingMarkdown}}
5891
-
5892
- > **A requested policy is not proof of the applied charge or date.** Some billing
5893
- > providers cannot schedule every change. Review any preview the application
5894
- > presents, then verify the returned subscription, invoice or credit and the
5895
- > resulting access. If those disagree with the approval, stop and reconcile the
5896
- > change instead of applying another one.
5897
-
5898
- ## Choose the lifecycle action
5899
-
5900
- ~~~mermaid
5901
- flowchart TD
5902
- accTitle: Choose how a subscription should change
5903
- accDescr: An administrator decides whether the organization needs a different base plan, an add-on change, end-of-period cancellation, immediate cancellation or reversal of a still-pending cancellation, then verifies the returned subscription and access consequence.
5904
- need{"What business decision was approved?"}
5905
- need -->|Replace base offer| plan["Change plan with product policy timing"]
5906
- need -->|Add or remove capacity| addon["Change subscription add-on"]
5907
- need -->|Stop at renewal| period["Schedule cancellation at period end"]
5908
- need -->|Stop now| immediate["Cancel immediately if policy permits"]
5909
- need -->|Undo pending cancellation| resume["Resume before the subscription ends"]
5910
- plan --> verify["Re-read subscription, charge evidence and access"]
5911
- addon --> verify
5912
- period --> verify
5913
- immediate --> verify
5914
- resume --> verify
5915
- ~~~
5916
-
5917
- Do not choose from the wording “upgrade” or “downgrade” alone. Determine which
5918
- base product and add-ons will remain, when the provider applies the commercial
5919
- change, and when users should gain or lose the corresponding application access.
5920
-
5921
- ## Change the plan or its add-ons
5922
-
5923
- 1. Read the current subscription, including its items, state and period end.
5924
- 2. Choose the new plan or add-on from the current application catalogue.
5925
- 3. Confirm that the selected price belongs to that product and applies to the
5926
- billed scope.
5927
- 4. Review the presented timing and proration before approving the change.
5928
- 5. After confirmation, re-read the subscription and verify the expected access.
5929
-
5930
- For an add-on change, verify the base plan remains present and the intended
5931
- add-on appears or disappears exactly once. For a plan replacement, verify that
5932
- the returned base item is the approved product and price. Do not infer the
5933
- result from a checkout or portal confirmation message.
5934
-
5935
- Do not assume every billing connector can defer a downgrade. The **returned
5936
- application subscription** is the source of truth for what was applied.
5937
-
5938
- If the hosted billing provider is Stripe, use its maintained
5939
- [proration explanation](https://docs.stripe.com/billing/subscriptions/prorations)
5940
- to interpret provider invoice lines. In particular, a credit line is not by
5941
- itself proof that money was refunded, and a positive proration is not by itself
5942
- proof that it was collected immediately. Match the application change, invoice
5943
- state and payment evidence.
5944
-
5945
- ### Worked example: increase capacity mid-period
5946
-
5947
- An organization has a 50-run plan and needs 20 additional runs immediately.
5948
- Before confirming an add-on, record:
5949
-
5950
- - the current 50-run limit and current billing-period end;
5951
- - the add-on price and whether the presented adjustment is immediate;
5952
- - the expected resolved limit of 70;
5953
- - the application operation that will prove the extra capacity.
5954
-
5955
- After confirmation, verify that the base plan is still present, the add-on
5956
- appears once, any provider adjustment matches the presented currency and period,
5957
- and the resolved limit is 70. If the request times out, re-read those records
5958
- before trying to add the same item again.
5959
-
5960
- ## End at period end or cancel immediately
5961
-
5962
- - **End at period end** keeps the subscription active through its current
5963
- period and marks it for cancellation. Access normally continues while the
5964
- subscription remains Active or Trialing.
5965
- - **Cancel immediately** marks the subscription Cancelled now and removes its
5966
- contribution to billing-derived access when access is recomputed.
5967
-
5968
- Immediate cancellation is not always allowed by the product policy. Use it only
5969
- when the business decision includes the immediate access consequence.
5970
-
5971
- Before immediate cancellation, identify workflows, exports or administrative
5972
- actions that depend on billing-derived access. Do not use a temporary manual
5973
- role or feature override to disguise the resulting loss; if continuity is
5974
- required, approve the alternative access source explicitly.
5975
-
5976
- ## Resume a scheduled cancellation
5977
-
5978
- A subscription can be resumed only while it is pending end-of-period
5979
- cancellation. Resume clears that pending cancellation and returns the local
5980
- subscription to Active. Verify both the next period date and the resulting
5981
- access; a subscription that already ended needs a new approved purchase rather
5982
- than resume.
5983
-
5984
- ## Verify by action and by time
5985
-
5986
- 1. Confirm the returned subscription items, state, period end and cancellation
5987
- flag in the billed scope.
5988
- 2. Match any immediate invoice or credit to the selected currency, quantity,
5989
- proration and period.
5990
- 3. Repeat the application action affected by the change.
5991
- 4. For a deferred change, record what stays active now and schedule a check at
5992
- the effective boundary.
5993
- 5. Verify an expected unaffected capability so the change did not alter a
5994
- broader scope.
5995
-
5996
- Retain the approval, old and new product/price identities, selected timing,
5997
- visible proration evidence, subscription state and access proof. If the request
5998
- times out, read the subscription before repeating it; an absent response is not
5999
- evidence that the change failed.`,
6000
- },
6001
- {
6002
- managedPath: 'billing-and-subscriptions/billing-records.md',
6003
- unitRef: 'technical-documentation:unit/billing-records-and-usage',
6004
- sourceRefs: ['saas-technical-doc:engine-content/billing-records-and-usage'],
6005
- markdown: `# Billing records and usage
6006
-
6007
- Use this journey when you need to explain **what was bought, what was charged,
6008
- what was measured, or why application access differs from the commercial
6009
- record**. Those questions use related records, but they do not have the same
6010
- authority.
6011
-
6012
- | Before you begin | Successful result |
6013
- | --- | --- |
6014
- | Know the environment, organization, product, billing period and the exact question being investigated | The question is answered from the record that owns it, then checked against the adjacent records without exposing payment data or duplicating a charge |
6015
-
6016
- ## Choose the record that owns the answer
6017
-
6018
- | Question | Start with | Then compare |
6019
- | --- | --- | --- |
6020
- | Which recurring product is current? | Subscription items, status and period | Product/price offer and resolved access |
6021
- | Was a charge created or paid? | Application invoice summary | Complete document in the authenticated provider portal |
6022
- | Why is the invoice quantity different? | Local usage rows for the same period | Reported state and provider meter result |
6023
- | Why is a feature unavailable after payment? | Current subscription and resolved access | Invoice/payment evidence only if the subscription state requires it |
6024
-
6025
- ~~~mermaid
6026
- flowchart LR
6027
- accTitle: Use each billing record for the question it owns
6028
- accDescr: The product and price describe the offer. The subscription records the recurring relationship and drives billing-derived access. Metered activity becomes local usage records and is reported to the provider. The provider creates the invoice; the application stores a safe summary while the complete document and payment methods remain in the authenticated provider portal.
6029
- offer["Product and price"] --> subscription["Subscription"]
6030
- subscription --> access["Billing-derived access"]
6031
- activity["Metered activity"] --> usage["Local usage records"]
6032
- usage --> provider["Provider meter"]
6033
- subscription --> invoice["Provider invoice"]
6034
- provider --> invoice
6035
- invoice --> summary["Application invoice summary"]
6036
- invoice --> portal["Authenticated provider portal"]
6037
- ~~~
6038
-
6039
- The useful boundary is between **operational summary** and **protected source
6040
- document**. The application exposes enough invoice state and total information
6041
- for reconciliation, but complete tax documents and payment methods stay behind
6042
- the provider's authenticated portal. Usage remains separately traceable so a
6043
- quantity can be explained before it becomes an invoice line.
6044
-
6045
- ## Choose your task
6046
-
6047
- - [Manage invoices and payment details](/billing-and-subscriptions/invoices-and-payments)
6048
- when the question concerns payment state, currency, tax or the complete invoice.
6049
- - [Understand metered usage](/billing-and-subscriptions/metered-usage) when the
6050
- question concerns measured quantity, reporting delay or a provider meter.
6051
- - [Reconcile billing and application access](/billing-and-subscriptions/reconcile-access)
6052
- when the commercial record is correct but the application outcome is not.
6053
- - [Troubleshoot a billing change](/billing-and-subscriptions/troubleshoot) when
6054
- the first durable record or failing boundary is still unclear.
6055
-
6056
- Retain identifiers, totals, states, periods and correlation evidence. Do not
6057
- copy card data, payment-method details, complete invoice documents, temporary
6058
- portal URLs or personal data from usage metadata into ordinary tickets.`,
6059
- },
6060
- {
6061
- managedPath: 'billing-and-subscriptions/invoices-and-payments.md',
6062
- unitRef: 'technical-documentation:unit/manage-invoices-and-payments',
6063
- sourceRefs: [
6064
- 'saas-technical-doc:engine-content/manage-invoices-and-payments',
6065
- 'source:consumer-fact:billing-lifecycle',
6066
- ],
6067
- markdown: `# Manage invoices and payment details
6068
-
6069
- An invoice is evidence of a charge, not the subscription itself. The application
6070
- stores a small invoice summary—state, total and timestamps—while the billing
6071
- provider keeps the complete tax document and payment methods behind its
6072
- authenticated portal.
6073
-
6074
- | Before you begin | Successful result |
6075
- | --- | --- |
6076
- | Know the billed organization, product, billing period and question being investigated; use an authorized billing administrator for provider documents | The application summary and provider document refer to the same charge, payment state and period without exposing payment data |
6077
-
6078
- ## Know which record answers which question
6079
-
6080
- ~~~mermaid
6081
- flowchart LR
6082
- accTitle: Separate the application invoice summary from the protected provider document
6083
- accDescr: A subscription defines the recurring product and billing period. The provider creates an invoice and owns payment methods and the complete tax document. The application retains a safe status and total summary for operational review, while an authorized administrator opens the authenticated portal for the full document or payment change.
6084
- subscription["Subscription and billing period"] --> invoice["Provider invoice"]
6085
- invoice --> summary["Application invoice summary"]
6086
- invoice --> portal["Authenticated billing portal"]
6087
- portal --> document["Complete invoice or receipt"]
6088
- portal --> payment["Payment methods and billing details"]
6089
- ~~~
6090
-
6091
- The application summary is designed for status checks and reconciliation. It is
6092
- not a replacement for the provider's full tax document.
6093
-
6094
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#invoiceStatusesMarkdown}}
6095
-
6096
- Open complete invoice documents through the authenticated billing portal. Direct
6097
- provider document URLs are deliberately not exposed in the application API:
6098
- they can act like bearer links to documents containing names and billing
6099
- addresses.
6100
-
6101
- Use the provider portal only from the application's Billing area and return to
6102
- the intended environment afterwards. Do not copy a complete invoice into an
6103
- ordinary ticket when its identifier, total, currency, period and state are
6104
- sufficient to investigate.
6105
-
6106
- If the portal identifies itself as Stripe, use Stripe's maintained
6107
- [customer-portal guide](https://docs.stripe.com/customer-management/integrate-customer-portal)
6108
- to understand the provider handoff and its
6109
- [Hosted Invoice Page guide](https://docs.stripe.com/invoicing/hosted-invoice-page)
6110
- to understand the complete document surface. Always open a fresh portal session
6111
- from the application; do not preserve or redistribute its temporary URL.
6112
-
6113
- ## Read payment and subscription state together
6114
-
6115
- - An **Open** or **Uncollectible** invoice explains a payment problem, but the
6116
- current subscription state determines whether billing-derived access remains.
6117
- - A **Paid** invoice proves settlement of that invoice; still verify that the
6118
- corresponding subscription and access update reached the intended scope.
6119
- - A **Void** invoice should not be treated as a successful payment or as a new
6120
- entitlement.
6121
-
6122
- An invoice can change after it is first created—for example from Draft to Open
6123
- or Paid. Record the state and observation time when using it as evidence. The
6124
- latest application summary is appropriate for operational review; the provider
6125
- portal owns the complete document.
6126
-
6127
- ## Interpret currency, tax and payment state separately
6128
-
6129
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#taxDisplayModesMarkdown}}
6130
-
6131
- Inclusive or exclusive describes the **displayed price**. It does not prove the
6132
- tax rate, jurisdiction, payment success or final amount. Compare the invoice
6133
- currency, subtotal, tax, total and period with the approved offer and the
6134
- provider document. For an unfamiliar currency code, use the
6135
- [ISO 4217 currency list](https://www.iso.org/iso-4217-currency-codes.html).
6136
-
6137
- When the invoice is Open or Uncollectible, an authorized administrator should
6138
- open Billing, enter the authenticated provider portal and review the available
6139
- payment action. Do not ask the person to send card numbers, bank details or a
6140
- screenshot of the payment instrument to application support.
6141
-
6142
- ### Example: a paid invoice but unchanged access
6143
-
6144
- A Paid invoice proves that **that invoice** was settled. It does not prove that
6145
- the application processed the corresponding subscription update or recomputed
6146
- access. Confirm the subscription is Active or Trialing, then repeat the
6147
- application action that should now be available. If that action still fails,
6148
- continue with [Reconcile billing and application access](/billing-and-subscriptions/reconcile-access).
6149
-
6150
- ## Protect financial evidence
6151
-
6152
- Invoice summaries remain financial history even when the billed scope is later
6153
- retired. Limit access to administrators who need it, keep exports in protected
6154
- storage, and apply the organization's retention and legal-hold rules. Retain
6155
- the invoice identifier, currency, total, state, period and observation time in
6156
- ordinary support evidence—not the complete document or payment details.`,
6157
- },
6158
- {
6159
- managedPath: 'billing-and-subscriptions/metered-usage.md',
6160
- unitRef: 'technical-documentation:unit/understand-metered-usage',
6161
- sourceRefs: [
6162
- 'saas-technical-doc:engine-content/understand-metered-usage',
6163
- 'source:consumer-fact:billing-lifecycle',
6164
- ],
6165
- markdown: `# Understand metered usage
6166
-
6167
- A metered product charges for a measured quantity, such as processed documents,
6168
- automated runs or storage consumed. The application records the activity first;
6169
- the billing provider receives grouped usage later and uses its configured meter
6170
- when preparing the invoice.
6171
-
6172
- | Before you begin | Successful result |
6173
- | --- | --- |
6174
- | Know the organization, metered product, billing period, unit being counted and application action that produces it | Local records, reported state, provider quantity and invoice use the same product, scope, period and unit without duplicating usage |
6175
-
6176
- ## Follow one quantity through the system
6177
-
6178
- ~~~mermaid
6179
- sequenceDiagram
6180
- accTitle: Follow metered usage from application activity to an invoice
6181
- accDescr: A successful application action creates a local usage row. The Billing area can show the local total while the row is pending. A scheduled flush groups pending rows by billing account and product, reports the quantity to the provider, then marks the local rows as reported. Provider processing is asynchronous, so the invoice can lag behind the newest local activity.
6182
- participant U as User or workload
6183
- participant App as Application
6184
- participant Store as Local usage records
6185
- participant Job as Billing reporter
6186
- participant P as Billing provider
6187
- U->>App: Complete a metered action
6188
- App->>Store: Record product, quantity and occurrence time
6189
- Note over Store: reportedToProvider = false
6190
- Job->>Store: Read pending rows by account + product
6191
- Job->>P: Report grouped quantity
6192
- P-->>Job: Accept report
6193
- Job->>Store: Mark included rows as reported
6194
- P->>P: Process meter and prepare invoice asynchronously
6195
- ~~~
6196
-
6197
- The two delays in the diagram are different:
6198
-
6199
- - **Pending locally** means the application has recorded the activity but has
6200
- not yet marked it as reported to the provider.
6201
- - **Accepted by the provider** still does not mean an invoice or provider usage
6202
- summary has updated immediately. Provider meter processing is asynchronous.
6203
-
6204
- The application does not promise a universal flush interval. Record the last
6205
- local occurrence time and the reported state instead of saying “billing updates
6206
- every hour”.
6207
-
6208
- ## Understand what created the record
6209
-
6210
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#usageSourceTypesMarkdown}}
6211
-
6212
- The source helps an administrator locate the business action that produced the
6213
- quantity. It is not a reason to store a person's name, email or free-text notes
6214
- in usage metadata. These records are retained as financial history.
6215
-
6216
- ## Worked example: local usage is temporarily ahead
6217
-
6218
- Three application actions create quantities of 40, 25 and 10 for the same
6219
- product and billing period. The local total is **75**. The first two rows have
6220
- already been reported, while the last row remains pending, so the provider can
6221
- temporarily show **65**.
6222
-
6223
- Do not manually send the missing 10 or recreate the application action. First
6224
- wait for the normal reporting boundary and verify that the original row becomes
6225
- reported. Re-sending a quantity that the normal flush later sends can create a
6226
- duplicate charge.
6227
-
6228
- ## Reconcile a usage difference
6229
-
6230
- | Check | What to compare | What it rules out |
6231
- | --- | --- | --- |
6232
- | Scope | Environment, organization and billing account | Usage from another tenant or test environment |
6233
- | Product | Product key and the unit described by the offer | Comparing requests with storage, seats or another meter |
6234
- | Period | Local occurrence timestamps and provider invoice period | Correct usage assigned to a different billing cycle |
6235
- | Local total | Every row in the period, including pending rows | A dashboard total that omitted recent activity |
6236
- | Reporting state | **Reported to provider** and **reported at** values | Treating pending activity as already invoiced |
6237
- | Provider result | Meter quantity and invoice state after processing | A provider-side delay or rejected meter event |
6238
-
6239
- Close the investigation only when the same records explain both totals, or when
6240
- a named correction owner has accepted the difference. Preserve the product,
6241
- period, local total, reported total, pending row identifiers and correlation
6242
- evidence. Do not retain provider credentials or personal data.
6243
-
6244
- If the hosted provider is Stripe, its maintained
6245
- [usage-based billing overview](https://docs.stripe.com/billing/subscriptions/usage-based/how-it-works),
6246
- [meter-event recording guide](https://docs.stripe.com/billing/subscriptions/usage-based/recording-usage-api)
6247
- and [meter configuration guide](https://docs.stripe.com/billing/subscriptions/usage-based/meters/configure)
6248
- explain the provider side. Use those references to interpret provider processing;
6249
- use the application's local records to identify what was actually measured.`,
6250
- },
6251
- {
6252
- managedPath: 'billing-and-subscriptions/reconcile-access.md',
6253
- unitRef: 'technical-documentation:unit/reconcile-billing-and-access',
6254
- sourceRefs: [
6255
- 'saas-technical-doc:engine-content/reconcile-billing-and-access',
6256
- 'source:consumer-fact:billing-lifecycle',
6257
- ],
6258
- markdown: `# Reconcile billing and application access
6259
-
6260
- Use reconciliation when the provider, the application subscription and the
6261
- person's visible access do not tell the same story. Keep those layers separate;
6262
- changing a role to conceal a billing problem creates a second access problem.
6263
-
6264
- | Before you begin | Successful result |
6265
- | --- | --- |
6266
- | Have the environment, billed organization, subscription and invoice identities, expected product grants, original application action and approximate change time | The first inconsistent layer is identified, repaired through its normal workflow and verified against current application state without a duplicate charge or unrelated access grant |
6267
-
6268
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#subscriptionStatusesMarkdown}}
6269
-
6270
- ## Name the layer that disagrees
6271
-
6272
- | Layer | Evidence to read | Typical question |
6273
- | --- | --- | --- |
6274
- | Commercial offer | Current product, price, quantity and behavior policy shown by the application | Was the approved item actually offered for this scope? |
6275
- | Provider transaction | Authenticated checkout/portal and invoice evidence | Did the provider complete, refuse or leave payment incomplete? |
6276
- | Application billing state | Subscription items, status, period dates, invoice summary and usage reporting state | Did the verified result reach the intended billing account? |
6277
- | Billing-derived access | Product grants and resolved feature/limit in the billed scope | Did the application recompute the access that this state should contribute? |
6278
- | Original user action | The operation the person or workload tried to perform | Is the intended outcome now genuinely usable? |
6279
-
6280
- Move down the table in order. A role change at the final layer cannot repair an
6281
- incomplete provider transaction, and a paid invoice does not by itself prove
6282
- that the application's subscription or feature state was updated.
6283
-
6284
- ~~~mermaid
6285
- flowchart TD
6286
- accTitle: Reconcile a billing change from scope to entitlement
6287
- accDescr: The reviewer confirms the billed scope, reads the application subscription and invoice, checks whether the subscription state contributes billing access, compares product grants with resolved access, and then repairs the failing layer.
6288
- symptom["Expected access differs from visible access"] --> scope["Confirm billed organization or person"]
6289
- scope --> subscription["Read current application subscription"]
6290
- subscription --> state{"Active or Trialing?"}
6291
- state -->|No| payment["Inspect invoice and payment state"]
6292
- state -->|Yes| grants["Compare plan and add-on grants"]
6293
- grants --> resolved["Check resolved feature and limit"]
6294
- payment --> repair["Repair billing or wait for verified update"]
6295
- resolved --> repair
6296
- repair --> verify["Repeat the original application action"]
6297
- ~~~
6298
-
6299
- Notice that the flow checks **Active or Trialing** before comparing grants.
6300
- Those are the only maintained subscription states that contribute billing-
6301
- derived access. Payment repair belongs before feature repair when the
6302
- subscription is in another state.
6303
-
6304
- ## Reconciliation checklist
6305
-
6306
- 1. Confirm the environment and billed scope.
6307
- 2. Read the current application subscription; do not rely on a provider email
6308
- or redirect.
6309
- 3. Verify its product items, status, period dates and cancellation flag.
6310
- 4. If payment is involved, match the invoice total, currency and state.
6311
- 5. Compare the plan and add-on grants with the resolved feature or limit.
6312
- 6. Repeat the original application operation to prove access, not merely the
6313
- Billing screen.
6314
- 7. Record the final subscription state and the evidence used to close the case.
6315
-
6316
- Where a change may still be processing, preserve the first attempt and allow a
6317
- bounded observation window instead of opening a replacement checkout. Where a
6318
- write returned no response, re-read current subscription state before deciding
6319
- whether a retry is safe.
6320
-
6321
- Only Active and Trialing subscriptions contribute billing-derived access. A
6322
- feature can still come from another allowed source, and a finite application
6323
- limit can combine with plan and add-on limits. Explain the actual resolved
6324
- result instead of assuming one plan label owns all access.
6325
-
6326
- ## Choose the repair by the failing layer
6327
-
6328
- - **Wrong scope or offer:** return to the intended organization and select a
6329
- current product/price through the normal Billing journey.
6330
- - **Incomplete or failed payment:** use the authenticated billing portal; do not
6331
- send payment details to application support.
6332
- - **Stale application subscription:** preserve transaction and correlation
6333
- evidence for support rather than patching the subscription record.
6334
- - **Correct subscription but wrong grants:** compare the product and add-on
6335
- definitions with the resolved feature/limit; do not add a broad role as a
6336
- substitute.
6337
- - **Correct resolved access but failed action:** troubleshoot the operation's
6338
- organization, role, resource visibility and request separately from billing.
6339
-
6340
- Close with the organization, original symptom, responsible layer, correction,
6341
- final subscription/invoice state and the repeated application action. Exclude
6342
- card details, provider secrets and complete invoice documents.`,
6343
- },
6344
- {
6345
- managedPath: 'billing-and-subscriptions/troubleshoot.md',
6346
- unitRef: 'technical-documentation:unit/troubleshoot-billing-change',
6347
- sourceRefs: [
6348
- 'saas-technical-doc:engine-content/troubleshoot-billing-change',
6349
- 'source:consumer-fact:billing-lifecycle',
6350
- ],
6351
- markdown: `# Troubleshoot a billing change
6352
-
6353
- Begin with what the administrator or user can observe. Repeating checkout,
6354
- granting a broader role or manually changing access before identifying the
6355
- failed layer can create duplicate charges or hide the original problem.
6356
-
6357
- | Before you begin | Successful result |
6358
- | --- | --- |
6359
- | Preserve the environment, organization, first attempt, product/price, subscription or invoice identifier, approximate time, visible status and original application symptom | One failing boundary is identified and repaired without duplicating a purchase, exposing payment data or leaving unrelated access in place |
6360
-
6361
- ## Diagnose from the first durable record
6362
-
6363
- ~~~mermaid
6364
- flowchart TD
6365
- accTitle: Troubleshoot a billing change without repeating it blindly
6366
- accDescr: The administrator starts from the first checkout or subscription evidence, confirms the billed scope, reads current subscription and invoice state, compares product grants with resolved access, then repeats the original application action after repairing only the failing layer.
6367
- symptom["Billing or access result is unexpected"] --> scope["Confirm environment and billed scope"]
6368
- scope --> attempt["Preserve first checkout or change evidence"]
6369
- attempt --> subscription{"Application subscription present?"}
6370
- subscription -->|No or incomplete| payment["Check invoice/provider evidence and processing state"]
6371
- subscription -->|Yes| status{"State contributes billing access?"}
6372
- status -->|No| payment
6373
- status -->|Yes| grants["Compare product/add-on grants with resolved access"]
6374
- grants --> action["Repeat original application action"]
6375
- payment --> repair["Repair payment or reconcile verified provider result"]
6376
- repair --> subscription
6377
- ~~~
6378
-
6379
- Do not begin from a success redirect, provider email or plan label. Begin from
6380
- the application billing account and current subscription for the intended
6381
- scope, then correlate provider evidence only where the state requires it.
6382
-
6383
- Use these maintained state meanings while diagnosing:
6384
-
6385
- {{CONSUMER_FACT:source:consumer-fact:billing-lifecycle#subscriptionStatusesMarkdown}}
6386
-
6387
- ## Start from the symptom
6388
-
6389
- | Symptom | First checks | Safe next step |
6390
- | --- | --- | --- |
6391
- | Returned from checkout but no subscription appears | Intended scope, existing incomplete subscription, processing delay and the first attempt's billing evidence | Reconcile the first attempt before opening checkout again |
6392
- | Subscription is Active but feature is unavailable | Product and price, expected grants, current organization, resolved feature/limit and the original operation | Repair grant/access reconciliation; do not broaden the person's role |
6393
- | Subscription is Past due, Unpaid or Paused | Current invoice state and the authenticated billing portal; these states do not contribute billing-derived access | Resolve payment in the portal, then wait for verified application state |
6394
- | Downgrade or cancellation appears immediate | Returned subscription items, status, period end and cancellation flag; do not rely only on the requested timing | Compare the applied product policy and access consequence with the approval |
6395
- | Cancellation was requested but access remains | End-of-period cancellation may intentionally keep an Active subscription until period end | Record the effective boundary and verify again when the period ends |
6396
- | Invoice total differs from expectation | Currency, tax display, quantity, proration, promotion and billing period | Compare the selected offer and applied adjustment before disputing the charge |
6397
- | Metered usage differs from provider | Local period total, last recorded time, reported state and aggregation delay | Wait for the bounded reporting window, then compare the same product and period |
6398
- | Resume is refused | The subscription must still be pending end-of-period cancellation | If it already ended, use a newly approved purchase instead of resume |
6399
- | Billing screen is correct but the application action fails | Resolved feature/limit, organization, role and exact operation | Troubleshoot authorization or resource scope separately from billing |
6400
-
6401
- ## Recover without making the record harder to understand
6402
-
6403
- - Keep the first checkout or change identifiers and do not create a replacement
6404
- until its state is known.
6405
- - Use the authenticated billing portal for payment methods and invoice documents.
6406
- - Correct the product, price or scope through the normal billing operation; do
6407
- not patch subscription status directly.
6408
- - After recovery, verify both the billing record and the application action that
6409
- depends on it.
6410
- - Share invoice identifiers, status, timestamps and correlation evidence with
6411
- support—not card details, full invoice documents or provider secrets.
6412
-
6413
- If the application record and provider evidence still disagree, preserve both
6414
- views and escalate the reconciliation. The application retains subscriptions,
6415
- invoice references and usage records as financial history rather than deleting
6416
- them when a scope is retired.
6417
-
6418
- ## Escalate a reproducible case
6419
-
6420
- Provide the environment, billed organization, product/price identity, first
6421
- attempt or subscription identifier, invoice summary, timestamps, current state,
6422
- expected access, observed application action and correlation identifier. State
6423
- whether payment, subscription state or access changed between observations.
6424
- Never attach card data, payment-method details, complete invoice documents,
6425
- hosted invoice URLs, provider secrets or unrestricted personal data.`,
6426
- },
6427
- {
6428
- managedPath: 'integrations.md',
6429
- unitRef: 'technical-documentation:unit/integrations',
6430
- sourceRefs: ['saas-technical-doc:engine-content/integrations'],
6431
- markdown: `# Integrations
6432
-
6433
- An integration connects this application to another system so that people do
6434
- not have to copy information or repeat the same action by hand. Start with the
6435
- outcome you need, then choose the smallest contract that can deliver it safely.
6436
-
6437
- | Before you begin | Successful result |
6438
- | --- | --- |
6439
- | Name one business outcome, the source of truth, the organization affected, the acting person or workload and who owns failures | The chosen connection proves one bounded allowed outcome, one expected refusal and a recovery path that does not expose credentials or repeat a business effect |
6440
-
6441
- ## Which integration should you choose?
6442
-
6443
- | You need to… | Use | Why |
6444
- | --- | --- | --- |
6445
- | Read or change application data on demand | [REST API](/integrations/rest-api) | Your system sends a request and receives an immediate response |
6446
- | Build a workflow in n8n, Zapier or Make | [Automation platforms](/integrations/automation-platforms) | The platform calls the REST API or receives a published event |
6447
- | React when the application publishes an event | The Webhooks section, when available below | The application pushes a signed delivery to your receiver |
6448
- | Connect Cursor, Codex or Claude Code | The AI coding tools section, when available below | The client discovers MCP tools available to its authenticated identity |
6449
- | Give another AI client callable tools or exchange agent tasks | The Agent integrations section below | MCP covers tools; A2A covers messages and task lifecycles |
6450
-
6451
- Do not choose a protocol because its name is familiar. Choose it because its
6452
- interaction model matches the business outcome. A scheduled REST poll is not a
6453
- replacement for an event when timeliness matters; a webhook is not a command
6454
- channel; an MCP tool call is not an A2A conversation.
6455
-
6456
- ~~~mermaid
6457
- flowchart TD
6458
- accTitle: Choose an integration contract from the intended outcome
6459
- accDescr: Immediate request and response work uses REST. Event notification uses webhooks. Tool discovery and invocation uses MCP. Agent conversations and tasks use A2A.
6460
- outcome["What must the other system accomplish?"] --> immediate{"Immediate request and response?"}
6461
- immediate -->|Yes| rest["REST API"]
6462
- immediate -->|No| event{"React to an application event?"}
6463
- event -->|Yes| webhook["Webhook"]
6464
- event -->|No| agent{"AI or agent interaction?"}
6465
- agent -->|Call advertised tools| mcp["MCP"]
6466
- agent -->|Exchange messages and tasks| a2a["A2A"]
6467
- ~~~
6468
-
6469
- The diagram is a starting point, not a permission decision. The identity used
6470
- by the integration still needs access to the intended organization, resources
6471
- and operations.
6472
-
6473
- ## Keep the source of truth explicit
6474
-
6475
- Decide which system owns the final fact before writing synchronization logic.
6476
- REST can retrieve or change current application state; a webhook announces that
6477
- an event occurred; MCP invokes an advertised operation; A2A carries a message or
6478
- task. None of those contracts automatically makes the receiving copy
6479
- authoritative.
6480
-
6481
- After a timeout, duplicate or conflicting change, reconcile against the system
6482
- that owns the fact. If ownership is shared or unclear, define a human decision
6483
- path rather than letting the last retry silently win.
6484
-
6485
- ## Before you connect anything
6486
-
6487
- - Name the business outcome and the system that owns the authoritative state.
6488
- - Use a separate identity and credential for each deployed workload.
6489
- - Begin in a non-production organization with the least privilege required.
6490
- - Decide how you will reconcile timeouts, duplicates and partially completed work.
6491
- - Record correlation information without copying credentials or restricted data.
6492
-
6493
- ## Prove the boundary before production
6494
-
6495
- Start with a harmless action whose correct result a person can recognize. Prove
6496
- the intended environment, identity, organization and response shape. Then prove
6497
- one expected refusal: another organization, an unavailable operation, an
6498
- invalid signature or insufficient authority must remain blocked.
6499
-
6500
- Only after those checks should you add a mutation, automatic retry, schedule or
6501
- production volume. For a state-changing path, deliberately simulate a lost
6502
- response and show how authoritative state is read before another attempt.
6503
-
6504
- Retain the integration owner, deployed workload, non-secret credential
6505
- identifier, environment, organization, operation or event identity, outcome and
6506
- correlation evidence. Define how the credential is rotated or revoked and who
6507
- receives an alert when the business result cannot be reconciled.
6508
-
6509
- If you are building a new connection, begin with [Create your own integration](/integrations/create-your-own-integration). The API reference supplies exact paths and schemas after you have chosen the correct journey.`,
6510
- },
6511
- {
6512
- managedPath: 'integrations/automation-platforms.md',
6513
- unitRef: 'technical-documentation:unit/automation-platforms',
6514
- sourceRefs: ['saas-technical-doc:engine-content/automation-platforms'],
6515
- markdown: `# Automation platforms
6516
-
6517
- Use an automation platform when a business process must move information or
6518
- start work across systems without requiring a custom deployed service. This
6519
- section covers **n8n**, **Zapier** and **Make**. The application does not need a
6520
- platform-specific connector: each platform can call the published REST API,
6521
- and it can receive application webhooks when a Webhooks section is available.
6522
-
6523
- ## Start from the business event
6524
-
6525
- Describe the automation in one sentence before opening the workflow editor:
6526
- “When an approved record changes, create or update its counterpart in the
6527
- reporting system.” That sentence identifies the trigger, the intended effect
6528
- and the system that owns the final state.
6529
-
6530
- | The work starts when… | Begin with | Why |
6531
- | --- | --- | --- |
6532
- | A schedule, form or another system produces input | An authenticated REST request | The workflow controls when the application is read or changed |
6533
- | This application publishes a relevant event | A webhook receiver | The workflow starts from the event instead of repeatedly polling |
6534
- | A person presses a workflow button | A safe read, followed by an explicit write | The person can confirm scope before a change is made |
6535
-
6536
- ~~~mermaid
6537
- flowchart LR
6538
- accTitle: Build an automation around one authoritative business outcome
6539
- accDescr: A schedule, human action, or application event starts the workflow. The workflow validates the input, reads current application state, applies one intended effect, and records enough evidence to reconcile failures.
6540
- trigger["Schedule, person or application event"] --> validate["Validate input and organization"]
6541
- validate --> read["Read authoritative state"]
6542
- read --> decide{"Change still required?"}
6543
- decide -->|No| finish["Record no change"]
6544
- decide -->|Yes| write["Apply one documented operation"]
6545
- write --> confirm["Confirm result and retain correlation"]
6546
- ~~~
6547
-
6548
- ## Choose your platform
6549
-
6550
- - [Connect n8n](/integrations/automation/n8n) when you want a visual workflow
6551
- that can also be self-hosted and extended with technical nodes.
6552
- - [Connect Zapier](/integrations/automation/zapier) when the business workflow
6553
- already lives in Zaps and should use an API or webhook step.
6554
- - [Connect Make](/integrations/automation/make) when you want to map a scenario
6555
- visually and control HTTP request fields in a dedicated module.
6556
-
6557
- ## Keep the first workflow deliberately small
6558
-
6559
- Use a dedicated credential for one environment and one workflow. Prove one
6560
- read operation, then one write only if the business outcome requires it. Store
6561
- the credential in the platform's credential or connection store—not in a URL,
6562
- ordinary field, workflow name, execution note or shared screenshot.
6563
-
6564
- Before enabling unattended runs, test invalid input, expired or revoked access,
6565
- insufficient permission, a timeout after a write and a duplicate trigger. The
6566
- workflow is production-ready only when an operator can determine whether the
6567
- business effect occurred without blindly repeating it.`,
6568
- },
6569
- {
6570
- managedPath: 'integrations/automation/n8n.md',
6571
- unitRef: 'technical-documentation:unit/connect-n8n',
6572
- sourceRefs: [
6573
- 'saas-technical-doc:engine-content/connect-n8n',
6574
- 'source:consumer-fact:access-api-keys',
6575
- ],
6576
- markdown: `# Connect n8n
6577
-
6578
- Use n8n when a visual workflow should read or change information in this
6579
- application. There is no application-specific n8n node to install: the workflow
6580
- uses n8n's standard **HTTP Request** node and the REST API published in this
6581
- documentation.
6582
-
6583
- This guide first builds a manual, read-only workflow. By the end, one click in
6584
- n8n will call one authenticated operation and show recognizable application
6585
- data in the node output. Only then will you connect a trigger or add a write.
6586
-
6587
- > **If the application should start the workflow when something happens,**
6588
- > complete the read-only connection first, then follow **Start from an
6589
- > application event** below. Do not poll repeatedly when a published webhook
6590
- > already represents the event you need.
6591
-
6592
- ## What do you need before starting?
6593
-
6594
- Ask the application administrator or integration owner for:
6595
-
6596
- - the server origin shown in this application's API reference;
6597
- - one authenticated **GET** operation and its complete path;
6598
- - the non-production organization and known data you may use for the test;
6599
- - a dedicated API key with only the role accepted by that operation; and
6600
- - an owner who can revoke the key if the workflow is abandoned or exposed.
6601
-
6602
- Use [Create and store an API key](/access-and-identity/api-keys-create-and-store)
6603
- if the credential has not been prepared. Keep n8n and the application in the
6604
- same environment throughout the test.
6605
-
6606
- ## Build the first read-only workflow
6607
-
6608
- ### 1. Create an explicit test trigger
6609
-
6610
- Create a new workflow and keep it inactive. Add a **Manual Trigger** so that the
6611
- application is contacted only when you choose **Execute workflow**. Name the
6612
- workflow after the business result and environment—for example, “Read approved
6613
- records — test”—not after the credential.
6614
-
6615
- ### 2. Add the application request
6616
-
6617
- Add an **HTTP Request** node after the trigger, then complete these fields from
6618
- the same API-reference operation:
5847
+ Add an **HTTP Request** node after the trigger, then complete these fields from
5848
+ the same API-reference operation:
6619
5849
 
6620
5850
  | n8n field | Value to use | How to verify it |
6621
5851
  | --- | --- | --- |
6622
5852
  | **Method** | The operation's documented HTTP method, initially **GET** | It matches the operation heading in the API reference |
6623
- | **URL** | Server origin followed by the complete documented operation path | The origin appears once and the API path appears once |
5853
+ | **URL** | \`{{APPLICATION_CONNECTION_VALUE:baseUrl}}\` followed by the operation's documented path | The origin appears once and the API path appears once |
6624
5854
  | **Authentication** | **Generic Credential Type**, then **Header Auth** | The key is stored as an n8n credential, not in the URL or workflow data |
6625
5855
  | **Send Headers** | Add **Accept** with value **application/json** | The request asks for the documented JSON response |
6626
5856
  | Query/path values | Only values required by the selected operation | Organization and resource identifiers are in the locations defined by the reference |
@@ -6756,6 +5986,7 @@ HTTP response needs deeper diagnosis.
6756
5986
  unitRef: 'technical-documentation:unit/connect-zapier',
6757
5987
  sourceRefs: [
6758
5988
  'saas-technical-doc:engine-content/connect-zapier',
5989
+ 'source:companion-projection:application-connection',
6759
5990
  'source:consumer-fact:access-api-keys',
6760
5991
  ],
6761
5992
  markdown: `# Connect Zapier
@@ -6816,7 +6047,7 @@ Complete the API Request action from one operation in the current reference:
6816
6047
  | Zapier field | Value to use | Verification |
6817
6048
  | --- | --- | --- |
6818
6049
  | Method | The documented method, initially **GET** | It matches the operation heading |
6819
- | URL | Server origin plus the complete operation path | Origin and API prefix each appear once |
6050
+ | URL | \`{{APPLICATION_CONNECTION_VALUE:baseUrl}}\` plus the operation's documented path | Origin and API prefix each appear once |
6820
6051
  | Headers | **Accept: application/json** plus connection-managed authentication | No API key is visible in the Zap step |
6821
6052
  | Parameters | Only required query/path values | Organization and record values are in the documented locations |
6822
6053
  | Body | Empty for the selected read unless the operation explicitly documents one | No response field was copied back as an invented request field |
@@ -6924,6 +6155,7 @@ flowchart TD
6924
6155
  unitRef: 'technical-documentation:unit/connect-make',
6925
6156
  sourceRefs: [
6926
6157
  'saas-technical-doc:engine-content/connect-make',
6158
+ 'source:companion-projection:application-connection',
6927
6159
  'source:consumer-fact:access-api-keys',
6928
6160
  ],
6929
6161
  markdown: `# Connect Make
@@ -6975,7 +6207,7 @@ Complete the remaining module fields from the same API-reference operation:
6975
6207
 
6976
6208
  | Make field | Value to use | Verification |
6977
6209
  | --- | --- | --- |
6978
- | URL | Server origin plus the complete operation path | HTTPS, origin and API prefix each appear once |
6210
+ | URL | \`{{APPLICATION_CONNECTION_VALUE:baseUrl}}\` plus the operation's documented path | The scheme, origin and API prefix each appear once |
6979
6211
  | Method | The documented method, initially **GET** | It matches the operation heading |
6980
6212
  | Headers | Add **Accept: application/json** | Authentication remains in **Credentials**, not duplicated here |
6981
6213
  | Query parameters | Only parameters accepted by the operation | Organization and record values are in documented locations |
@@ -7087,7 +6319,10 @@ sequenceDiagram
7087
6319
  {
7088
6320
  managedPath: 'integrations/ai-coding-tools.md',
7089
6321
  unitRef: 'technical-documentation:unit/ai-coding-tools',
7090
- sourceRefs: ['saas-technical-doc:engine-content/ai-coding-tools'],
6322
+ sourceRefs: [
6323
+ 'saas-technical-doc:engine-content/ai-coding-tools',
6324
+ 'source:companion-projection:application-connection',
6325
+ ],
7091
6326
  markdown: `# AI coding tools
7092
6327
 
7093
6328
  This application publishes an **MCP tool server** that supported AI coding
@@ -7099,7 +6334,7 @@ access, and it does not turn every application action into an autonomous one.
7099
6334
 
7100
6335
  - the server origin for the intended application environment;
7101
6336
  - authorization to use the MCP audience at
7102
- **<server-origin>/api/v1/mcp**;
6337
+ **{{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}**;
7103
6338
  - membership and roles for the organization you intend to work in;
7104
6339
  - confirmation of which tools may run automatically and which require approval.
7105
6340
 
@@ -7148,7 +6383,10 @@ be pasted into either.`,
7148
6383
  {
7149
6384
  managedPath: 'integrations/ai-tools/cursor.md',
7150
6385
  unitRef: 'technical-documentation:unit/connect-cursor',
7151
- sourceRefs: ['saas-technical-doc:engine-content/connect-cursor'],
6386
+ sourceRefs: [
6387
+ 'saas-technical-doc:engine-content/connect-cursor',
6388
+ 'source:companion-projection:application-connection',
6389
+ ],
7152
6390
  markdown: `# Connect Cursor
7153
6391
 
7154
6392
  Use this guide when Cursor Agent should read or act on information in this
@@ -7182,14 +6420,13 @@ Create the selected **mcp.json** file and add:
7182
6420
  {
7183
6421
  "mcpServers": {
7184
6422
  "application": {
7185
- "url": "<server-origin>/api/v1/mcp"
6423
+ "url": "{{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}"
7186
6424
  }
7187
6425
  }
7188
6426
  }
7189
6427
  ~~~
7190
6428
 
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
6429
+ 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
6430
  header to the shared JSON when the server supports interactive OAuth.
7194
6431
 
7195
6432
  Cursor's [current MCP documentation](https://cursor.com/docs/mcp) owns the
@@ -7287,7 +6524,10 @@ sequenceDiagram
7287
6524
  {
7288
6525
  managedPath: 'integrations/ai-tools/codex.md',
7289
6526
  unitRef: 'technical-documentation:unit/connect-codex',
7290
- sourceRefs: ['saas-technical-doc:engine-content/connect-codex'],
6527
+ sourceRefs: [
6528
+ 'saas-technical-doc:engine-content/connect-codex',
6529
+ 'source:companion-projection:application-connection',
6530
+ ],
7291
6531
  markdown: `# Connect Codex
7292
6532
 
7293
6533
  Use this guide when Codex needs authorized information or operations from this
@@ -7318,12 +6558,12 @@ Add this table to the selected configuration file:
7318
6558
 
7319
6559
  ~~~toml
7320
6560
  [mcp_servers.application]
7321
- url = "<server-origin>/api/v1/mcp"
6561
+ url = "{{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}"
7322
6562
  default_tools_approval_mode = "prompt"
7323
6563
  ~~~
7324
6564
 
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
6565
+ The URL above is this application’s MCP endpoint. Confirm it belongs to the environment
6566
+ you intend before connecting. Keep **/api/v1/mcp** exactly once. The prompt approval mode means
7327
6567
  Codex asks before using tools from this server.
7328
6568
 
7329
6569
  The [official Codex MCP documentation](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)
@@ -7423,7 +6663,10 @@ connectivity. A working read does not justify automatic write approval.
7423
6663
  {
7424
6664
  managedPath: 'integrations/ai-tools/claude-code.md',
7425
6665
  unitRef: 'technical-documentation:unit/connect-claude-code',
7426
- sourceRefs: ['saas-technical-doc:engine-content/connect-claude-code'],
6666
+ sourceRefs: [
6667
+ 'saas-technical-doc:engine-content/connect-claude-code',
6668
+ 'source:companion-projection:application-connection',
6669
+ ],
7427
6670
  markdown: `# Connect Claude Code
7428
6671
 
7429
6672
  Use this guide when Claude Code should read or act on information in this
@@ -7452,11 +6695,11 @@ Start with local scope unless a reviewed team or personal-wide need exists.
7452
6695
  Run this from the intended project:
7453
6696
 
7454
6697
  ~~~bash
7455
- claude mcp add --transport http application <server-origin>/api/v1/mcp
6698
+ claude mcp add --transport http application {{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}
7456
6699
  ~~~
7457
6700
 
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
6701
+ The URL above is this application’s MCP endpoint. Confirm it belongs to the environment
6702
+ you intend before connecting, and keep **/api/v1/mcp** exactly once. Add **--scope project** or
7460
6703
  **--scope user** only after making the scope decision above.
7461
6704
 
7462
6705
  The [current Claude Code MCP documentation](https://code.claude.com/docs/en/mcp)
@@ -7677,6 +6920,7 @@ evidence, but they do not replace the application's authoritative audit trail.
7677
6920
  unitRef: 'technical-documentation:unit/first-request',
7678
6921
  sourceRefs: [
7679
6922
  'saas-technical-doc:engine-content/first-request',
6923
+ 'source:companion-projection:application-connection',
7680
6924
  'source:consumer-fact:access-api-keys',
7681
6925
  ],
7682
6926
  markdown: `# Create your own integration
@@ -7764,13 +7008,13 @@ unclear operation contract.
7764
7008
 
7765
7009
  ## Step 2: send the request once
7766
7010
 
7767
- Replace the two angle-bracket placeholders with the server origin and operation
7768
- path from the same API-reference environment. The generated authorization line
7011
+ The origin below is this application's own, so the only thing left to supply is
7012
+ the operation path from the API reference and your key. The authorization line
7769
7013
  is the application's maintained API-key transport contract.
7770
7014
 
7771
7015
  ~~~bash
7772
7016
  curl --fail-with-body \
7773
- --url "<server-origin><documented-operation-path>" \
7017
+ --url "{{APPLICATION_CONNECTION_VALUE:baseUrl}}<documented-operation-path>" \
7774
7018
  {{CONSUMER_FACT:source:consumer-fact:access-api-keys#curlRequestHeader}} \
7775
7019
  --header "accept: application/json"
7776
7020
  ~~~
@@ -7860,1203 +7104,717 @@ including collections, controlled writes and troubleshooting.`,
7860
7104
  {
7861
7105
  managedPath: 'integrations/rest-api.md',
7862
7106
  unitRef: 'technical-documentation:unit/common-rest-api',
7863
- sourceRefs: ['saas-technical-doc:engine-content/common-rest-api'],
7107
+ sourceRefs: [
7108
+ 'saas-technical-doc:engine-content/common-rest-api',
7109
+ ],
7864
7110
  markdown: `# REST API
7865
7111
 
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.
7112
+ 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
7113
 
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 |
7114
+ ## What you get
7874
7115
 
7875
- ## Decide whether REST matches the work
7116
+ - 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.
7117
+ - 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.
7118
+ - 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
7119
 
7877
- | Need | Use | Why |
7120
+ ## Choose the right tool first
7121
+
7122
+ | You need to | Use | Why |
7878
7123
  | --- | --- | --- |
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 |
7124
+ | Read or change data now, and know the result | REST | The application answers each request with the outcome |
7125
+ | React after something happens in the application | [Webhooks](/integrations/webhooks) | The application calls you; no polling |
7126
+ | Let an AI assistant work with the application | [MCP](/integrations/mcp) | Tools are discovered and invoked by the assistant, not scripted by you |
7127
+ | Build in n8n, Zapier or Make | [Automation platforms](/integrations/automation-platforms) | Those tools already speak REST; the guides show the exact settings |
7883
7128
 
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.
7129
+ ## The four pages of this section
7888
7130
 
7889
- ## Follow the request lifecycle
7131
+ 1. [Send your first API request](/integrations/rest/send-request): reach the right environment with the right credential and prove it in ten minutes.
7132
+ 2. [Read collections](/integrations/rest/read-collections): page, sort, search and filter without skipping or duplicating records.
7133
+ 3. [Write and reconcile changes](/integrations/rest/write-and-reconcile): make a change once, use optimistic locking, and recover from a lost response.
7134
+ 4. [Troubleshoot an API request](/integrations/rest/troubleshoot): turn a status code and an error body into the one thing to fix.
7890
7135
 
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"]
7136
+ 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.`,
7137
+ },
7138
+ {
7139
+ managedPath: 'integrations/rest/conventions.md',
7140
+ unitRef: 'technical-documentation:unit/rest-api-conventions',
7141
+ sourceRefs: [
7142
+ 'saas-technical-doc:engine-content/rest-api-conventions',
7143
+ 'source:companion-projection:application-connection',
7144
+ 'source:consumer-fact:access-api-keys',
7145
+ 'source:consumer-fact:rest-conventions',
7146
+ ],
7147
+ markdown: `# REST API conventions
7148
+
7149
+ 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.
7150
+
7151
+ ## Addressing
7152
+
7153
+ - Every path in the API reference already starts with the \`/api/v1\` mount. Prepend the base URL of the environment you are calling:
7154
+
7155
+ {{APPLICATION_CONNECTION:baseUrls}}
7156
+
7157
+ - 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.
7158
+ - 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.
7159
+ - Send and expect \`application/json\`. Identifiers are opaque strings: store them, compare them, never parse them.
7160
+ - Header names are case-insensitive; this documentation writes them the way the application emits them.
7161
+
7162
+ ## Authentication
7163
+
7164
+ {{CONSUMER_FACT:source:consumer-fact:access-api-keys#markdown}}
7165
+
7166
+ 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.
7167
+
7168
+ ## Collections
7169
+
7170
+ List and search operations share one query vocabulary:
7171
+
7172
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#collectionQueryMarkdown}}
7173
+
7174
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#paginationResponseMarkdown}}
7175
+
7176
+ A concrete request and its response, using the members of your own organization:
7177
+
7178
+ ~~~bash
7179
+ curl --fail-with-body \\
7180
+ --url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID/organization-members?page=1&limit=20&sort=joinedAt:desc" \\
7181
+ --header "Authorization: $ORGANIZATION_API_KEY" \\
7182
+ --header "accept: application/json"
7183
+ ~~~
7184
+
7185
+ ~~~json
7186
+ {
7187
+ "data": [
7188
+ {
7189
+ "_id": "0d3f2a9e-6c41-4b7a-8e15-5f9c2b7d4a10",
7190
+ "userId": "b8e4c2f1-3a57-4d09-9f6e-1c2d3e4f5a6b",
7191
+ "userEmail": "alex.morgan@example.com",
7192
+ "organizationId": "7c1f6f0a-4b1e-4d8f-9a0c-2e5b3c9d1f42",
7193
+ "roles": ["ORG_MEMBER"],
7194
+ "status": "ACTIVE",
7195
+ "createdAt": "2026-07-03T09:15:00.000Z",
7196
+ "updatedAt": "2026-08-18T08:00:00.000Z",
7197
+ "_version": 4
7198
+ }
7199
+ ],
7200
+ "pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
7201
+ }
7202
+ ~~~
7203
+
7204
+ ## Writes
7205
+
7206
+ - 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.
7207
+ - 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.
7208
+ - 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.
7209
+
7210
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#concurrencyMarkdown}}
7211
+
7212
+ ## Errors
7213
+
7214
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#errorEnvelopeMarkdown}}
7215
+
7216
+ For example, removing the last owner of an organization is refused like this:
7217
+
7218
+ ~~~json
7219
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#errorExampleJson}}
7901
7220
  ~~~
7902
7221
 
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.`,
7222
+ Branch on the HTTP status first, then on \`error.type\`, and on \`error.code\` only for refusals the operation documents by name:
7223
+
7224
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#errorStatusMarkdown}}
7225
+
7226
+ ## Rate limits
7227
+
7228
+ The application enforces several windows at once and reports the tightest one:
7229
+
7230
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#rateLimitMarkdown}}
7231
+
7232
+ 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.
7233
+
7234
+ ## Support evidence
7235
+
7236
+ 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
7237
  },
7940
7238
  {
7941
7239
  managedPath: 'integrations/rest/send-request.md',
7942
7240
  unitRef: 'technical-documentation:unit/send-api-request',
7943
7241
  sourceRefs: [
7944
7242
  'saas-technical-doc:engine-content/send-api-request',
7243
+ 'source:companion-projection:application-connection',
7945
7244
  'source:consumer-fact:access-api-keys',
7946
7245
  ],
7947
7246
  markdown: `# Send your first API request
7948
7247
 
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.
7248
+ 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
7249
 
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 |
7250
+ ## What you need
7957
7251
 
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"]
7252
+ - The **base URL** of the environment you are calling. {{APPLICATION_NAME}} publishes these:
7253
+
7254
+ {{APPLICATION_CONNECTION:baseUrls}}
7255
+
7256
+ - Your **organization identifier**, shown in the application's organization settings and present in the URL of most screens.
7257
+ - 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.
7258
+
7259
+ 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.
7260
+
7261
+ ## Step 1: read your own organization
7262
+
7263
+ The safest first call reads the organization the key belongs to. It needs no other identifier and changes nothing.
7264
+
7265
+ ~~~bash
7266
+ BASE_URL="{{APPLICATION_CONNECTION_VALUE:baseUrl}}" # this application's own origin
7267
+ ORGANIZATION_ID="7c1f6f0a-4b1e-4d8f-9a0c-2e5b3c9d1f42" # from the application's organization settings
7268
+ ORGANIZATION_API_KEY="sk_org_…" # the key you created
7269
+
7270
+ curl --fail-with-body \\
7271
+ --url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID" \\
7272
+ {{CONSUMER_FACT:source:consumer-fact:access-api-keys#curlRequestHeader}} \\
7273
+ --header "accept: application/json"
7969
7274
  ~~~
7970
7275
 
7971
- ## What you need
7276
+ Replace the organization id and the key with yours; the base URL above is already this application's. Leave the header exactly as shown. {{CONSUMER_FACT:source:consumer-fact:access-api-keys#markdown}}
7972
7277
 
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.
7278
+ 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
7279
 
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.
7280
+ ## Step 2: read a collection
7982
7281
 
7983
- Copy the operation path and parameters from the API reference. For an API key,
7984
- send the key in the standard authorization header:
7282
+ Now list the organization's members. This exercises the collection envelope you will meet on every list and search operation:
7985
7283
 
7986
7284
  ~~~bash
7987
- curl --fail-with-body \
7988
- --url "<server-origin>/<documented-path>" \
7989
- {{CONSUMER_FACT:source:consumer-fact:access-api-keys#curlRequestHeader}} \
7285
+ curl --fail-with-body \\
7286
+ --url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID/organization-members?limit=5&sort=joinedAt:desc" \\
7287
+ --header "Authorization: $ORGANIZATION_API_KEY" \\
7990
7288
  --header "accept: application/json"
7991
7289
  ~~~
7992
7290
 
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.
7291
+ 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
7292
 
7999
- ## Verify the result
7293
+ ## Step 3: prove the boundary holds
8000
7294
 
8001
- Do not stop at “the request returned JSON.” Confirm:
7295
+ A connection is only safe when a wrong credential is refused. Send the first request again with the key deliberately broken:
7296
+
7297
+ ~~~bash
7298
+ curl --include \\
7299
+ --url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID" \\
7300
+ --header "Authorization: sk_org_not_a_real_key" \\
7301
+ --header "accept: application/json"
7302
+ ~~~
8002
7303
 
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.
7304
+ 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.
8008
7305
 
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.
7306
+ ## If it fails
8013
7307
 
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.
7308
+ | You see | It means | Do this |
7309
+ | --- | --- | --- |
7310
+ | 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 |
7311
+ | **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 |
7312
+ | **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 |
7313
+ | **404** \`NOT_FOUND\` | The organization identifier is wrong, or this key cannot see it | Copy the identifier from the application again |
7314
+ | **429** \`RATE_LIMIT\` | Too many requests | Wait \`Retry-After\` seconds |
8018
7315
 
8019
- ## Before the first write
7316
+ 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
7317
 
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.
7318
+ ## Before your first write
8024
7319
 
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.`,
7320
+ 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
7321
  },
8030
7322
  {
8031
7323
  managedPath: 'integrations/rest/read-collections.md',
8032
7324
  unitRef: 'technical-documentation:unit/work-with-api-collections',
8033
- sourceRefs: ['saas-technical-doc:engine-content/work-with-api-collections'],
8034
- markdown: `# Read collections reliably
7325
+ sourceRefs: [
7326
+ 'saas-technical-doc:engine-content/work-with-api-collections',
7327
+ 'source:consumer-fact:rest-conventions',
7328
+ ],
7329
+ markdown: `# Read collections
8035
7330
 
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.
7331
+ 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
7332
 
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 |
7333
+ ## The query vocabulary
8045
7334
 
8046
- ## Begin with the business question
7335
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#collectionQueryMarkdown}}
8047
7336
 
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:
7337
+ 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
7338
 
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.
7339
+ ## The response envelope
8055
7340
 
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.
7341
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#paginationResponseMarkdown}}
8060
7342
 
8061
- ## Read one page at a time
7343
+ An empty \`data\` array on page 1 is a valid answer. Confirm the organization and the filters before treating it as a failure.
8062
7344
 
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.
7345
+ ## Walk every page
8068
7346
 
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"]
7347
+ ~~~bash
7348
+ page=1
7349
+ while : ; do
7350
+ response=$(curl --fail-with-body --silent \\
7351
+ --url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID/organization-members?page=$page&limit=100&sort=joinedAt:asc" \\
7352
+ --header "Authorization: $ORGANIZATION_API_KEY" \\
7353
+ --header "accept: application/json")
7354
+ echo "$response" | jq -c '.data[]' >> members.ndjson
7355
+ totalPages=$(echo "$response" | jq '.pagination.totalPages')
7356
+ [ "$page" -ge "$totalPages" ] && break
7357
+ page=$((page + 1))
7358
+ done
8079
7359
  ~~~
8080
7360
 
8081
- Follow that lifecycle in order:
7361
+ Three rules keep a scan correct:
8082
7362
 
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.
7363
+ 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.
7364
+ 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.
7365
+ 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
7366
 
8151
- ## Continue safely
7367
+ ## Search and filter
8152
7368
 
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
7369
+ Search operations (\`…/search\`) add \`q\` for free text. Combine it with filters and sorting:
8165
7370
 
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.
7371
+ ~~~bash
7372
+ curl --fail-with-body \\
7373
+ --url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID/organization-members/search?q=morgan&status=ACTIVE&sort=joinedAt:desc" \\
7374
+ --header "Authorization: $ORGANIZATION_API_KEY" \\
7375
+ --header "accept: application/json"
7376
+ ~~~
8169
7377
 
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 |
7378
+ Date-range filters are objects and use bracket notation, one key per bound:
8173
7379
 
8174
- ## Define the business identity of the change
7380
+ ~~~text
7381
+ ?createdAt[startDate]=2026-09-01T00:00:00Z&createdAt[endDate]=2026-09-30T23:59:59Z
7382
+ ~~~
8175
7383
 
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.
7384
+ Send instants in ISO 8601 with a timezone. An unparseable bound answers **400** with the field named in \`error.validationErrors\`.
8181
7385
 
8182
- Separate three identifiers where the business process has them:
7386
+ ## Counts and summaries
8183
7387
 
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.
7388
+ 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
7389
 
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.
7390
+ ## Reconcile a long scan
8191
7391
 
8192
- ## Prepare one controlled write
7392
+ 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
7393
 
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.
7394
+ ## When a page fails
8206
7395
 
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
- ~~~
7396
+ 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.`,
7397
+ },
7398
+ {
7399
+ managedPath: 'integrations/rest/write-and-reconcile.md',
7400
+ unitRef: 'technical-documentation:unit/write-and-reconcile-api-changes',
7401
+ sourceRefs: [
7402
+ 'saas-technical-doc:engine-content/write-and-reconcile-api-changes',
7403
+ 'source:consumer-fact:rest-conventions',
7404
+ ],
7405
+ markdown: `# Write and reconcile changes
8222
7406
 
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.
7407
+ 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
7408
 
8227
- ## Reconcile an ambiguous outcome
7409
+ ## Send the change
8228
7410
 
8229
- When the connection closes, times out or loses the response:
7411
+ 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.
7412
+ 2. Read the record first and keep its \`_version\`.
7413
+ 3. Send the write with the version in the \`if-match\` header.
7414
+ 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
7415
 
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.
7416
+ Updating a member's roles, with optimistic locking:
8240
7417
 
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.
7418
+ ~~~bash
7419
+ curl --fail-with-body \\
7420
+ --request PUT \\
7421
+ --url "$BASE_URL/api/v1/organizations/$ORGANIZATION_ID/organization-members/$MEMBER_ID" \\
7422
+ --header "Authorization: $ORGANIZATION_API_KEY" \\
7423
+ --header "content-type: application/json" \\
7424
+ --header "if-match: 4" \\
7425
+ --data '{ "roles": ["ORG_MANAGER"] }'
7426
+ ~~~
8244
7427
 
8245
- ## Treat failures by meaning
7428
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#concurrencyMarkdown}}
8246
7429
 
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.
7430
+ 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
7431
 
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.
7432
+ ## Read the refusal
8260
7433
 
8261
- ## Protect concurrent work
7434
+ A write the application will not perform answers with the standard error envelope. Three cases matter for a writer:
8262
7435
 
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.
7436
+ | Status | \`error.type\` | What happened | What to do |
7437
+ | --- | --- | --- | --- |
7438
+ | **400** | \`VALIDATION\` | The body breaks the schema; \`error.validationErrors\` names each field | Fix the request. Resending it unchanged fails again |
7439
+ | **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 |
7440
+ | **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
7441
 
8269
- ## Preserve useful evidence
7442
+ 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
7443
 
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.
7444
+ ## Recover from a lost response
8276
7445
 
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?**
7446
+ 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
7447
 
8281
- ## Continue safely
7448
+ 1. Stop automatic retries for this change.
7449
+ 2. Read the target record. For a create, search for it by the business key you sent (an email, a reference number, a name).
7450
+ 3. If the change is there, treat the original attempt as applied and continue.
7451
+ 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.
7452
+ 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.
8282
7453
 
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.`,
7454
+ Operations marked **idempotent** in the API reference can be resent without this procedure. Everything else needs it.
7455
+
7456
+ ## Bulk changes
7457
+
7458
+ 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.
7459
+
7460
+ ## Keep the right evidence
7461
+
7462
+ 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
7463
  },
8290
7464
  {
8291
7465
  managedPath: 'integrations/rest/troubleshoot.md',
8292
7466
  unitRef: 'technical-documentation:unit/troubleshoot-api-request',
8293
- sourceRefs: ['saas-technical-doc:engine-content/troubleshoot-api-request'],
7467
+ sourceRefs: [
7468
+ 'saas-technical-doc:engine-content/troubleshoot-api-request',
7469
+ 'source:consumer-fact:access-api-keys',
7470
+ 'source:consumer-fact:rest-conventions',
7471
+ ],
8294
7472
  markdown: `# Troubleshoot an API request
8295
7473
 
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.
7474
+ 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
7475
 
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 |
7476
+ ## Read the status and the error body
8304
7477
 
8305
- ## Keep the original failure intact
7478
+ Every failed request answers with the same envelope. The two fields to read first are \`error.type\` and, when present, \`error.code\`:
8306
7479
 
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.
7480
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#errorEnvelopeMarkdown}}
8311
7481
 
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
- ~~~
7482
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#errorStatusMarkdown}}
8331
7483
 
8332
- ## Diagnose one boundary at a time
7484
+ ## No HTTP response at all
8333
7485
 
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 |
7486
+ 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
7487
 
8345
- ## Check the boundaries in order
7488
+ - Compare the base URL with the **Servers** list in the API reference; a URL copied from another environment is the usual cause.
7489
+ - Test from the network the integration runs on, not only from a laptop.
7490
+ - 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
7491
 
8347
- ### 1. Environment and transport
7492
+ ## 401: the credential was not accepted
8348
7493
 
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.
7494
+ - 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}}
7495
+ - 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.
7496
+ - Check the environment: a key minted in one environment does not work in another.
8353
7497
 
8354
- ### 2. Request contract
7498
+ Do not fix a 401 by borrowing an administrator's key. That hides the defect and widens what the integration can do.
8355
7499
 
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.
7500
+ ## 403: valid credential, refused operation
8360
7501
 
8361
- ### 3. Credential and identity
7502
+ The application knows who is calling and refuses this operation in this scope.
8362
7503
 
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.
7504
+ - Compare the roles on the key with the roles the operation lists in the API reference.
7505
+ - A collection under an organization the key is not a member of is refused with 403; confirm the organization identifier.
7506
+ - \`FEATURE_NOT_AVAILABLE\` and \`FEATURE_LIMIT_EXCEEDED\` are plan refusals, not role refusals: the organization's subscription does not include the capability, or has reached its limit for it. Changing a role will not clear either one.
8367
7507
 
8368
- ### 4. Authority and visibility
7508
+ ## 404: the record is not visible here
8369
7509
 
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.
7510
+ 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
7511
 
8376
- ### 5. Current state and retry safety
7512
+ ## 409 and 422: the current state refuses the change
8377
7513
 
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.
7514
+ 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
7515
 
8384
- ## Build a safe investigation record
7516
+ ## 429: too many requests
8385
7517
 
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.
7518
+ {{CONSUMER_FACT:source:consumer-fact:rest-conventions#rateLimitMarkdown}}
8390
7519
 
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.
7520
+ 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
7521
 
8395
- ## Prove the repair
7522
+ ## 5xx: the application failed
8396
7523
 
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.
7524
+ 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
7525
 
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.
7526
+ ## Prove the fix
8406
7527
 
8407
- ## Continue safely
7528
+ 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
7529
 
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.`,
7530
+ ## Ask for help with the right evidence
7531
+
7532
+ 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
7533
  },
8416
7534
  {
8417
7535
  managedPath: 'integrations/webhooks.md',
8418
7536
  unitRef: 'technical-documentation:unit/webhooks',
8419
- sourceRefs: ['saas-technical-doc:engine-content/webhooks'],
7537
+ sourceRefs: [
7538
+ 'saas-technical-doc:engine-content/webhooks',
7539
+ 'source:companion-projection:application-integration',
7540
+ 'source:consumer-fact:outbound-webhooks',
7541
+ ],
8420
7542
  markdown: `# Webhooks
8421
7543
 
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.
7544
+ 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
7545
 
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 |
7546
+ ## What the application sends
8430
7547
 
8431
- ## Decide whether an event should drive the work
7548
+ - **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).
7549
+ - **A signature in every request.** {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#signatureMarkdown}}
7550
+ - **Retries on your behalf.** {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#retryMarkdown}}
8432
7551
 
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.
7552
+ ## The events this application publishes
8459
7553
 
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
- ~~~
7554
+ {{APPLICATION_INTEGRATION:webhookEvents}}
8475
7555
 
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.
7556
+ ## What you configure
8479
7557
 
8480
- ## Split responsibilities deliberately
7558
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#endpointMarkdown}}
8481
7559
 
8482
- | Application sends | Receiver must do |
7560
+ 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.
7561
+
7562
+ ## What your receiver must do
7563
+
7564
+ | Step | Why it cannot be skipped |
8483
7565
  | --- | --- |
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.`,
7566
+ | Keep the raw request bytes | The signature covers the exact bytes; a JSON parser that reformats them breaks the check |
7567
+ | Verify the token and the body hash before reading the body | Anything before verification is an unauthenticated write into your system |
7568
+ | Claim \`jti\` atomically before doing work | Retries carry the same \`jti\`; two concurrent attempts must produce one effect |
7569
+ | Answer within the timeout, then do the work | A slow receiver is retried, then marked failed, while the work may have half-run |
7570
+ | Reconcile against the application for anything money- or access-related | A \`delivered\` row proves a 2xx, not that your worker finished |
7571
+
7572
+ ## The four pages of this section
7573
+
7574
+ 1. [Configure and test an endpoint](/integrations/webhooks/configure-and-test): register a receiver, run the built-in test, enable it.
7575
+ 2. [Verify a webhook delivery](/integrations/webhooks/verify-delivery): the signature, the claims and a working receiver in Node.js.
7576
+ 3. [Operate webhook deliveries](/integrations/webhooks/operate-deliveries): delivery statuses, the retry ladder, monitoring and manual retry.
7577
+ 4. [Troubleshoot webhook deliveries](/integrations/webhooks/troubleshoot): from a failed or missing delivery to the boundary that broke.`,
8509
7578
  },
8510
7579
  {
8511
7580
  managedPath: 'integrations/webhooks/configure-and-test.md',
8512
7581
  unitRef: 'technical-documentation:unit/configure-and-test-webhook-endpoint',
8513
- sourceRefs: ['saas-technical-doc:engine-content/configure-and-test-webhook-endpoint'],
7582
+ sourceRefs: [
7583
+ 'saas-technical-doc:engine-content/configure-and-test-webhook-endpoint',
7584
+ 'source:consumer-fact:outbound-webhooks',
7585
+ ],
8514
7586
  markdown: `# Configure and test a webhook endpoint
8515
7587
 
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
8530
-
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.
8536
-
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.
8541
-
8542
- ## Prepare the receiver
8543
-
8544
- Before adding the endpoint:
8545
-
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.
8553
-
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.
7588
+ 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.
8576
7589
 
8577
- ## Register the endpoint safely
7590
+ ## Before you register
8578
7591
 
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.
7592
+ The receiver must:
8590
7593
 
8591
- ## Test before enabling delivery
7594
+ - 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;
7595
+ - not carry credentials in the URL and not resolve to a private or loopback address; such URLs are refused with the \`not_sent\` outcome;
7596
+ - 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;
7597
+ - store the delivery identifier \`jti\` before doing any work, so a retry cannot repeat the work.
8592
7598
 
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.
7599
+ ## Register the endpoint
8596
7600
 
8597
- Interpret the test outcome precisely:
7601
+ 1. Open the webhook settings of the organization (or of the application, for application-wide events).
7602
+ 2. Copy \`applicationPublicKeyPem\` from the configuration into your receiver's secret store. This is the key that verifies every signature.
7603
+ 3. Add an endpoint with the final HTTPS URL and a description that names the receiving system and environment. Leave it **disabled**.
7604
+ 4. Save. The endpoint's \`id\` is the identifier every delivery of it will carry; note it.
8598
7605
 
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.
7606
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#endpointMarkdown}}
8608
7607
 
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.
7608
+ 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.
8613
7609
 
8614
- Run four checks before enablement:
7610
+ ## Run the endpoint test
8615
7611
 
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.
7612
+ 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:
8622
7613
 
8623
- ## Enable in stages
7614
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#testOutcomesMarkdown}}
8624
7615
 
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.
7616
+ 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.
8629
7617
 
8630
- For the first real event, confirm all of the following:
7618
+ Before enabling, make the receiver pass four cases:
8631
7619
 
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.
7620
+ - the test is \`accepted\`, and the receiver's own log shows verification ran before it answered;
7621
+ - a request with a missing or altered signature is refused before the body is parsed;
7622
+ - the same \`jti\` sent twice produces one effect;
7623
+ - a receiver that answers slowly produces \`unreachable\`, and both teams can find that attempt.
8637
7624
 
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.
7625
+ ## Enable and confirm the first real event
8641
7626
 
8642
- ## Retain safe operating evidence
7627
+ 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.
8643
7628
 
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.
7629
+ 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.
8649
7630
 
8650
- ## Continue safely
7631
+ ## Change an endpoint later
8651
7632
 
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.`,
7633
+ 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
7634
  },
8659
7635
  {
8660
7636
  managedPath: 'integrations/webhooks/verify-delivery.md',
8661
7637
  unitRef: 'technical-documentation:unit/verify-webhook-delivery',
8662
- sourceRefs: ['saas-technical-doc:engine-content/verify-webhook-delivery'],
7638
+ sourceRefs: [
7639
+ 'saas-technical-doc:engine-content/verify-webhook-delivery',
7640
+ 'source:consumer-fact:outbound-webhooks',
7641
+ ],
8663
7642
  markdown: `# Verify a webhook delivery
8664
7643
 
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
7644
+ 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
7645
 
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.
7646
+ ## The signature
8683
7647
 
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.
7648
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#signatureMarkdown}}
8687
7649
 
8688
- ## Verification sequence
7650
+ A decoded token payload looks like this:
8689
7651
 
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"]
7652
+ ~~~json
7653
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#claimsExampleJson}}
8714
7654
  ~~~
8715
7655
 
8716
- ## Know what each signed value proves
7656
+ The endpoint test adds \`"synthetic": true\` and sets \`operationIdentifier\` to \`testEndpoint\`; treat such a delivery as verified but never act on it.
8717
7657
 
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 |
7658
+ ## The verification sequence
7659
+
7660
+ 1. Read the signature header. No header, or more than one, is a refusal.
7661
+ 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.
7662
+ 3. Hash the raw request bytes with the algorithm in \`bodyHashAlg\` and compare the hex digest with \`bodyHash\` using a constant-time comparison.
7663
+ 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.
7664
+ 5. Parse the body and hand it to a queue. Answer the application before the work runs.
8726
7665
 
8727
- Validate all required claims as one policy. A correctly signed token for another
8728
- environment, audience or event handler is still unacceptable here.
7666
+ 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
7667
 
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.
7668
+ ## A receiver in Node.js
8733
7669
 
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.
7670
+ Express and the \`jsonwebtoken\` package, with the raw body preserved:
8738
7671
 
8739
- ## Reject without creating a second vulnerability
7672
+ ~~~javascript
7673
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#verifierSampleNode}}
7674
+ ~~~
8740
7675
 
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.
7676
+ \`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
7677
 
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.
7678
+ 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
7679
 
8750
- ## Handle key and deployment changes deliberately
7680
+ ## Route by claims, not by body
8751
7681
 
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.
7682
+ 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
7683
 
8759
- Test verification with controlled cases:
7684
+ ## Prove it before production
8760
7685
 
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.
7686
+ Run these five cases against the receiver and keep the results with the endpoint's \`id\`:
8766
7687
 
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.
7688
+ - a valid delivery reaches the queue and is answered 2xx within a second;
7689
+ - one changed byte in the body is refused;
7690
+ - a token with the wrong audience or an expired \`exp\` is refused;
7691
+ - the same \`jti\` delivered twice, concurrently, produces one queued job;
7692
+ - the endpoint test from the application is \`accepted\` and is not acted on.
8770
7693
 
8771
- ## Continue safely
7694
+ ## When keys or deployments change
8772
7695
 
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.`,
7696
+ 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
7697
  },
8780
7698
  {
8781
7699
  managedPath: 'integrations/webhooks/operate-deliveries.md',
8782
7700
  unitRef: 'technical-documentation:unit/operate-webhook-deliveries',
8783
- sourceRefs: ['saas-technical-doc:engine-content/operate-webhook-deliveries'],
7701
+ sourceRefs: [
7702
+ 'saas-technical-doc:engine-content/operate-webhook-deliveries',
7703
+ 'source:consumer-fact:outbound-webhooks',
7704
+ ],
8784
7705
  markdown: `# Operate webhook deliveries
8785
7706
 
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.
7707
+ 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
7708
 
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.
7709
+ ## Delivery statuses
8795
7710
 
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 |
8799
-
8800
- ## Understand the delivery states
8801
-
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
- ~~~
7711
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#deliveryStatesMarkdown}}
8814
7712
 
8815
- Read each state as an operating instruction:
7713
+ 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.
8816
7714
 
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 |
7715
+ ## The retry ladder
8823
7716
 
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.
7717
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#retryMarkdown}}
8828
7718
 
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.
7719
+ 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
7720
 
8834
7721
  ## Design the receiver for retries
8835
7722
 
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.
7723
+ - Claim \`jti\` atomically before any effect; the same \`jti\` returns on every retry of one delivery.
7724
+ - Answer 2xx only after the request is verified and durably queued, and answer well inside the timeout.
7725
+ - Do the work in a worker, idempotently, as a second line of defence.
7726
+ - Return a short, non-sensitive body; it is recorded on the delivery row and read by operators.
8841
7727
 
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.
7728
+ 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
7729
 
8847
- ## Monitor both sides of acceptance
7730
+ ## Monitor
8848
7731
 
8849
- For the application delivery lifecycle, monitor:
7732
+ On the application side, watch:
8850
7733
 
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;
7734
+ - \`pending\` rows whose \`nextAttemptAt\` is in the past for longer than a minute: the worker is not draining;
7735
+ - \`failed\` rows by endpoint and \`responseStatus\`: a burst of one status names one broken boundary;
7736
+ - \`attemptCount\` reaching 4 on many rows: the receiver is slow or flapping;
8855
7737
  - endpoint changes around the first failure time.
8856
7738
 
8857
- For the receiver lifecycle, monitor:
7739
+ 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.
8858
7740
 
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.
7741
+ ## Retry a failed delivery
8864
7742
 
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.
7743
+ The retry operation re-queues one \`failed\` row for a fresh attempt with the same \`jti\` and the same body. Before using it:
8868
7744
 
8869
- ## Retry deliberately
7745
+ 1. Read the row: \`httpUrl\`, \`attemptCount\`, \`responseStatus\`, \`failureReason\`.
7746
+ 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.
7747
+ 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.
7748
+ 4. Retry one row and watch it become \`delivered\`, then retry the rest.
8870
7749
 
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.
7750
+ If the receiver already did the work, do not retry to turn the row green; record the mismatch and reconcile through your own process.
8874
7751
 
8875
- Use this decision sequence:
7752
+ ## Endpoint changes and outages
8876
7753
 
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.
7754
+ 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.
8890
7755
 
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.
7756
+ ## Retention
8894
7757
 
8895
- ## Treat endpoint changes as new operating risk
8896
-
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.
8902
-
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.
8906
-
8907
- ## Retain evidence without retaining secrets
8908
-
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.
8914
-
8915
- ## Continue safely
8916
-
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.`,
7758
+ 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
7759
  },
8924
7760
  {
8925
7761
  managedPath: 'integrations/webhooks/troubleshoot.md',
8926
7762
  unitRef: 'technical-documentation:unit/troubleshoot-webhook-deliveries',
8927
- sourceRefs: ['saas-technical-doc:engine-content/troubleshoot-webhook-deliveries'],
7763
+ sourceRefs: [
7764
+ 'saas-technical-doc:engine-content/troubleshoot-webhook-deliveries',
7765
+ 'source:consumer-fact:outbound-webhooks',
7766
+ ],
8928
7767
  markdown: `# Troubleshoot webhook deliveries
8929
7768
 
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.
7769
+ 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
7770
 
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 |
7771
+ ## Find the row
8937
7772
 
8938
- ## Locate the first failed boundary
7773
+ 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
7774
 
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
- ~~~
7775
+ ## Diagnose by what the row shows
8956
7776
 
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.
7777
+ | Row shows | Where it broke | What to check |
7778
+ | --- | --- | --- |
7779
+ | 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 |
7780
+ | \`failed\`, \`failureReason\` mentions signing | The application could not sign | Application-side configuration; nothing on the receiver can help |
7781
+ | \`failed\`, no \`responseStatus\` | No HTTP answer within the timeout | Public reachability, DNS, TLS, and the receiver's own processing time |
7782
+ | \`responseStatus\` **3xx** | The URL is not the final receiver | Register the redirect target itself; redirects are never followed |
7783
+ | \`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 |
7784
+ | \`responseStatus\` **404** | Wrong path or wrong deployment | The exact route the receiver serves |
7785
+ | \`responseStatus\` **408**, **429** or **5xx** | The receiver is overloaded or failing | Capacity and errors on the receiver; the row keeps retrying on the ladder |
7786
+ | \`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 |
7787
+ | The effect happened twice | The receiver did not claim \`jti\` atomically | The uniqueness constraint on \`jti\` and the transaction around it |
8960
7788
 
8961
- ## Diagnose by symptom
7789
+ {{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#retryMarkdown}}
8962
7790
 
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.
7791
+ ## Signature failures after a change
9046
7792
 
9047
- ## Continue safely
7793
+ 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.
7794
+
7795
+ ## Duplicates
7796
+
7797
+ 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.
7798
+
7799
+ ## Prove the repair and retry
9048
7800
 
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.`,
7801
+ 1. Run the endpoint test and confirm it is \`accepted\` with verification logged on the receiver.
7802
+ 2. Search the receiver's store for the failed row's \`jti\` to know whether the work already ran.
7803
+ 3. Retry that one row from the delivery log and watch it become \`delivered\`.
7804
+ 4. Retry the remaining failed rows for the same endpoint.
7805
+
7806
+ ## Ask for help with the right evidence
7807
+
7808
+ 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
7809
  },
9056
7810
  {
9057
7811
  managedPath: 'integrations/mcp.md',
9058
7812
  unitRef: 'technical-documentation:unit/mcp-integrations',
9059
- sourceRefs: ['saas-technical-doc:engine-content/mcp-integrations'],
7813
+ sourceRefs: [
7814
+ 'saas-technical-doc:engine-content/mcp-integrations',
7815
+ 'source:companion-projection:application-connection',
7816
+ 'source:companion-projection:application-integration',
7817
+ ],
9060
7818
  markdown: `# Connect an MCP client
9061
7819
 
9062
7820
  Use **Model Context Protocol (MCP)** when an AI client should discover a set of
@@ -9068,6 +7826,16 @@ result, not a universal list of everything the application can do.
9068
7826
  | --- | --- |
9069
7827
  | 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
7828
 
7829
+ ## Connect to the endpoint
7830
+
7831
+ {{APPLICATION_CONNECTION:mcpEndpoint}}
7832
+
7833
+ 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.
7834
+
7835
+ ## The tools this application publishes
7836
+
7837
+ {{APPLICATION_INTEGRATION:mcpTools}}
7838
+
9071
7839
  ## Decide who authorizes the client
9072
7840
 
9073
7841
  Use a dedicated machine identity when a service owns the work. Use delegated
@@ -9143,7 +7911,7 @@ catalogue request. In normal operation, let the MCP client or SDK create these
9143
7911
  headers and keep the credential in its protected store.
9144
7912
 
9145
7913
  ~~~http
9146
- POST <MCP_SERVER_URL> HTTP/1.1
7914
+ POST {{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}} HTTP/1.1
9147
7915
  Authorization: Bearer <ACCESS_TOKEN_FOR_THIS_MCP_SERVER>
9148
7916
  Content-Type: application/json
9149
7917
  Accept: application/json, text/event-stream
@@ -9310,7 +8078,10 @@ operations the current identity can actually invoke.
9310
8078
  {
9311
8079
  managedPath: 'integrations/a2a.md',
9312
8080
  unitRef: 'technical-documentation:unit/a2a-integrations',
9313
- sourceRefs: ['saas-technical-doc:engine-content/a2a-integrations'],
8081
+ sourceRefs: [
8082
+ 'saas-technical-doc:engine-content/a2a-integrations',
8083
+ 'source:companion-projection:application-connection',
8084
+ ],
9314
8085
  markdown: `# Connect an A2A agent
9315
8086
 
9316
8087
  Use **Agent-to-Agent (A2A)** when another agent should exchange messages with
@@ -9325,6 +8096,8 @@ MCP catalogue.
9325
8096
 
9326
8097
  ## Read the agent card before sending work
9327
8098
 
8099
+ {{APPLICATION_CONNECTION:a2aAgentCard}}
8100
+
9328
8101
  The public card describes the selected agent and the skills this application
9329
8102
  publishes for it. Confirm the card belongs to the intended environment and named
9330
8103
  agent instance. Treat it as discovery metadata: task and message operations
@@ -9379,8 +8152,8 @@ similar to this redacted example:
9379
8152
  {
9380
8153
  "protocolVersion": "0.2.5",
9381
8154
  "supportedInterfaces": [
9382
- { "url": "<APPLICATION_AGENT_URL>", "transport": "JSONRPC", "version": "1.0" },
9383
- { "url": "<APPLICATION_AGENT_URL>", "transport": "JSONRPC", "version": "0.2.5" }
8155
+ { "url": "{{APPLICATION_CONNECTION_VALUE:agentCardUrl}}", "transport": "JSONRPC", "version": "1.0" },
8156
+ { "url": "{{APPLICATION_CONNECTION_VALUE:agentCardUrl}}", "transport": "JSONRPC", "version": "0.2.5" }
9384
8157
  ],
9385
8158
  "name": "<APPLICATION_AGENT_NAME>",
9386
8159
  "capabilities": {