Skip to content

Register an app

The apps.create operation: POST /api/v1/apps in the REST API and the apps_create tool in the MCP server.

POST/api/v1/apps

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.

At a glance

  • REST: POST /api/v1/apps
  • MCP tool: apps_create
  • Operation ID: apps.create
  • Scope: Organization
  • Permission: agents:admin
  • Risk tier: security
  • Dry run: Supported (send Ownrecord-Dry-Run: true)
  • Idempotency: Idempotency-Key required
  • Modes: Test and live
  • Authentication: API key with the agents:admin scope
  • Fresh sign-in: In the console, a sign-in within the last 15 minutes
  • Since: M10

Request

Body

A JSON object (Content-Type: application/json):

FieldTypeRequiredDescription
namestringYes
publisherstringYes
logoUrlstringNo
websiteUrlstringNo
privacyPolicyUrlstringNo
redirectUrisarray of stringYes
typeone of confidential, publicNo
visibilityone of private, listedNo
scopesarray of one of 84 values, e.g. org:read, org:write, org:admin (all in the OpenAPI document)Yes
dpopBoundAccessTokensbooleanNoRequire DPoP for every token: a token request without a DPoP proof is refused (RFC 9449).

Response

201 Created with a JSON object:

FieldTypeDescription
appobject
app.idocl_… ID
app.object"app"
app.ownerOrgIdorg_… ID
app.namestring
app.publisherstring
app.logoUrlstring or null
app.websiteUrlstring or null
app.privacyPolicyUrlstring or null
app.redirectUrisarray of string
app.typeone of confidential, public
app.visibilityone of private, listed
app.reviewobject
app.review.statusone of not_submitted, pending, changes_requested, approved, rejected
app.review.atstring or null
app.verifiedboolean
app.scopesarray of one of 84 values, e.g. org:read, org:write, org:admin (all in the OpenAPI document)
app.secretPrefixstring or null
app.dpopBoundAccessTokensboolean
app.statusone of active, disabled
app.suspensionobject or null
app.createdAtstring
app.updatedAtstring
credentialsobject
credentials.clientIdocl_… ID
credentials.clientSecretstring or null
credentials.tokenEndpointstring
credentials.authMethodone of none, client_secret_basic, client_secret_post, private_key_jwt

Examples

A listed accounting app

HTTP
POST /api/v1/apps
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/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"
  ]
}

Response (201):

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

Errors

CodeHTTP statusWhenWhat to do
validation_failed400A field is invalid, e.g. a redirect URI that isn't https or loopback.Fix the fields listed in details.issues and send the request again.
permission_denied403You can't manage the organization's apps.Ask an organization owner or admin for a role or scope that allows this action.
fresh_auth_required403Your last sign-in is older than 15 minutes.Sign in again, then repeat the action within 15 minutes.

It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, resource_not_found, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.