Apps tools
MCP tools for Apps: apps_create, apps_disable, apps_get, apps_installations, apps_list, apps_listing_get, apps_listing_submit, apps_listing_withdraw, apps_rotate_secret, apps_update.
apps_create: Register an appapps_disable: Disable a registered appapps_get: Get a registered appapps_installations: List an app's installationsapps_list: List the organization's registered appsapps_listing_get: Get an app's listingapps_listing_submit: Submit an app for listingapps_listing_withdraw: Withdraw an app's listing applicationapps_rotate_secret: Rotate a registered app's client secretapps_update: Update a registered app
apps_create
Register an app. Operation apps.create · Organization level · permission agents:admin · risk tier security · since M10
Registers an OAuth app (a partner integration) that organizations install through Ownrecord's consent screen.
It uses the authorization code flow with PKCE at /oauth/auth and /oauth/token, with rotating refresh
tokens, and acts in each organization that installs it, with at most the installing admin's access.
type: "confidential"(default): a server-side app. The response contains its client secret incredentials.clientSecret, shown only once.type: "public": a native or browser app without a secret (PKCE only).
visibility: "private" apps install only in your own organization. "listed" apps can be installed by any
organization once platform review approved them: apply with apps.listing.submit (publisher identity, privacy
policy, security questionnaire, scope justification); until then the listing is not_submitted and other
organizations can't install the app. scopes are the most the app may ask for.
dpopBoundAccessTokens: true requires a DPoP proof on every token request (RFC 9449): access and refresh
tokens are then always bound to the app's key.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: false · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | yes | (1–100 characters) |
publisher | string | yes | (1–100 characters) |
logoUrl | string | no | (at most 2000 characters) |
websiteUrl | string | no | (at most 2000 characters) |
privacyPolicyUrl | string | no | (at most 2000 characters) |
redirectUris | array of string | yes | (1–10 items) |
type | one of confidential, public | no | (default "confidential") |
visibility | one of private, listed | no | (default "private") |
scopes | array of one of 84 values, e.g. org:read, org:write, org:admin | yes | (1–200 items) |
dpopBoundAccessTokens | boolean | no | Require DPoP for every token: a token request without a DPoP proof is refused (RFC 9449). (default false) |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
validation_failed: A field is invalid, e.g. a redirect URI that isn't https or loopback.permission_denied: You can't manage the organization's apps.fresh_auth_required: Your last sign-in is older than 15 minutes.
Example
A listed accounting app
Arguments:
{
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"websiteUrl": "https://ledgerly.example",
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"redirectUris": ["https://ledgerly.example/oauth/callback"],
"visibility": "listed",
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"app": {
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "app",
"ownerOrgId": "org_01m4ttwd3veh9apbewxheq5xhb",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"logoUrl": null,
"websiteUrl": "https://ledgerly.example",
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"redirectUris": ["https://ledgerly.example/oauth/callback"],
"type": "confidential",
"visibility": "listed",
"review": {
"status": "not_submitted",
"at": null
},
"verified": false,
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"secretPrefix": "or_cs_Exampl",
"dpopBoundAccessTokens": false,
"status": "active",
"suspension": null,
"createdAt": "2026-10-25T09:00:00.000Z",
"updatedAt": "2026-10-25T09:00:00.000Z"
},
"credentials": {
"clientId": "ocl_01m4ttwd3veh9apbewxheq5xha",
"clientSecret": "or_cs_ExampleOnlyNotARealSecret0000000000000000",
"tokenEndpoint": "https://ownrecord.io/oauth/token",
"authMethod": "client_secret_basic"
}
}
apps_disable
Disable a registered app. Operation apps.disable · Organization level · permission agents:admin · risk tier security · since M10
Disables an app for good, in every organization at once, in one transaction:
- every access and refresh token, authorization code and grant it holds is deleted, and so are the tokens of the agents it owns;
- every installation that isn't uninstalled is paused (
pausedReason: app_disabled, whatever it was before); it never resumes (app_installations.resumeanswersoauth_client_disabled), and it can still be uninstalled; - from then on the OAuth server issues the app and its agents nothing and refuses their tokens, and nothing installs it.
Your organization and every organization with the app installed get the APP_DISABLED alert (high: their
owners and admins are emailed); other organizations aren't told who disabled it. Nothing re-enables an app:
register a new one instead. Disabling a disabled app changes nothing.
Annotations: readOnlyHint: false · destructiveHint: true · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (ocl_…) | yes | – |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
resource_not_found: Your organization registered no app with this ID.version_conflict: The app or one of its installations changed at the same moment; retry.permission_denied: You can't manage the organization's apps.
Example
Disable an app
Arguments:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "app",
"ownerOrgId": "org_01m4ttwd3veh9apbewxheq5xhb",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"logoUrl": null,
"websiteUrl": "https://ledgerly.example",
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"redirectUris": ["https://ledgerly.example/oauth/callback"],
"type": "confidential",
"visibility": "listed",
"review": {
"status": "approved",
"at": "2026-10-25T09:00:00.000Z"
},
"verified": true,
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"secretPrefix": "or_cs_Exampl",
"dpopBoundAccessTokens": false,
"status": "disabled",
"suspension": null,
"createdAt": "2026-10-25T09:00:00.000Z",
"updatedAt": "2026-10-26T14:30:00.000Z"
}
apps_get
Get a registered app. Operation apps.get · Organization level · permission agents:read · risk tier read · since M10
Returns one of your organization's registered OAuth apps. Never the secret.
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (ocl_…) | yes | – |
- Read-only: changes nothing;
idempotency_keyis ignored.
Errors
resource_not_found: Your organization registered no app with this ID.permission_denied: You can't see the organization's apps.
Example
An app
Arguments:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha"
}
Result output:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "app",
"ownerOrgId": "org_01m4ttwd3veh9apbewxheq5xhb",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"logoUrl": null,
"websiteUrl": "https://ledgerly.example",
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"redirectUris": ["https://ledgerly.example/oauth/callback"],
"type": "confidential",
"visibility": "listed",
"review": {
"status": "approved",
"at": "2026-10-25T09:00:00.000Z"
},
"verified": true,
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"secretPrefix": "or_cs_Exampl",
"dpopBoundAccessTokens": false,
"status": "active",
"suspension": null,
"createdAt": "2026-10-25T09:00:00.000Z",
"updatedAt": "2026-10-25T09:00:00.000Z"
}
apps_installations
List an app's installations. Operation apps.installations · Organization level · permission agents:read · risk tier read · since M10
For the publisher of a registered app: the organizations that installed it, newest first (at most 100), with
the scopes they granted and the installation's status. Each API call the app makes names one of them with
Ownrecord-Org when its token covers several.
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (ocl_…) | yes | – |
- Read-only: changes nothing;
idempotency_keyis ignored.
Errors
resource_not_found: Your organization registered no app with this ID.permission_denied: You can't see the organization's apps.
Example
Where an app is installed
Arguments:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha"
}
Result output:
{
"object": "list",
"data": [
{
"id": "ains_01m4ttwd3veh9apbewxheq5xhc",
"orgId": "org_01m4ttwd3veh9apbewxheq5xh3",
"orgName": "Acme",
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write"],
"status": "active",
"createdAt": "2026-10-25T09:00:00.000Z",
"updatedAt": "2026-10-25T09:00:00.000Z"
}
]
}
apps_list
List the organization's registered apps. Operation apps.list · Organization level · permission agents:read · risk tier read · since M10
Lists the OAuth apps your organization registered for partners (Developers → Apps), newest first, with their review status. Never returns secrets. Filter by status; paginate with limit and starting_after.
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
limit | integer | no | (1–100) |
starting_after | ID (ocl_…) | no | – |
status | one of active, disabled | no | – |
- Read-only: changes nothing;
idempotency_keyis ignored.
Errors
validation_failed: A filter or pagination value is invalid.permission_denied: You can't see the organization's apps.
Examples
Registered apps
Arguments:
{}
Result output:
{
"object": "list",
"data": [
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "app",
"ownerOrgId": "org_01m4ttwd3veh9apbewxheq5xhb",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"logoUrl": null,
"websiteUrl": "https://ledgerly.example",
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"redirectUris": ["https://ledgerly.example/oauth/callback"],
"type": "confidential",
"visibility": "listed",
"review": {
"status": "approved",
"at": "2026-10-25T09:00:00.000Z"
},
"verified": true,
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"secretPrefix": "or_cs_Exampl",
"dpopBoundAccessTokens": false,
"status": "active",
"suspension": null,
"createdAt": "2026-10-25T09:00:00.000Z",
"updatedAt": "2026-10-25T09:00:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}
One per page
Arguments:
{
"limit": 1
}
Result output:
{
"object": "list",
"data": [
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "app",
"ownerOrgId": "org_01m4ttwd3veh9apbewxheq5xhb",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"logoUrl": null,
"websiteUrl": "https://ledgerly.example",
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"redirectUris": ["https://ledgerly.example/oauth/callback"],
"type": "confidential",
"visibility": "listed",
"review": {
"status": "approved",
"at": "2026-10-25T09:00:00.000Z"
},
"verified": true,
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"secretPrefix": "or_cs_Exampl",
"dpopBoundAccessTokens": false,
"status": "active",
"suspension": null,
"createdAt": "2026-10-25T09:00:00.000Z",
"updatedAt": "2026-10-25T09:00:00.000Z"
}
],
"has_more": true,
"next_cursor": "ocl_01m4ttwd3veh9apbewxheq5xha"
}
apps_listing_get
Get an app's listing. Operation apps.listing.get · Organization level · permission agents:read · risk tier read · since M13
Returns the listing of one of your organization's registered apps: the review status, whether it is listed
for every organization (a verified publisher) and for which scopes (approvedScopes; pendingScopes wait for
review), the last decision's reason, your application, its history, and the current security questionnaire
to answer with apps.listing.submit.
Statuses: not_submitted (no application yet, or withdrawn), pending (Ownrecord reviews it),
changes_requested (edit the application and submit it again), approved, rejected. An approved app stays
listed for its approved scopes while scopes it added are reviewed again.
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (ocl_…) | yes | – |
- Read-only: changes nothing;
idempotency_keyis ignored.
Errors
resource_not_found: Your organization registered no app with this ID.permission_denied: You can't see the organization's apps.
Example
An application waiting for review
Arguments:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha"
}
Result output:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "app_listing",
"ownerOrgId": "org_01m4ttwd3veh9apbewxheq5xhb",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"visibility": "listed",
"appStatus": "active",
"status": "pending",
"listed": false,
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"approvedScopes": null,
"pendingScopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"submittedAt": "2026-10-30T09:00:00.000Z",
"decidedAt": null,
"reason": null,
"application": {
"publisher": {
"legalName": "Ledgerly AI B.V.",
"country": "NL",
"websiteUrl": "https://ledgerly.example",
"contact": {
"name": "Mira de Vries",
"email": "partners@ledgerly.example"
}
},
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"questionnaire": {
"version": 1,
"answers": [
{
"questionId": "data_location",
"answer": null,
"details": "AWS eu-central-1 (Frankfurt), Germany"
},
{
"questionId": "encryption_in_transit",
"answer": "yes",
"details": null
},
{
"questionId": "encryption_at_rest",
"answer": "yes",
"details": null
},
{
"questionId": "credential_storage",
"answer": "yes",
"details": null
},
{
"questionId": "staff_access",
"answer": "yes",
"details": null
},
{
"questionId": "incident_response",
"answer": "yes",
"details": null
},
{
"questionId": "subprocessors",
"answer": "yes",
"details": "AWS (hosting), Postmark (email)"
},
{
"questionId": "model_training",
"answer": "no",
"details": null
},
{
"questionId": "data_deletion",
"answer": "yes",
"details": "Within 30 days of uninstalling"
},
{
"questionId": "vulnerability_management",
"answer": "yes",
"details": null
}
]
},
"scopeJustifications": [
{
"scope": "filings:read",
"justification": "Reads filing periods to prepare each return."
},
{
"scope": "filings:write",
"justification": "Records the prepared return and its submission."
},
{
"scope": "tasks:read",
"justification": "Finds the filing tasks assigned to the app."
},
{
"scope": "tasks:write",
"justification": "Claims and completes filing tasks."
},
{
"scope": "ledger:read",
"justification": "Reconciles the return with the tax payable accounts."
}
],
"partnerTermsVersion": "1.0",
"submittedBy": "usr_01m4ttwd3veh9apbewxheq5xhf"
},
"history": [
{
"action": "submitted",
"at": "2026-10-30T09:00:00.000Z",
"by": "usr_01m4ttwd3veh9apbewxheq5xhf",
"reason": null,
"scopes": []
}
],
"version": 3,
"questionnaire": {
"version": 1,
"status": "proposed",
"questions": [
{
"id": "data_location",
"type": "text",
"required": true,
"text": "Where do you store and process organizations' data? Name the countries and hosting providers.",
"detailsRequiredWhen": null,
"hint": null
}
]
}
}
apps_listing_submit
Submit an app for listing. Operation apps.listing.submit · Organization level · permission agents:admin · risk tier write · since M13
Applies to list one of your organization's apps for every organization on Ownrecord, or submits a changed
application. Ownrecord reviews it (pending) and approves it, asks for changes or rejects it, with a reason sent
to the contact you give. Once approved, the app is listed: any organization can install it, and its consent
screen shows a verified publisher.
The application names the publisher (legal name, country, website and a contact), links the app's privacy
policy, answers the current security questionnaire (apps.listing.get returns its questions and version) and
justifies every scope the app asks for. Your organization must have accepted the current Partner and App Terms
(partner_terms.accept). Submitting makes the app listed.
You can edit and submit again while the application waits for review or for your changes. Submitting again for an approved app keeps it listed for its approved scopes during the review, unless the publisher's legal name, country or website changed: then it is listed again only once approved.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (ocl_…) | yes | – |
publisher | object | yes | – |
privacyPolicyUrl | string | yes | The app's privacy policy (it becomes the app's privacyPolicyUrl). (at most 2000 characters) |
questionnaire | object | yes | – |
scopeJustifications | array of object | yes | (at most 200 items) |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
app_listing_incomplete: A required question isn't answered, a scope isn't justified, or the questionnaire version isn't the current one.partner_terms_not_accepted: Your organization hasn't accepted the current Partner and App Terms.oauth_client_disabled: The app is disabled.validation_failed: A field is invalid, e.g. a URL that isn't https.resource_not_found: Your organization registered no app with this ID.version_conflict: The app changed at the same moment; retry.permission_denied: You can't manage the organization's apps.
Example
Apply to list an accounting app
Arguments:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"publisher": {
"legalName": "Ledgerly AI B.V.",
"country": "NL",
"websiteUrl": "https://ledgerly.example",
"contact": {
"name": "Mira de Vries",
"email": "partners@ledgerly.example"
}
},
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"questionnaire": {
"version": 1,
"answers": [
{
"questionId": "data_location",
"details": "AWS eu-central-1 (Frankfurt), Germany"
},
{
"questionId": "encryption_in_transit",
"answer": "yes"
},
{
"questionId": "encryption_at_rest",
"answer": "yes"
},
{
"questionId": "credential_storage",
"answer": "yes"
},
{
"questionId": "staff_access",
"answer": "yes"
},
{
"questionId": "incident_response",
"answer": "yes"
},
{
"questionId": "subprocessors",
"answer": "yes",
"details": "AWS (hosting), Postmark (email)"
},
{
"questionId": "model_training",
"answer": "no"
},
{
"questionId": "data_deletion",
"answer": "yes",
"details": "Within 30 days of uninstalling"
},
{
"questionId": "vulnerability_management",
"answer": "yes"
}
]
},
"scopeJustifications": [
{
"scope": "filings:read",
"justification": "Reads filing periods to prepare each return."
},
{
"scope": "filings:write",
"justification": "Records the prepared return and its submission."
},
{
"scope": "tasks:read",
"justification": "Finds the filing tasks assigned to the app."
},
{
"scope": "tasks:write",
"justification": "Claims and completes filing tasks."
},
{
"scope": "ledger:read",
"justification": "Reconciles the return with the tax payable accounts."
}
],
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "app_listing",
"ownerOrgId": "org_01m4ttwd3veh9apbewxheq5xhb",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"visibility": "listed",
"appStatus": "active",
"status": "pending",
"listed": false,
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"approvedScopes": null,
"pendingScopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"submittedAt": "2026-10-30T09:00:00.000Z",
"decidedAt": null,
"reason": null,
"application": {
"publisher": {
"legalName": "Ledgerly AI B.V.",
"country": "NL",
"websiteUrl": "https://ledgerly.example",
"contact": {
"name": "Mira de Vries",
"email": "partners@ledgerly.example"
}
},
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"questionnaire": {
"version": 1,
"answers": [
{
"questionId": "data_location",
"answer": null,
"details": "AWS eu-central-1 (Frankfurt), Germany"
},
{
"questionId": "encryption_in_transit",
"answer": "yes",
"details": null
},
{
"questionId": "encryption_at_rest",
"answer": "yes",
"details": null
},
{
"questionId": "credential_storage",
"answer": "yes",
"details": null
},
{
"questionId": "staff_access",
"answer": "yes",
"details": null
},
{
"questionId": "incident_response",
"answer": "yes",
"details": null
},
{
"questionId": "subprocessors",
"answer": "yes",
"details": "AWS (hosting), Postmark (email)"
},
{
"questionId": "model_training",
"answer": "no",
"details": null
},
{
"questionId": "data_deletion",
"answer": "yes",
"details": "Within 30 days of uninstalling"
},
{
"questionId": "vulnerability_management",
"answer": "yes",
"details": null
}
]
},
"scopeJustifications": [
{
"scope": "filings:read",
"justification": "Reads filing periods to prepare each return."
},
{
"scope": "filings:write",
"justification": "Records the prepared return and its submission."
},
{
"scope": "tasks:read",
"justification": "Finds the filing tasks assigned to the app."
},
{
"scope": "tasks:write",
"justification": "Claims and completes filing tasks."
},
{
"scope": "ledger:read",
"justification": "Reconciles the return with the tax payable accounts."
}
],
"partnerTermsVersion": "1.0",
"submittedBy": "usr_01m4ttwd3veh9apbewxheq5xhf"
},
"history": [
{
"action": "submitted",
"at": "2026-10-30T09:00:00.000Z",
"by": "usr_01m4ttwd3veh9apbewxheq5xhf",
"reason": null,
"scopes": []
}
],
"version": 3
}
apps_listing_withdraw
Withdraw an app's listing application. Operation apps.listing.withdraw · Organization level · permission agents:admin · risk tier write · since M13
Withdraws an application that waits for review (pending) or for your changes (changes_requested). The
application stays saved for a later apps.listing.submit. A first application goes back to not_submitted; a
re-review of an approved app goes back to approved, still listed for its approved scopes only.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: false · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (ocl_…) | yes | – |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
app_listing_not_withdrawable: The app has no application waiting for review or for changes.resource_not_found: Your organization registered no app with this ID.version_conflict: The app changed at the same moment; retry.permission_denied: You can't manage the organization's apps.
Example
Withdraw a first application
Arguments:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "app_listing",
"ownerOrgId": "org_01m4ttwd3veh9apbewxheq5xhb",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"visibility": "listed",
"appStatus": "active",
"status": "not_submitted",
"listed": false,
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"approvedScopes": null,
"pendingScopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"submittedAt": "2026-10-30T09:00:00.000Z",
"decidedAt": null,
"reason": null,
"application": {
"publisher": {
"legalName": "Ledgerly AI B.V.",
"country": "NL",
"websiteUrl": "https://ledgerly.example",
"contact": {
"name": "Mira de Vries",
"email": "partners@ledgerly.example"
}
},
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"questionnaire": {
"version": 1,
"answers": [
{
"questionId": "data_location",
"answer": null,
"details": "AWS eu-central-1 (Frankfurt), Germany"
},
{
"questionId": "encryption_in_transit",
"answer": "yes",
"details": null
},
{
"questionId": "encryption_at_rest",
"answer": "yes",
"details": null
},
{
"questionId": "credential_storage",
"answer": "yes",
"details": null
},
{
"questionId": "staff_access",
"answer": "yes",
"details": null
},
{
"questionId": "incident_response",
"answer": "yes",
"details": null
},
{
"questionId": "subprocessors",
"answer": "yes",
"details": "AWS (hosting), Postmark (email)"
},
{
"questionId": "model_training",
"answer": "no",
"details": null
},
{
"questionId": "data_deletion",
"answer": "yes",
"details": "Within 30 days of uninstalling"
},
{
"questionId": "vulnerability_management",
"answer": "yes",
"details": null
}
]
},
"scopeJustifications": [
{
"scope": "filings:read",
"justification": "Reads filing periods to prepare each return."
},
{
"scope": "filings:write",
"justification": "Records the prepared return and its submission."
},
{
"scope": "tasks:read",
"justification": "Finds the filing tasks assigned to the app."
},
{
"scope": "tasks:write",
"justification": "Claims and completes filing tasks."
},
{
"scope": "ledger:read",
"justification": "Reconciles the return with the tax payable accounts."
}
],
"partnerTermsVersion": "1.0",
"submittedBy": "usr_01m4ttwd3veh9apbewxheq5xhf"
},
"history": [
{
"action": "submitted",
"at": "2026-10-30T09:00:00.000Z",
"by": "usr_01m4ttwd3veh9apbewxheq5xhf",
"reason": null,
"scopes": []
},
{
"action": "withdrawn",
"at": "2026-10-30T11:00:00.000Z",
"by": "usr_01m4ttwd3veh9apbewxheq5xhf",
"reason": null,
"scopes": []
}
],
"version": 4
}
apps_rotate_secret
Rotate a registered app's client secret. Operation apps.rotate_secret · Organization level · permission agents:admin · risk tier security · since M10
Replaces a confidential app's client secret. The new secret is in credentials.clientSecret, shown only
once; the old one stops working at once, so update your server first. Tokens already issued keep working.
Annotations: readOnlyHint: false · destructiveHint: true · idempotentHint: false · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (ocl_…) | yes | – |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
validation_failed: The app is public and has no secret.oauth_client_disabled: The app is disabled.resource_not_found: Your organization registered no app with this ID.version_conflict: The app changed at the same moment; retry.permission_denied: You can't manage the organization's apps.fresh_auth_required: Your last sign-in is older than 15 minutes.
Example
Rotate the secret
Arguments:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"app": {
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "app",
"ownerOrgId": "org_01m4ttwd3veh9apbewxheq5xhb",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"logoUrl": null,
"websiteUrl": "https://ledgerly.example",
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"redirectUris": ["https://ledgerly.example/oauth/callback"],
"type": "confidential",
"visibility": "listed",
"review": {
"status": "approved",
"at": "2026-10-25T09:00:00.000Z"
},
"verified": true,
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"secretPrefix": "or_cs_Exampl",
"dpopBoundAccessTokens": false,
"status": "active",
"suspension": null,
"createdAt": "2026-10-25T09:00:00.000Z",
"updatedAt": "2026-10-26T14:30:00.000Z"
},
"credentials": {
"clientId": "ocl_01m4ttwd3veh9apbewxheq5xha",
"clientSecret": "or_cs_ExampleOnlyNotARealSecret0000000000000000",
"tokenEndpoint": "https://ownrecord.io/oauth/token",
"authMethod": "client_secret_basic"
}
}
apps_update
Update a registered app. Operation apps.update · Organization level · permission agents:admin · risk tier security · since M10
Changes a registered app's name, publisher, links, redirect URIs, visibility, scopes or whether every token must
be DPoP-bound (dpopBoundAccessTokens). Send only what changes; null removes a link. Narrower scopes apply to
the app's tokens at once in every installation; wider scopes need the organizations' consent again.
For listed apps (see apps.listing.get): visibility: "listed" waits for an application
(apps.listing.submit); "private" ends the listing. On an approved app, scopes beyond the approved ones go to
review again (pending): the app stays listed for its approved scopes, other organizations can consent only to
those, and installations keep the scopes they consented to until the new ones are approved and consented. A new
name or publisher of an approved app is reviewed again before the app is listed again.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (ocl_…) | yes | – |
name | string | no | (1–100 characters) |
publisher | string | no | (1–100 characters) |
logoUrl | string or null | no | – |
websiteUrl | string or null | no | – |
privacyPolicyUrl | string or null | no | – |
redirectUris | array of string | no | (1–10 items) |
visibility | one of private, listed | no | – |
scopes | array of one of 84 values, e.g. org:read, org:write, org:admin | no | (1–200 items) |
dpopBoundAccessTokens | boolean | no | Require DPoP for every token: a token request without a DPoP proof is refused (RFC 9449). |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
validation_failed: A field is invalid.oauth_client_disabled: The app is disabled.resource_not_found: Your organization registered no app with this ID.version_conflict: The app changed at the same moment; retry.permission_denied: You can't manage the organization's apps.
Example
Add a redirect URI
Arguments:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"redirectUris": ["https://ledgerly.example/oauth/callback", "https://eu.ledgerly.example/oauth/callback"],
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "ocl_01m4ttwd3veh9apbewxheq5xha",
"object": "app",
"ownerOrgId": "org_01m4ttwd3veh9apbewxheq5xhb",
"name": "Ledgerly AI",
"publisher": "Ledgerly AI B.V.",
"logoUrl": null,
"websiteUrl": "https://ledgerly.example",
"privacyPolicyUrl": "https://ledgerly.example/privacy",
"redirectUris": ["https://ledgerly.example/oauth/callback", "https://eu.ledgerly.example/oauth/callback"],
"type": "confidential",
"visibility": "listed",
"review": {
"status": "approved",
"at": "2026-10-25T09:00:00.000Z"
},
"verified": true,
"scopes": ["filings:read", "filings:write", "tasks:read", "tasks:write", "ledger:read"],
"secretPrefix": "or_cs_Exampl",
"dpopBoundAccessTokens": false,
"status": "active",
"suspension": null,
"createdAt": "2026-10-25T09:00:00.000Z",
"updatedAt": "2026-10-26T14:30:00.000Z"
}