Skip to content

Submit an app for listing

The apps.listing.submit operation: POST /api/v1/apps/{id}/listing in the REST API and the apps_listing_submit tool in the MCP server.

POST/api/v1/apps/{id}/listing

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.

At a glance

  • REST: POST /api/v1/apps/{id}/listing
  • MCP tool: apps_listing_submit
  • Operation ID: apps.listing.submit
  • Scope: Organization
  • Permission: agents:admin
  • Risk tier: write
  • Dry run: Supported (send Ownrecord-Dry-Run: true)
  • Idempotency: Idempotency-Key required
  • Modes: Test and live
  • Authentication: API key with the agents:admin scope
  • Since: M13

Request

Path parameters

FieldTypeDescription
idocl_… ID

Body

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

FieldTypeRequiredDescription
publisherobjectYes
publisher.legalNamestringYesThe publisher's registered legal name, e.g. Ledgerly AI B.V.
publisher.countrystringYesWhere the publisher is registered (ISO 3166-1 alpha-2).
publisher.websiteUrlstringYesThe publisher's website.
publisher.contactobjectYesWho Ownrecord writes to about the listing and about incidents.
publisher.contact.namestringYes
publisher.contact.emailstringYes
privacyPolicyUrlstringYesThe app's privacy policy (it becomes the app's privacyPolicyUrl).
questionnaireobjectYes
questionnaire.versioninteger (at least 1)YesThe questionnaire version answered: the current one (apps.listing.get).
questionnaire.answersarray of objectYes
questionnaire.answers[].questionIdstringYes
questionnaire.answers[].answerone of yes, noNoFor yes/no questions.
questionnaire.answers[].detailsstringNoA written answer, or details of a yes/no answer.
scopeJustificationsarray of objectYes
scopeJustifications[].scopeone of 84 values, e.g. org:read, org:write, org:admin (all in the OpenAPI document)Yes
scopeJustifications[].justificationstringYesWhy the app needs this scope: what it reads or does with it.

Response

200 OK with a JSON object:

FieldTypeDescription
idocl_… ID
object"app_listing"
ownerOrgIdorg_… ID
namestring
publisherstring
visibilityone of private, listed
appStatusone of active, disabled
statusone of not_submitted, pending, changes_requested, approved, rejected
listedboolean
scopesarray of one of 84 values, e.g. org:read, org:write, org:admin (all in the OpenAPI document)
approvedScopesarray of one of 84 values, e.g. org:read, org:write, org:admin (all in the OpenAPI document) or null
pendingScopesarray of one of 84 values, e.g. org:read, org:write, org:admin (all in the OpenAPI document)
submittedAtdate-time or null
decidedAtdate-time or null
reasonstring or null
applicationobject or null
historyarray of object
history[].actionone of submitted, withdrawn, approved, rejected, changes_requested, scopes_added, identity_changed, unlisted
history[].atdate-time
history[].bystring or null
history[].reasonstring or null
history[].scopesarray of one of 84 values, e.g. org:read, org:write, org:admin (all in the OpenAPI document)
versioninteger

Examples

Apply to list an accounting app

HTTP
POST /api/v1/apps/ocl_01m4ttwd3veh9apbewxheq5xha/listing
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "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."
    }
  ]
}

Response (200):

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
}

Errors

CodeHTTP statusWhenWhat to do
app_listing_incomplete400A required question isn't answered, a scope isn't justified, or the questionnaire version isn't the current one.Answer every required question of the current security questionnaire, justify every scope the app asks for, and give a privacy policy URL. details.problems lists what's missing.
partner_terms_not_accepted409Your organization hasn't accepted the current Partner and App Terms.An owner or admin reads the Partner and App Terms at /legal/partners and accepts them under Developers → Apps (or with partner_terms.accept), then submits the application again.
oauth_client_disabled409The app is disabled.Register a new app, or create a new agent.
validation_failed400A field is invalid, e.g. a URL that isn't https.Fix the fields listed in details.issues and send the request again.
resource_not_found404Your organization registered no app with this ID.Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.
version_conflict409The app changed at the same moment; retry.Fetch the object again and retry the change.
permission_denied403You can't manage the organization's apps.Ask an organization owner or admin for a role or scope that allows this action.

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