Skip to content

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 app
  • apps_disable: Disable a registered app
  • apps_get: Get a registered app
  • apps_installations: List an app's installations
  • apps_list: List the organization's registered apps
  • apps_listing_get: Get an app's listing
  • apps_listing_submit: Submit an app for listing
  • apps_listing_withdraw: Withdraw an app's listing application
  • apps_rotate_secret: Rotate a registered app's client secret
  • apps_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 in credentials.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

ArgumentTypeRequiredDescription
namestringyes(1–100 characters)
publisherstringyes(1–100 characters)
logoUrlstringno(at most 2000 characters)
websiteUrlstringno(at most 2000 characters)
privacyPolicyUrlstringno(at most 2000 characters)
redirectUrisarray of stringyes(1–10 items)
typeone of confidential, publicno(default "confidential")
visibilityone of private, listedno(default "private")
scopesarray of one of 84 values, e.g. org:read, org:write, org:adminyes(1–200 items)
dpopBoundAccessTokensbooleannoRequire DPoP for every token: a token request without a DPoP proof is refused (RFC 9449). (default false)
idempotency_keystringnoA 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_runbooleannotrue: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false)
  • Idempotency: the result returns the idempotency_key used, 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: true checks permissions and rules and computes the result, then rolls everything back.

Errors

Example

A listed accounting app

Arguments:

JSON
{
  "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:

JSON
{
  "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.resume answers oauth_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

ArgumentTypeRequiredDescription
idID (ocl_…)yes–
idempotency_keystringnoA 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_runbooleannotrue: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false)
  • Idempotency: the result returns the idempotency_key used, 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: true checks permissions and rules and computes the result, then rolls everything back.

Errors

Example

Disable an app

Arguments:

JSON
{
  "id": "ocl_01m4ttwd3veh9apbewxheq5xha",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}

Result output:

JSON
{
  "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

ArgumentTypeRequiredDescription
idID (ocl_…)yes–
  • Read-only: changes nothing; idempotency_key is ignored.

Errors

Example

An app

Arguments:

JSON
{
  "id": "ocl_01m4ttwd3veh9apbewxheq5xha"
}

Result output:

JSON
{
  "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

ArgumentTypeRequiredDescription
idID (ocl_…)yes–
  • Read-only: changes nothing; idempotency_key is ignored.

Errors

Example

Where an app is installed

Arguments:

JSON
{
  "id": "ocl_01m4ttwd3veh9apbewxheq5xha"
}

Result output:

JSON
{
  "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

ArgumentTypeRequiredDescription
limitintegerno(1–100)
starting_afterID (ocl_…)no–
statusone of active, disabledno–
  • Read-only: changes nothing; idempotency_key is ignored.

Errors

Examples

Registered apps

Arguments:

JSON
{}

Result output:

JSON
{
  "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:

JSON
{
  "limit": 1
}

Result output:

JSON
{
  "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

ArgumentTypeRequiredDescription
idID (ocl_…)yes–
  • Read-only: changes nothing; idempotency_key is ignored.

Errors

Example

An application waiting for review

Arguments:

JSON
{
  "id": "ocl_01m4ttwd3veh9apbewxheq5xha"
}

Result output:

JSON
{
  "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

ArgumentTypeRequiredDescription
idID (ocl_…)yes–
publisherobjectyes–
privacyPolicyUrlstringyesThe app's privacy policy (it becomes the app's privacyPolicyUrl). (at most 2000 characters)
questionnaireobjectyes–
scopeJustificationsarray of objectyes(at most 200 items)
idempotency_keystringnoA 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_runbooleannotrue: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false)
  • Idempotency: the result returns the idempotency_key used, 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: true checks permissions and rules and computes the result, then rolls everything back.

Errors

Example

Apply to list an accounting app

Arguments:

JSON
{
  "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:

JSON
{
  "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

ArgumentTypeRequiredDescription
idID (ocl_…)yes–
idempotency_keystringnoA 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_runbooleannotrue: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false)
  • Idempotency: the result returns the idempotency_key used, 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: true checks permissions and rules and computes the result, then rolls everything back.

Errors

Example

Withdraw a first application

Arguments:

JSON
{
  "id": "ocl_01m4ttwd3veh9apbewxheq5xha",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}

Result output:

JSON
{
  "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

ArgumentTypeRequiredDescription
idID (ocl_…)yes–
idempotency_keystringnoA 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_runbooleannotrue: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false)
  • Idempotency: the result returns the idempotency_key used, 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: true checks permissions and rules and computes the result, then rolls everything back.

Errors

Example

Rotate the secret

Arguments:

JSON
{
  "id": "ocl_01m4ttwd3veh9apbewxheq5xha",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}

Result output:

JSON
{
  "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

ArgumentTypeRequiredDescription
idID (ocl_…)yes–
namestringno(1–100 characters)
publisherstringno(1–100 characters)
logoUrlstring or nullno–
websiteUrlstring or nullno–
privacyPolicyUrlstring or nullno–
redirectUrisarray of stringno(1–10 items)
visibilityone of private, listedno–
scopesarray of one of 84 values, e.g. org:read, org:write, org:adminno(1–200 items)
dpopBoundAccessTokensbooleannoRequire DPoP for every token: a token request without a DPoP proof is refused (RFC 9449).
idempotency_keystringnoA 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_runbooleannotrue: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false)
  • Idempotency: the result returns the idempotency_key used, 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: true checks permissions and rules and computes the result, then rolls everything back.

Errors

Example

Add a redirect URI

Arguments:

JSON
{
  "id": "ocl_01m4ttwd3veh9apbewxheq5xha",
  "redirectUris": ["https://ledgerly.example/oauth/callback", "https://eu.ledgerly.example/oauth/callback"],
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}

Result output:

JSON
{
  "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"
}