@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.
- package/dist/esm/companion/application-documentation/application-administration-documentation.d.ts +52 -0
- package/dist/esm/companion/application-documentation/application-administration-documentation.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/application-administration-documentation.js +58 -0
- package/dist/esm/companion/application-documentation/application-administration-documentation.js.map +1 -0
- package/dist/esm/companion/application-documentation/application-authentication-documentation.d.ts +76 -0
- package/dist/esm/companion/application-documentation/application-authentication-documentation.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/application-authentication-documentation.js +116 -0
- package/dist/esm/companion/application-documentation/application-authentication-documentation.js.map +1 -0
- package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts +111 -0
- package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/application-connection-documentation.js +165 -0
- package/dist/esm/companion/application-documentation/application-connection-documentation.js.map +1 -0
- package/dist/esm/companion/application-documentation/application-domain-documentation.d.ts +133 -0
- package/dist/esm/companion/application-documentation/application-domain-documentation.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/application-domain-documentation.js +243 -0
- package/dist/esm/companion/application-documentation/application-domain-documentation.js.map +1 -0
- package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts +48 -0
- package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/application-integration-documentation.js +86 -0
- package/dist/esm/companion/application-documentation/application-integration-documentation.js.map +1 -0
- package/dist/esm/companion/application-documentation/application-organization-role-documentation.d.ts +30 -0
- package/dist/esm/companion/application-documentation/application-organization-role-documentation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/application-organization-role-documentation.js +38 -0
- package/dist/esm/companion/application-documentation/application-organization-role-documentation.js.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts +82 -4
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js +412 -219
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts +10 -0
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js +9 -1
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-publication-policy.js +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-publication-policy.js.map +1 -1
- package/dist/esm/companion/index.d.ts +6 -1
- package/dist/esm/companion/index.d.ts.map +1 -1
- package/dist/esm/companion/index.js +6 -1
- package/dist/esm/companion/index.js.map +1 -1
- package/dist/esm/companion/manual-controller-route-projection.d.ts +112 -0
- package/dist/esm/companion/manual-controller-route-projection.d.ts.map +1 -0
- package/dist/esm/companion/manual-controller-route-projection.js +249 -0
- package/dist/esm/companion/manual-controller-route-projection.js.map +1 -0
- package/dist/esm/companion/openapi-generator.d.ts +16 -0
- package/dist/esm/companion/openapi-generator.d.ts.map +1 -1
- package/dist/esm/companion/openapi-generator.js +497 -32
- package/dist/esm/companion/openapi-generator.js.map +1 -1
- package/dist/esm/companion/operation-projection.schemas.d.ts +44 -0
- package/dist/esm/companion/operation-projection.schemas.d.ts.map +1 -1
- package/dist/esm/companion/operation-projection.schemas.js +37 -0
- package/dist/esm/companion/operation-projection.schemas.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.d.ts +7 -1
- package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js +44 -20
- package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.js +8 -1
- package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-markdown-links.d.ts +28 -0
- package/dist/esm/companion/rendering/technical-documentation-markdown-links.d.ts.map +1 -0
- package/dist/esm/companion/rendering/technical-documentation-markdown-links.js +52 -0
- package/dist/esm/companion/rendering/technical-documentation-markdown-links.js.map +1 -0
- package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.d.ts +0 -9
- package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.d.ts.map +0 -1
- package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.js +0 -106
- package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.js.map +0 -1
- package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts +1 -0
- package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-render-model.js +112 -88
- package/dist/esm/companion/rendering/technical-documentation-render-model.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.d.ts +10 -0
- package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.js +10 -15
- package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.js.map +1 -1
- package/dist/esm/companion/technical-documentation-capture-execution-port.d.ts +27 -1
- package/dist/esm/companion/technical-documentation-capture-execution-port.d.ts.map +1 -1
- package/dist/esm/companion/technical-documentation-capture-execution-port.js.map +1 -1
- package/dist/esm/companion/technical-documentation-diagram-definitions.d.ts +33 -0
- package/dist/esm/companion/technical-documentation-diagram-definitions.d.ts.map +1 -0
- package/dist/esm/companion/technical-documentation-diagram-definitions.js +54 -0
- package/dist/esm/companion/technical-documentation-diagram-definitions.js.map +1 -0
- package/dist/esm/companion/technical-documentation-diagram-materializer.d.ts +10 -18
- package/dist/esm/companion/technical-documentation-diagram-materializer.d.ts.map +1 -1
- package/dist/esm/companion/technical-documentation-diagram-materializer.js +9 -39
- package/dist/esm/companion/technical-documentation-diagram-materializer.js.map +1 -1
- package/dist/esm/config/wildo-tech-doc-config.schemas.d.ts +8 -4
- package/dist/esm/config/wildo-tech-doc-config.schemas.d.ts.map +1 -1
- package/dist/esm/config/wildo-tech-doc-config.schemas.js +8 -4
- package/dist/esm/config/wildo-tech-doc-config.schemas.js.map +1 -1
- package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts +105 -122
- package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts.map +1 -1
- package/dist/esm/content/application-consumer-documentation-content.techdoc.js +934 -2161
- package/dist/esm/content/application-consumer-documentation-content.techdoc.js.map +1 -1
- package/dist/esm/runtime/DocsAuthContext.d.ts +16 -1
- package/dist/esm/runtime/DocsAuthContext.d.ts.map +1 -1
- package/dist/esm/runtime/DocsAuthContext.js +18 -2
- package/dist/esm/runtime/DocsAuthContext.js.map +1 -1
- package/dist/esm/runtime/decode-jwt-claims.d.ts +7 -4
- package/dist/esm/runtime/decode-jwt-claims.d.ts.map +1 -1
- package/dist/esm/runtime/decode-jwt-claims.js +7 -4
- package/dist/esm/runtime/decode-jwt-claims.js.map +1 -1
- package/dist/esm/runtime/docs-auth-session.schemas.d.ts +16 -5
- package/dist/esm/runtime/docs-auth-session.schemas.d.ts.map +1 -1
- package/dist/esm/runtime/docs-auth-session.schemas.js +16 -5
- package/dist/esm/runtime/docs-auth-session.schemas.js.map +1 -1
- package/dist/esm/runtime/frontend-provider-registry.techdoc.d.ts +35 -13
- package/dist/esm/runtime/frontend-provider-registry.techdoc.d.ts.map +1 -1
- package/dist/esm/runtime/frontend-provider-registry.techdoc.js +28 -19
- package/dist/esm/runtime/frontend-provider-registry.techdoc.js.map +1 -1
- package/dist/esm/runtime/index.d.ts +1 -0
- package/dist/esm/runtime/index.d.ts.map +1 -1
- package/dist/esm/runtime/index.js +1 -0
- package/dist/esm/runtime/index.js.map +1 -1
- package/dist/esm/runtime/use-docs-auth-session.d.ts +9 -7
- package/dist/esm/runtime/use-docs-auth-session.d.ts.map +1 -1
- package/dist/esm/runtime/use-docs-auth-session.js +9 -7
- package/dist/esm/runtime/use-docs-auth-session.js.map +1 -1
- package/dist/esm/runtime/use-docs-provider-sdks.d.ts +21 -0
- package/dist/esm/runtime/use-docs-provider-sdks.d.ts.map +1 -0
- package/dist/esm/runtime/use-docs-provider-sdks.js +49 -0
- package/dist/esm/runtime/use-docs-provider-sdks.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +6 -5
- 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.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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: [
|
|
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
|
|
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
|
-
|
|
|
41
|
+
| You need to | Start here | You will find |
|
|
29
42
|
| --- | --- | --- |
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
the MCP connection required by the Cursor, Codex and Claude Code guides. If a
|
|
40
|
-
section is absent, do not assume that its capability or administrative surface
|
|
41
|
-
exists in this application.
|
|
42
|
-
|
|
43
|
-
~~~mermaid
|
|
44
|
-
flowchart TD
|
|
45
|
-
accTitle: Choose a documentation path from the outcome you need
|
|
46
|
-
accDescr: A reader starts from a human access task, an administration task, an integration task, or an operational investigation. Each path leads to a guide, then to an exact reference only when implementation details are needed.
|
|
47
|
-
need["What outcome do you need?"] --> person{"A person's access?"}
|
|
48
|
-
person -->|"Sign in or recover"| identity["Access and identity"]
|
|
49
|
-
person -->|"Invite, change or remove"| administration["User administration"]
|
|
50
|
-
person -->|"No"| system{"Connect or operate software?"}
|
|
51
|
-
system -->|"Connect or automate"| integrations["Integrations"]
|
|
52
|
-
system -->|"Investigate or export evidence"| security["Security and audit"]
|
|
53
|
-
identity --> contract["Use the exact reference when needed"]
|
|
54
|
-
administration --> contract
|
|
55
|
-
integrations --> contract
|
|
56
|
-
security --> contract
|
|
57
|
-
~~~
|
|
58
|
-
|
|
59
|
-
The diagram is a starting map, not an access model. Opening a guide does not
|
|
60
|
-
grant a capability. The current application's navigation, visible controls and
|
|
61
|
-
published operation contracts determine what is actually available to you.
|
|
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
|
-
|
|
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
|
-
|
|
55
|
+
## Three things to know before you change anything
|
|
66
56
|
|
|
67
|
-
1. **
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
##
|
|
61
|
+
## How the documentation is organised
|
|
79
62
|
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
|
|
94
|
-
|
|
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
|
-
|
|
101
|
-
|
|
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
|
-
|
|
89
|
+
## What it manages
|
|
104
90
|
|
|
105
|
-
|
|
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
|
-
##
|
|
93
|
+
## Each one in detail
|
|
113
94
|
|
|
114
|
-
|
|
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
|
-
##
|
|
97
|
+
## Where to go next
|
|
123
98
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
159
|
+
## What {{APPLICATION_NAME}} accepts to sign in
|
|
177
160
|
|
|
178
|
-
{{
|
|
161
|
+
{{APPLICATION_AUTHENTICATION:enabledSignInMethods}}
|
|
179
162
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
|
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: [
|
|
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
|
|
525
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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\`
|
|
3716
|
-
\`
|
|
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": "
|
|
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: [
|
|
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: [
|
|
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: '
|
|
5529
|
-
unitRef: 'technical-documentation:unit/
|
|
5530
|
-
sourceRefs: [
|
|
5531
|
-
|
|
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
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
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
|
-
|
|
|
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
|
-
##
|
|
5670
|
+
## Which integration should you choose?
|
|
5552
5671
|
|
|
5553
|
-
|
|
|
5672
|
+
| You need to… | Use | Why |
|
|
5554
5673
|
| --- | --- | --- |
|
|
5555
|
-
|
|
|
5556
|
-
|
|
|
5557
|
-
|
|
|
5558
|
-
|
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
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
|
|
5569
|
-
accTitle:
|
|
5570
|
-
accDescr:
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
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
|
|
5580
|
-
|
|
5581
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
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
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
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
|
-
##
|
|
5714
|
+
## Before you connect anything
|
|
5627
5715
|
|
|
5628
|
-
|
|
5629
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
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
|
-
|
|
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
|
|
5649
|
-
|
|
5650
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5752
|
+
## Start from the business event
|
|
5655
5753
|
|
|
5656
|
-
|
|
5657
|
-
|
|
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
|
-
|
|
|
5759
|
+
| The work starts when… | Begin with | Why |
|
|
5660
5760
|
| --- | --- | --- |
|
|
5661
|
-
|
|
|
5662
|
-
|
|
|
5663
|
-
|
|
|
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
|
|
5708
|
-
accTitle:
|
|
5709
|
-
accDescr:
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
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: '
|
|
5765
|
-
unitRef: 'technical-documentation:unit/
|
|
5799
|
+
managedPath: 'integrations/automation/n8n.md',
|
|
5800
|
+
unitRef: 'technical-documentation:unit/connect-n8n',
|
|
5766
5801
|
sourceRefs: [
|
|
5767
|
-
'saas-technical-doc:engine-content/
|
|
5768
|
-
'source:
|
|
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: `#
|
|
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
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
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
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5802
|
-
|
|
5803
|
-
the
|
|
5804
|
-
|
|
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
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5845
|
+
### 2. Add the application request
|
|
5834
5846
|
|
|
5835
|
-
|
|
5836
|
-
|
|
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** |
|
|
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 |
|
|
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 |
|
|
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: [
|
|
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
|
-
|
|
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: [
|
|
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": "
|
|
6423
|
+
"url": "{{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}"
|
|
7186
6424
|
}
|
|
7187
6425
|
}
|
|
7188
6426
|
}
|
|
7189
6427
|
~~~
|
|
7190
6428
|
|
|
7191
|
-
|
|
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: [
|
|
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 = "
|
|
6561
|
+
url = "{{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}"
|
|
7322
6562
|
default_tools_approval_mode = "prompt"
|
|
7323
6563
|
~~~
|
|
7324
6564
|
|
|
7325
|
-
|
|
7326
|
-
|
|
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: [
|
|
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
|
|
6698
|
+
claude mcp add --transport http application {{APPLICATION_CONNECTION_VALUE:mcpEndpointUrl}}
|
|
7456
6699
|
~~~
|
|
7457
6700
|
|
|
7458
|
-
|
|
7459
|
-
|
|
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
|
-
|
|
7768
|
-
path from the
|
|
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 "<
|
|
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: [
|
|
7107
|
+
sourceRefs: [
|
|
7108
|
+
'saas-technical-doc:engine-content/common-rest-api',
|
|
7109
|
+
],
|
|
7864
7110
|
markdown: `# REST API
|
|
7865
7111
|
|
|
7866
|
-
|
|
7867
|
-
and needs an immediate response. This section explains the decisions shared by
|
|
7868
|
-
REST integrations. The generated API reference remains authoritative for exact
|
|
7869
|
-
paths, fields, roles, statuses and resource-specific limits.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7120
|
+
## Choose the right tool first
|
|
7121
|
+
|
|
7122
|
+
| You need to | Use | Why |
|
|
7878
7123
|
| --- | --- | --- |
|
|
7879
|
-
| Read
|
|
7880
|
-
|
|
|
7881
|
-
|
|
|
7882
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
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
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 "
|
|
7989
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
7293
|
+
## Step 3: prove the boundary holds
|
|
8000
7294
|
|
|
8001
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: [
|
|
8034
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
7335
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#collectionQueryMarkdown}}
|
|
8047
7336
|
|
|
8048
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
~~~
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
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
|
-
|
|
7361
|
+
Three rules keep a scan correct:
|
|
8082
7362
|
|
|
8083
|
-
1.
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
that the result belongs to the intended environment and organization.
|
|
8087
|
-
3. Process each record **idempotently**. Reprocessing the same stable record
|
|
8088
|
-
identifier must not create a second payment, message, account or other
|
|
8089
|
-
business effect.
|
|
8090
|
-
4. Persist a checkpoint only after the page's work is safely recorded. Keep the
|
|
8091
|
-
page number, filter and sort basis, stable identifiers or counts, and the
|
|
8092
|
-
returned pagination metadata.
|
|
8093
|
-
5. Advance only while the response says another page exists. Do not stop merely
|
|
8094
|
-
because a page contains fewer items than requested.
|
|
8095
|
-
|
|
8096
|
-
An empty first page can be a correct result. Prove it by confirming the caller,
|
|
8097
|
-
organization, filters and expected visibility rather than treating emptiness as
|
|
8098
|
-
a transport failure.
|
|
8099
|
-
|
|
8100
|
-
## Read the pagination metadata literally
|
|
8101
|
-
|
|
8102
|
-
The standard collection envelope contains the returned data plus **page**,
|
|
8103
|
-
**limit**, **total** and **totalPages**. These values describe the server's
|
|
8104
|
-
answer to that request:
|
|
8105
|
-
|
|
8106
|
-
- **page** identifies the page just returned;
|
|
8107
|
-
- **limit** is the page size applied by that operation;
|
|
8108
|
-
- **total** is the matching record count reported for the query;
|
|
8109
|
-
- **totalPages** tells the caller whether another numbered page remains.
|
|
8110
|
-
|
|
8111
|
-
Do not manufacture a next page from the number of items alone. If the operation
|
|
8112
|
-
publishes a different paging model, follow that operation instead of translating
|
|
8113
|
-
it into this one.
|
|
8114
|
-
|
|
8115
|
-
## Decide how changes during a scan are handled
|
|
8116
|
-
|
|
8117
|
-
Records may be created or changed while a multi-page scan runs. If the exact
|
|
8118
|
-
operation does not publish snapshot or cursor semantics, do not claim a
|
|
8119
|
-
point-in-time view. Make downstream processing idempotent, retain stable record
|
|
8120
|
-
identifiers and schedule a reconciliation pass appropriate to the business risk.
|
|
8121
|
-
|
|
8122
|
-
Choose the recovery posture before production:
|
|
8123
|
-
|
|
8124
|
-
- For a low-risk report, a later complete pass may be sufficient.
|
|
8125
|
-
- For a state synchronization, persist stable identifiers and compare the final
|
|
8126
|
-
downstream state with a fresh authoritative read.
|
|
8127
|
-
- For financial, access or other sensitive effects, use a documented stable
|
|
8128
|
-
ordering or change marker when available and explicitly investigate gaps.
|
|
8129
|
-
- If the scan fails halfway through, resume from the last **completed**
|
|
8130
|
-
checkpoint. Reprocessing the boundary page is safer than skipping uncertain
|
|
8131
|
-
work when processing is idempotent.
|
|
8132
|
-
|
|
8133
|
-
Never assume that page 2 still starts after the same record if concurrent
|
|
8134
|
-
changes can reorder the collection. The operation contract determines whether a
|
|
8135
|
-
stable scan is possible; the client cannot create that guarantee by remembering
|
|
8136
|
-
only a page number.
|
|
8137
|
-
|
|
8138
|
-
## Verify completion
|
|
8139
|
-
|
|
8140
|
-
Compare the processed count and checkpoints with the final returned metadata,
|
|
8141
|
-
then sample a small number of recognizable records in the intended organization.
|
|
8142
|
-
Run one controlled repeat and confirm it produces no duplicate downstream
|
|
8143
|
-
effect. If records can change during the pass, run the planned reconciliation
|
|
8144
|
-
and explain any difference rather than forcing the counts to appear equal.
|
|
8145
|
-
|
|
8146
|
-
Retain the operation identity, environment, organization, filter and sort basis,
|
|
8147
|
-
start/end time, pages processed, final pagination metadata and non-sensitive
|
|
8148
|
-
checkpoint. Do not log full result sets merely to prove the scan ran; store the
|
|
8149
|
-
minimum identifiers and counts needed to investigate omissions or duplicates.
|
|
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
|
-
##
|
|
7367
|
+
## Search and filter
|
|
8152
7368
|
|
|
8153
|
-
|
|
8154
|
-
turning collection results into mutations.
|
|
8155
|
-
- [Troubleshoot an API request](/integrations/rest/troubleshoot) when one page
|
|
8156
|
-
fails or visibility differs from the expected organization scope.
|
|
8157
|
-
- Return to [REST API](/integrations/rest-api) to review the request, identity,
|
|
8158
|
-
organization and operation boundaries.`,
|
|
8159
|
-
},
|
|
8160
|
-
{
|
|
8161
|
-
managedPath: 'integrations/rest/write-and-reconcile.md',
|
|
8162
|
-
unitRef: 'technical-documentation:unit/write-and-reconcile-api-changes',
|
|
8163
|
-
sourceRefs: ['saas-technical-doc:engine-content/write-and-reconcile-api-changes'],
|
|
8164
|
-
markdown: `# Write and reconcile API changes
|
|
7369
|
+
Search operations (\`…/search\`) add \`q\` for free text. Combine it with filters and sorting:
|
|
8165
7370
|
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7380
|
+
~~~text
|
|
7381
|
+
?createdAt[startDate]=2026-09-01T00:00:00Z&createdAt[endDate]=2026-09-30T23:59:59Z
|
|
7382
|
+
~~~
|
|
8175
7383
|
|
|
8176
|
-
|
|
8177
|
-
identifier lets you recognize the same intended change later. Use a documented
|
|
8178
|
-
idempotency contract when the operation publishes one. Otherwise, prevent
|
|
8179
|
-
concurrent duplicate work in your own system and reconcile from authoritative
|
|
8180
|
-
state after uncertainty.
|
|
7384
|
+
Send instants in ISO 8601 with a timezone. An unparseable bound answers **400** with the field named in \`error.validationErrors\`.
|
|
8181
7385
|
|
|
8182
|
-
|
|
7386
|
+
## Counts and summaries
|
|
8183
7387
|
|
|
8184
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
Ambiguous --> SafeToRetry: authoritative state shows not applied
|
|
8218
|
-
SafeToRetry --> Sent
|
|
8219
|
-
Confirmed --> [*]
|
|
8220
|
-
Refused --> [*]
|
|
8221
|
-
~~~
|
|
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
|
-
|
|
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
|
-
##
|
|
7409
|
+
## Send the change
|
|
8228
7410
|
|
|
8229
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
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
|
-
|
|
7428
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#concurrencyMarkdown}}
|
|
8246
7429
|
|
|
8247
|
-
-
|
|
8248
|
-
- **401 or 403:** repair identity or authority; more retries cannot grant access.
|
|
8249
|
-
- **404:** check identifier and visible organization scope before assuming absence.
|
|
8250
|
-
- **409:** read current state and resolve the business conflict.
|
|
8251
|
-
- **429 or retryable server failure:** follow the documented delay and use
|
|
8252
|
-
bounded backoff.
|
|
8253
|
-
- **Timeout or connection loss:** read authoritative state before deciding
|
|
8254
|
-
whether another write is safe.
|
|
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
|
-
|
|
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
|
-
|
|
7434
|
+
A write the application will not perform answers with the standard error envelope. Three cases matter for a writer:
|
|
8262
7435
|
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
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: [
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
7484
|
+
## No HTTP response at all
|
|
8333
7485
|
|
|
8334
|
-
|
|
8335
|
-
| --- | --- | --- |
|
|
8336
|
-
| No HTTP response | Network, DNS, TLS or server availability | Target environment, resolved host, TLS result and timeout |
|
|
8337
|
-
| **400** or validation error | Request does not match the operation contract | Content type, required fields, enum values and parameter placement |
|
|
8338
|
-
| **401** | Credential was not accepted | Header transport, expiry, revocation, credential type and environment |
|
|
8339
|
-
| **403** | Identity lacks authority for the operation | Active organization, membership, role and operation requirements |
|
|
8340
|
-
| **404** | Resource is absent or invisible in this scope | Identifier, organization and resource visibility |
|
|
8341
|
-
| **409** | Current state conflicts with the requested transition | Read authoritative state and resolve the business conflict |
|
|
8342
|
-
| **429** | Caller exceeded an enforced limit | Stop immediate retries and follow the documented delay |
|
|
8343
|
-
| **5xx** | Server could not complete the request | Preserve correlation evidence; retry only when the operation is safe |
|
|
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
|
-
|
|
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
|
-
|
|
7492
|
+
## 401: the credential was not accepted
|
|
8348
7493
|
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7502
|
+
The application knows who is calling and refuses this operation in this scope.
|
|
8362
7503
|
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
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
|
-
|
|
7508
|
+
## 404: the record is not visible here
|
|
8369
7509
|
|
|
8370
|
-
|
|
8371
|
-
scope. Confirm active organization membership, role and operation authority.
|
|
8372
|
-
For **404**, confirm the identifier and organization visibility before deciding
|
|
8373
|
-
the record does not exist; an inaccessible record can intentionally be
|
|
8374
|
-
indistinguishable from a missing one.
|
|
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
|
-
|
|
7512
|
+
## 409 and 422: the current state refuses the change
|
|
8377
7513
|
|
|
8378
|
-
|
|
8379
|
-
honour the documented delay and stop parallel callers from immediately filling
|
|
8380
|
-
the same limit again. For **5xx** or transport loss, determine whether the
|
|
8381
|
-
operation is safe to repeat. A read can usually be repeated; a write first needs
|
|
8382
|
-
authoritative reconciliation.
|
|
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
|
-
##
|
|
7516
|
+
## 429: too many requests
|
|
8385
7517
|
|
|
8386
|
-
|
|
8387
|
-
organization, status, stable error code and correlation identifier. Replace
|
|
8388
|
-
payload values with a minimal redacted reproduction. Never paste an API key,
|
|
8389
|
-
Bearer token or unrestricted personal data into a ticket.
|
|
7518
|
+
{{CONSUMER_FACT:source:consumer-fact:rest-conventions#rateLimitMarkdown}}
|
|
8390
7519
|
|
|
8391
|
-
|
|
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
|
-
##
|
|
7522
|
+
## 5xx: the application failed
|
|
8396
7523
|
|
|
8397
|
-
|
|
8398
|
-
success and one expected refusal, then verify the business result in the
|
|
8399
|
-
intended organization. If the correction involved a credential or permission,
|
|
8400
|
-
also confirm that access outside the approved scope remains refused.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
retrying a mutation with an uncertain outcome.
|
|
8413
|
-
- Return to [REST API](/integrations/rest-api) to review the four shared
|
|
8414
|
-
integration boundaries.`,
|
|
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: [
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7558
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#endpointMarkdown}}
|
|
8481
7559
|
|
|
8482
|
-
|
|
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
|
-
|
|
|
8485
|
-
|
|
|
8486
|
-
|
|
|
8487
|
-
|
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
- [Configure and test an endpoint](/integrations/webhooks/configure-and-test)
|
|
8497
|
-
before enabling production delivery.
|
|
8498
|
-
- [Verify a webhook delivery](/integrations/webhooks/verify-delivery) using the
|
|
8499
|
-
raw bytes, public key and signed claims.
|
|
8500
|
-
- [Operate webhook deliveries](/integrations/webhooks/operate-deliveries) with
|
|
8501
|
-
fast acknowledgement, durable work and reconciliation.
|
|
8502
|
-
- [Troubleshoot webhook deliveries](/integrations/webhooks/troubleshoot) from
|
|
8503
|
-
delivery state and response meaning.
|
|
8504
|
-
|
|
8505
|
-
The event and API references remain authoritative for the exact events,
|
|
8506
|
-
payloads and configuration operations exposed by this application. Before
|
|
8507
|
-
production, prove a valid delivery, an invalid signature, a duplicate delivery,
|
|
8508
|
-
a receiver timeout and recovery from a failed downstream effect.`,
|
|
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: [
|
|
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
|
-
|
|
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
|
-
##
|
|
7590
|
+
## Before you register
|
|
8578
7591
|
|
|
8579
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
7610
|
+
## Run the endpoint test
|
|
8615
7611
|
|
|
8616
|
-
|
|
8617
|
-
- a request with a missing or invalid signature is rejected before parsing or
|
|
8618
|
-
business processing;
|
|
8619
|
-
- a repeated signed delivery identity does not enqueue a second effect;
|
|
8620
|
-
- a receiver delay or outage produces a non-success outcome that operators can
|
|
8621
|
-
locate in both systems.
|
|
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
|
-
|
|
7614
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#testOutcomesMarkdown}}
|
|
8624
7615
|
|
|
8625
|
-
|
|
8626
|
-
and verify the complete receiver path. Only then enable broader traffic. Retain
|
|
8627
|
-
the endpoint identity, test time and outcome without retaining the signature or
|
|
8628
|
-
synthetic payload unnecessarily.
|
|
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
|
-
|
|
7618
|
+
Before enabling, make the receiver pass four cases:
|
|
8631
7619
|
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
7631
|
+
## Change an endpoint later
|
|
8651
7632
|
|
|
8652
|
-
|
|
8653
|
-
complete receiver-side trust boundary.
|
|
8654
|
-
- [Operate webhook deliveries](/integrations/webhooks/operate-deliveries) before
|
|
8655
|
-
enabling production volume or automatic recovery.
|
|
8656
|
-
- [Troubleshoot webhook deliveries](/integrations/webhooks/troubleshoot) when a
|
|
8657
|
-
test or controlled event does not complete.`,
|
|
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: [
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7650
|
+
A decoded token payload looks like this:
|
|
8689
7651
|
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
3. Pin **ES256** and validate the expected issuer, audience, key identifier,
|
|
8693
|
-
issued time and expiry. Allow only a small, deliberate clock tolerance.
|
|
8694
|
-
4. Compute SHA-256 over the **raw request bytes** and compare it with the signed
|
|
8695
|
-
body hash using a constant-time comparison.
|
|
8696
|
-
5. Validate the signed event channel, resource and operation claims against the
|
|
8697
|
-
receiver route you expected.
|
|
8698
|
-
6. Atomically claim the signed **jti** before applying a business effect.
|
|
8699
|
-
7. Parse and validate the JSON payload against the published event contract.
|
|
8700
|
-
|
|
8701
|
-
~~~mermaid
|
|
8702
|
-
flowchart TD
|
|
8703
|
-
accTitle: Reject a webhook until identity, integrity and replay checks pass
|
|
8704
|
-
accDescr: The receiver validates the signature token, recomputes the raw-body hash, checks expected event claims, and atomically claims the jti before parsing and processing the payload.
|
|
8705
|
-
request["Incoming request"] --> token{"Token valid and expected?"}
|
|
8706
|
-
token -->|No| reject["Reject without processing"]
|
|
8707
|
-
token -->|Yes| hash{"Raw-body SHA-256 matches?"}
|
|
8708
|
-
hash -->|No| reject
|
|
8709
|
-
hash -->|Yes| claims{"Event claims expected?"}
|
|
8710
|
-
claims -->|No| reject
|
|
8711
|
-
claims -->|Yes| dedup{"jti claimed atomically?"}
|
|
8712
|
-
dedup -->|Already seen| acknowledge["Return the established duplicate outcome"]
|
|
8713
|
-
dedup -->|New| parse["Parse, validate and queue work"]
|
|
7652
|
+
~~~json
|
|
7653
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#claimsExampleJson}}
|
|
8714
7654
|
~~~
|
|
8715
7655
|
|
|
8716
|
-
|
|
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
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7672
|
+
~~~javascript
|
|
7673
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#verifierSampleNode}}
|
|
7674
|
+
~~~
|
|
8740
7675
|
|
|
8741
|
-
|
|
8742
|
-
explain which cryptographic comparison failed to an unauthenticated caller, and
|
|
8743
|
-
do not log the complete token or sensitive body. Retain the time, destination
|
|
8744
|
-
route, reason category and non-secret correlation evidence needed for support.
|
|
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
|
-
|
|
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
|
-
##
|
|
7680
|
+
## Route by claims, not by body
|
|
8751
7681
|
|
|
8752
|
-
|
|
8753
|
-
token's key identifier, issuer and audience with the verification key loaded
|
|
8754
|
-
from the same application environment. Refresh through the documented key
|
|
8755
|
-
surface; never fetch a key from an untrusted location supplied by the incoming
|
|
8756
|
-
request. Keep the previous accepted key only when the application's published
|
|
8757
|
-
rotation contract explicitly requires an overlap.
|
|
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
|
-
|
|
7684
|
+
## Prove it before production
|
|
8760
7685
|
|
|
8761
|
-
|
|
8762
|
-
- one changed raw byte is rejected;
|
|
8763
|
-
- one expired or wrong-audience token is rejected;
|
|
8764
|
-
- one unexpected event claim is rejected by the route;
|
|
8765
|
-
- the same **jti** received concurrently produces one accepted business effect.
|
|
7686
|
+
Run these five cases against the receiver and keep the results with the endpoint's \`id\`:
|
|
8766
7687
|
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
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
|
-
##
|
|
7694
|
+
## When keys or deployments change
|
|
8772
7695
|
|
|
8773
|
-
|
|
8774
|
-
before enabling a receiver that has not passed the negative cases.
|
|
8775
|
-
- [Operate webhook deliveries](/integrations/webhooks/operate-deliveries) to
|
|
8776
|
-
separate verified acceptance from longer business work.
|
|
8777
|
-
- [Troubleshoot webhook deliveries](/integrations/webhooks/troubleshoot) when
|
|
8778
|
-
the application and receiver disagree about the result.`,
|
|
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: [
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
8837
|
-
-
|
|
8838
|
-
-
|
|
8839
|
-
-
|
|
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
|
-
|
|
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
|
|
7730
|
+
## Monitor
|
|
8848
7731
|
|
|
8849
|
-
|
|
7732
|
+
On the application side, watch:
|
|
8850
7733
|
|
|
8851
|
-
- pending
|
|
8852
|
-
-
|
|
8853
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7752
|
+
## Endpoint changes and outages
|
|
8876
7753
|
|
|
8877
|
-
|
|
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
|
-
|
|
8892
|
-
turn the delivery row green. Record the mismatch and reconcile it through the
|
|
8893
|
-
appropriate operating process.
|
|
7756
|
+
## Retention
|
|
8894
7757
|
|
|
8895
|
-
|
|
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: [
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
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
|
-
|
|
7789
|
+
{{CONSUMER_FACT:source:consumer-fact:outbound-webhooks#retryMarkdown}}
|
|
8962
7790
|
|
|
8963
|
-
|
|
8964
|
-
| --- | --- | --- |
|
|
8965
|
-
| Test is **not sent** | Application configuration or signing | Enabled configuration, published key and local error evidence |
|
|
8966
|
-
| Test or delivery is **unreachable** | Network, DNS, TLS or receiver availability | Final public URL, certificate, resolved host and receiver timeout |
|
|
8967
|
-
| **3xx** response | Endpoint is not the final receiver URL | Configure the redirect destination directly |
|
|
8968
|
-
| **400** or **401** from receiver | Signature, claims, body bytes or payload validation | Raw-body capture, signature header, algorithm, key, clock and body hash |
|
|
8969
|
-
| **404** from receiver | Wrong route or deployment | Exact configured path and environment |
|
|
8970
|
-
| **408**, **429** or **5xx** | Transient receiver failure | Capacity, timeout and retry schedule; do not start a parallel retry loop |
|
|
8971
|
-
| Delivery is **Delivered**, but work is missing | Receiver acknowledged before durable acceptance or downstream work failed | Receiver queue, deduplication record and business reconciliation |
|
|
8972
|
-
| Repeated business effect | Receiver did not atomically deduplicate **jti** | Deduplication transaction and retention period |
|
|
8973
|
-
|
|
8974
|
-
## Investigate without changing several variables
|
|
8975
|
-
|
|
8976
|
-
### Nothing was sent
|
|
8977
|
-
|
|
8978
|
-
For **Not sent** or a delivery that failed during signing, check the published
|
|
8979
|
-
verification-key configuration and signing evidence for the intended
|
|
8980
|
-
environment. The receiver cannot repair a request that never left the
|
|
8981
|
-
application. Correct the application-side configuration, then use the endpoint
|
|
8982
|
-
test before retrying real traffic.
|
|
8983
|
-
|
|
8984
|
-
### The receiver did not answer
|
|
8985
|
-
|
|
8986
|
-
Confirm the configured URL is the final public route. Check DNS, TLS, firewall
|
|
8987
|
-
or allow-list rules, deployment health and the receiver's thirty-second request
|
|
8988
|
-
window. A timeout does not prove the receiver saw nothing: search receiver logs
|
|
8989
|
-
and the deduplication store by **jti** before allowing another delivery.
|
|
8990
|
-
|
|
8991
|
-
### The receiver answered with non-2xx
|
|
8992
|
-
|
|
8993
|
-
Preserve the status and bounded response evidence. For 3xx, configure the final
|
|
8994
|
-
destination directly. For 400 or 401, inspect raw-body capture, the signature
|
|
8995
|
-
header, pinned algorithm, key identifier, clock, audience, event claims and body
|
|
8996
|
-
hash. For 404, compare the exact configured route and deployment. For 408, 429
|
|
8997
|
-
or 5xx, stabilize receiver capacity and let the scheduled retry proceed unless
|
|
8998
|
-
the delivery has already become terminal.
|
|
8999
|
-
|
|
9000
|
-
### Delivery is green but business work is missing
|
|
9001
|
-
|
|
9002
|
-
A **Delivered** row proves only that the receiver returned 2xx. Check whether
|
|
9003
|
-
the receiver atomically stored **jti** and durably queued the work before that
|
|
9004
|
-
response. Then inspect the worker, downstream dependency and authoritative
|
|
9005
|
-
business record. Recover the queued job or reconcile the business effect; do not
|
|
9006
|
-
retry the application delivery when the receiver already accepted it unless the
|
|
9007
|
-
receiver's deduplication contract makes that recovery deliberate and safe.
|
|
9008
|
-
|
|
9009
|
-
### The business effect happened more than once
|
|
9010
|
-
|
|
9011
|
-
Stop or disable the affected receiver path to contain further duplicates.
|
|
9012
|
-
Compare the repeated records with one signed **jti**. Repair the atomic claim and
|
|
9013
|
-
work-enqueue boundary, define how duplicate business records will be reconciled,
|
|
9014
|
-
and prove concurrent receipt of the same **jti** creates one effect before
|
|
9015
|
-
restoring traffic.
|
|
9016
|
-
|
|
9017
|
-
## Preserve a minimal investigation record
|
|
9018
|
-
|
|
9019
|
-
Keep the delivery identifier, endpoint identity, event channel, attempt count,
|
|
9020
|
-
state, response status, timestamps and correlation information. Redact the
|
|
9021
|
-
signature, sensitive payload fields and receiver response content before
|
|
9022
|
-
sharing evidence.
|
|
9023
|
-
|
|
9024
|
-
If verification fails after a key or deployment change, compare the key
|
|
9025
|
-
identifier and expected application environment first. Never accept every
|
|
9026
|
-
algorithm or skip body-integrity verification as a fallback.
|
|
9027
|
-
|
|
9028
|
-
## Prove and close the repair
|
|
9029
|
-
|
|
9030
|
-
1. Test the endpoint while disabled and confirm both the application outcome and
|
|
9031
|
-
receiver verification evidence.
|
|
9032
|
-
2. Exercise one negative case relevant to the incident—invalid signature,
|
|
9033
|
-
changed bytes, duplicate **jti**, receiver timeout or failed downstream work.
|
|
9034
|
-
3. Trigger one controlled real event and follow it through delivery, durable
|
|
9035
|
-
acceptance and authoritative business state.
|
|
9036
|
-
4. Retry one failed delivery only when the earlier effect is absent or safely
|
|
9037
|
-
deduplicated.
|
|
9038
|
-
5. Record the cause, correction, affected scope, delivery identities and final
|
|
9039
|
-
reconciliation without storing secrets or unrestricted payloads.
|
|
9040
|
-
|
|
9041
|
-
Escalate with the delivery and endpoint identifiers, **jti**, event context,
|
|
9042
|
-
attempt timeline, response class and redacted receiver correlation. Say
|
|
9043
|
-
explicitly whether the receiver may already have accepted or completed the
|
|
9044
|
-
work. That prevents support from treating an ambiguous business outcome as a
|
|
9045
|
-
simple transport retry.
|
|
7791
|
+
## Signature failures after a change
|
|
9046
7792
|
|
|
9047
|
-
|
|
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
|
-
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
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: [
|
|
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
|
|
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: [
|
|
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": "
|
|
9383
|
-
{ "url": "
|
|
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": {
|