If you want to connect your own application or tool to the Documents Partner API, you'll need an application in your tenant's user management. This guide walks you through the setup step by step.
About the screenshots. They come from a test environment. The tenant name and the names of the people involved have been redacted; in your case, you'll see your own tenant instead. The client ID shown belongs to a test application and is only an example of the format.
⚠️ This requires a full license. As a rule, only a full license includes the right to register your own applications. With a partner-bound license, access is tied to the respective partner product, and a self-created application would be rejected with
403 CLIENT_NOT_LICENSED. The difference is explained in Licenses and Permissions.Your contract is what matters — it may contain different terms in individual cases. If you're unsure whether this option is available for your firm, check with your STP contact beforehand.
What you get — and what you don't
| Applies to | Your tenant and its sub-tenants only |
| Display name | Freely chosen |
| Client ID | Generated, not assigned by you — you receive a GUID |
| Authentication method | Only Authorization Code is enabled for the Documents Partner API |
| Permission scopes | Only from the selection approved for your tenant |
| Client secret | Required — the application is always confidential |
The fact that the client ID is generated is the part that tends to catch people off guard: you can't choose it yourself. Your application must use the generated GUID.
STP administrators can also see the applications you create — useful if you contact support.
Are you a partner looking to offer your product to multiple firms? Then this approach isn't the right one — a tenant-specific application only works for a single tenant. A client that works for all licensed firms is registered centrally by STP; contact STP support for that.
Step one: Go to the application overview
Sign in as an administrator of your tenant and open Identity Admin. In the left sidebar, you'll find the Applications entry.
The list is empty at first ("no applications"). In the top right, "+ new application" lets you create one.
Step two: Basic details and behavior
Start by entering the display name — this will appear on the consent page later, so it should be meaningful to your users. In the example above, it's My Test Client.
Below that are the toggles. These must be enabled:
| Toggle | Why |
|---|---|
| enabled | Otherwise the application is created but can't be used |
| requires user consent | The user confirms once what the application is allowed to do |
| requires Proof Key for Code Exchange (PKCE) | Required for the Authorization Code flow |
| allow access tokens via browser | Needed when authentication takes place in the browser |
| include user properties in ID token | Provides information about the signed-in user |
| user Xid property | The single most important toggle — see below |
| allow offline access | Issues refresh tokens so the application can keep running without requiring re-authentication every hour |
⚠️ Without "user Xid property", every request will fail later. The session in document management is run on behalf of the requesting user, and their identifier comes exclusively from this property. If it's missing, authentication completes without errors — yet every functional call will respond with
403 MISSING_USER_IDENTITY, an error that's nearly impossible to diagnose without this hint.
These toggles should remain off:
| Toggle | Why not |
|---|---|
| restrict access to internal host only | Would block external access |
| reuse refresh tokens | Only with good reason; the interface itself warns against it |
| update user properties with refresh tokens | Not needed for this purpose |
| use reference tokens | Requires additional validation capabilities on the API side |
The "refresh token length in bytes" and "left panel URI" fields should be left empty. For the former, the interface itself warns correctly — too few bytes leads to collisions.
You can leave the token lifetimes below unchanged. The default values are:
| Field | Default |
|---|---|
| Identity token lifetime |
300 seconds |
| Access token lifetime |
3600 seconds (one hour) |
| Authorization code lifetime |
300 seconds |
| Device code lifetime |
300 seconds |
| Absolute refresh token lifetime |
2592000 seconds (30 days) |
Step three: Grant type
Under allowed grant types, select Authorization Code.
For the Documents Partner API, this is the only available option — and that's intentional: the application acts on behalf of a user, and only this flow carries a user identifier. A flow without user authentication would fail at the same point as a missing Xid property.
Step four: Permission scopes
Under allowed scopes, select what the application is permitted to request. For the Documents Partner API, these are:
| Scope in the UI | Technical name | Purpose |
|---|---|---|
| ID / Your user identifier | openid |
Authentication |
| ID / User profile | profile |
User profile |
| ID / Your user email address | email |
User's email address |
| UserManagement.Identity.Account / read my profile | um.acc.profile.read |
Read the signed-in user's profile |
| Documents Partner API: standalone reach | dms.cloud.api.standalone |
Mandatory — see below |
| Documents Partner API: read documents (list, download) | dms.cloud.api.documents.read |
Read documents |
| Documents Partner API: write documents (upload/import) | dms.cloud.api.documents.write |
Write documents |
| Documents Partner API: stage content via presigned upload URLs | dms.cloud.api.uploads |
Stage files |
A read-only application can get by without
documents.write and uploads. Anything that
needs to store documents requires both.
Why
dms.cloud.api.standalone must not be omitted
Every request is checked against two types of
scopes: the functional scope and the license scope.
If the license scope is missing, the request is rejected before the
functional scope is even considered — with
403 LICENSE_MISSING.
For a self-created application, dms.cloud.api.standalone
is the only license scope available — the partner-bound variant belongs
to the respective partner product.
Without this scope, the application is fully configured yet rejected on every single request. The full validation sequence is described in Licenses and Permissions.
Step five: Client secret
The "requires client secret" toggle is mandatory for tenant-specific applications — leave it enabled. Use it to create a secret under "Pre-shared secrets" by clicking New.
Your application must therefore be able to store it securely. For a purely browser-based application that can't do this, this approach is not suitable — authentication should go through a server on your end that holds the secret.
The secret is only shown in plain text once. After that, the table only shows the hash value in the Value column; Value (unhashed) remains empty. Write down the secret immediately and store it in a secure location — it cannot be retrieved again, only deleted and recreated.
The second table, Certificate secrets, stays empty. It is not needed for the Documents Partner API.
Step six: URLs
Finally, enter the URLs for your application:
| Field | What to enter | Required |
|---|---|---|
| Post-login redirect URIs | The URL to redirect to after a successful sign-in, e.g. http://localhost:5099/callback
|
yes |
| Post-logout redirect URIs | The URL to redirect to after sign-out, e.g.
http://localhost:5099/
|
recommended |
| allowed CORS origins | Only needed if the application runs in the browser and calls the API directly, e.g.
http://localhost:5099
|
optional |
The URLs must match exactly what your application sends during sign-in — including scheme, port, and trailing slash.
A server-side service with no browser interface doesn't need a CORS origin.
Click Save to create the application.
Changes don't take effect immediately. The interface itself notes this: it can take up to 15 minutes for a change to take effect. If something isn't working right after saving, try waiting a moment and trying again before troubleshooting further.
Step seven: Copy the client ID
The application now appears in the list with its generated
client ID — a GUID such as
303be097-b510-4436-95ac-f804f8933439. Next to it are the
Owner (your tenant) and the Creator
(whoever created it). Below that, the overview shows the assigned
scopes, for example:
"dms.cloud.api.documents.read" "dms.cloud.api.documents.write"
"dms.cloud.api.standalone" "dms.cloud.api.uploads"
"email" "openid" "profile" "um.acc.profile.read"
This GUID is the client_id your application uses when
signing in. Together with the tenant name, you now have everything
described in Getting
Started for the authentication flow.
Step eight: Complete the authentication
With the client ID in hand, the flow is the same as for any other application. The endpoints are available at your tenant's authentication URL:
https://<tenant>.stp-cloud.de/identity/.well-known/openid-configuration
The subdomain must match the tenant that the acting user account belongs to — for both
authorizeandtoken. A request against a different tenant will fail, even if the client ID and secret are correct.
First: Send the user to sign in
GET https://<tenant>.stp-cloud.de/identity/connect/authorize
?client_id=303be097-b510-4436-95ac-f804f8933439
&redirect_uri=http%3A%2F%2Flocalhost%3A5099%2Fcallback
&response_type=code
&scope=*
&state=<your-random-value>
&code_challenge=<BASE64URL-SHA256-of-verifier>
&code_challenge_method=S256
After sign-in and consent, the user is redirected to the redirect
URI, which carries code and state as query
parameters.
scope=*is the recommended approach. STP-IAM responds to a wildcard — as well as an emptyscope— with all scopes the application is allowed to request. What actually ends up in the token is always the intersection with the license and the user's roles.The reason to prefer this over a fixed list: an authentication service will reject the entire request with
invalid_scopeif even a single named scope is unknown. A hard-coded list is therefore one rename away from breaking authentication entirely. An explicit list only makes sense if an application deliberately needs to request fewer scopes than it's entitled to.
Second: Exchange the code for a token
POST to the token endpoint, with content as
application/x-www-form-urlencoded:
| Field | Value |
|---|---|
grant_type |
authorization_code |
code |
The code from the redirect |
client_id |
The generated GUID of the application |
redirect_uri |
Exactly the same as in the previous request |
code_verifier |
The secret used to generate the code_challenge
|
client_secret |
The secret created in step five |
curl -sS -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=$CODE" \
-d "client_id=303be097-b510-4436-95ac-f804f8933439" \
-d "redirect_uri=http://localhost:5099/callback" \
-d "code_verifier=$CODE_VERIFIER" \
-d "client_secret=$CLIENT_SECRET" \
"https://<tenant>.stp-cloud.de/identity/connect/token"Two common reasons this request fails:
-
code_verifieris required whenever PKCE is enabled — and it is here. The authentication service validates it against the previously sentcode_challenge. Without it, the exchange is rejected even if thecodeand secret are correct. -
client_secretis always required here. Tenant-specific applications are always confidential, so the authentication service expects the secret. (Only with a public client — which doesn't exist in this setup — would the field be omitted entirely; sending it as empty would also be wrong in that case.)
The rest of the flow — renewal via
grant_type=refresh_token, token contents — is covered in
Getting
Started.
Step nine: Your first request
The API is available under the same tenant:
https://<tenant>.stp-cloud.de/documents/dms-cloud-api/api
The version identifier does not belong in the base URL — it goes at the beginning of each path. Your first request should be a search — it validates the entire chain from token through license all the way into document management:
curl -sS \
-H "Authorization: Bearer $TOKEN" \
-H "X-Correlation-Id: my-first-request" \
"https://<tenant>.stp-cloud.de/documents/dms-cloud-api/api/v1/dms/documents/container?pageSize=5"If you get a list back — even an empty one — the integration is working. To see where to go from there, check out Quick Start.
If you get an error instead
| Response | Meaning | What to do |
|---|---|---|
401 (no error body)
|
Token is missing, expired, or invalid | Fetch a new token |
403 LICENSE_MISSING |
The tenant has no license, or the token doesn't carry a license scope | Have the licensing checked |
403 CLIENT_NOT_LICENSED |
The license scope in the token belongs to a different application | Check that dms.cloud.api.standalone is in the allowed scopes |
403 INSUFFICIENT_SCOPE |
License is present, but the capability scope is missing | Check the account's role — and whether the application is allowed to request the scope |
403 MISSING_USER_IDENTITY |
The token doesn't carry a user identifier | Enable "user Xid property" on the application (step two) |
404 NOT_FOUND |
Does not exist or is not visible to this user | Check the identifier — and the account's permissions in document management |
502 CONNECTOR_OFFLINE |
The firm's connector is unreachable | Have the connector's status checked |
The full list is available in Error Codes.
What else you'll need
The application alone isn't enough — it only sets the upper limit. You'll still need:
-
A role for the user accounts that will be acting.
Without one, the functional scope won't be present in the token, and
requests will end with
403 INSUFFICIENT_SCOPE— see Roles and Groups. -
An installed and connected Connector — otherwise
every request will respond with
502 CONNECTOR_OFFLINE.
What actually ends up in the token is the intersection of what the application is allowed to request, what the license grants, and what the user's roles provide. A scope that's missing from the application will never make it in — no matter how well licensed or permissioned the user is.
Further reading
- Quick Start — individual API calls with curl and C#
- Roles and Groups
- Licenses and Permissions
- Error Codes