> ## Documentation Index
> Fetch the complete documentation index at: https://docs.base44.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Check ownership transfer

> <Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Checks whether an ownership transfer invitation can be sent to an email address, and lists what would transfer with the app. Nothing is sent or changed.

Read `can_send` for the answer. A problem such as an app that already has a pending transfer comes back in `blockers` rather than as an error, and the same problem stops [Start ownership transfer](/api-reference/start-ownership-transfer). A recipient without a Base44 account yet is only a warning, since they can sign up before they accept.

Use `transfer_package` to choose the `integration_decisions` and `secret_decisions` you send with the invitation.

You need to own the app or be an owner or admin of its workspace. A workspace admin who doesn't own the app also needs an Enterprise workspace or approved partner status. An invitation to someone outside the workspace needs that status too, and only workspace owners and admins can send one. The workspace's transfer policy can narrow that to owners, or turn it off. If you can't manage ownership transfers for this app, the result has a `NOT_APP_OWNER` blocker. For an invitation outside the workspace, a missing Enterprise or partner status is rejected instead.

This is limited to 60 requests a minute per workspace. Some workspaces have a different limit.

<Note>This endpoint accepts a personal API key. A read-only key is refused, and workspace API keys are not accepted.</Note>



## OpenAPI

````yaml /developers/references/app-management/app-management-openapi.json post /api/apps/{app_id}/ownership/transfer/preflight
openapi: 3.1.0
info:
  title: Base44 App Management API
  version: 1.0.0
servers:
  - url: https://app.base44.com
security:
  - PersonalAccessTokenAuth: []
paths:
  /api/apps/{app_id}/ownership/transfer/preflight:
    post:
      summary: Check ownership transfer
      description: >-
        <Info>This API is in beta. Endpoints, fields, and behavior may still
        change, so avoid depending on it in production.</Info>


        Checks whether an ownership transfer invitation can be sent to an email
        address, and lists what would transfer with the app. Nothing is sent or
        changed.


        Read `can_send` for the answer. A problem such as an app that already
        has a pending transfer comes back in `blockers` rather than as an error,
        and the same problem stops [Start ownership
        transfer](/api-reference/start-ownership-transfer). A recipient without
        a Base44 account yet is only a warning, since they can sign up before
        they accept.


        Use `transfer_package` to choose the `integration_decisions` and
        `secret_decisions` you send with the invitation.


        You need to own the app or be an owner or admin of its workspace. A
        workspace admin who doesn't own the app also needs an Enterprise
        workspace or approved partner status. An invitation to someone outside
        the workspace needs that status too, and only workspace owners and
        admins can send one. The workspace's transfer policy can narrow that to
        owners, or turn it off. If you can't manage ownership transfers for this
        app, the result has a `NOT_APP_OWNER` blocker. For an invitation outside
        the workspace, a missing Enterprise or partner status is rejected
        instead.


        This is limited to 60 requests a minute per workspace. Some workspaces
        have a different limit.


        <Note>This endpoint accepts a personal API key. A read-only key is
        refused, and workspace API keys are not accepted.</Note>
      operationId: preflight_transfer_api_apps__app_id__ownership_transfer_preflight_post
      parameters:
        - name: app_id
          in: path
          required: true
          schema:
            type: string
            description: ID of the app.
            title: App Id
          description: ID of the app.
          example: 6820f3a4e7b91d003c45a1f2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PreflightTransferRequest'
      responses:
        '200':
          description: The result of the check.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OwnershipTransferPreflightResponse'
        '401':
          description: Missing or invalid credentials.
        '403':
          description: >-
            Your API key is read-only, you used a workspace API key, or the
            app's workspace isn't Enterprise and you aren't an approved partner,
            for an invitation outside the workspace.
        '404':
          description: >-
            The app doesn't exist, or it's outside the workspace or apps your
            API key is scoped to.
        '422':
          description: >-
            `new_owner_email` is missing, or `destination` isn't
            `same_workspace` or `external_email`.
        '429':
          description: Rate limit reached. Retry later.
components:
  schemas:
    PreflightTransferRequest:
      properties:
        new_owner_email:
          type: string
          title: New Owner Email
          description: >-
            Email address of the new owner. An invalid address comes back as a
            blocker.
          example: jane@acme.com
        destination:
          anyOf:
            - type: string
              enum:
                - same_workspace
                - external_email
            - type: 'null'
          title: Destination
          description: >-
            Either `same_workspace`, to invite an owner, admin, editor, or
            member of the app's workspace, or `external_email`, to invite
            anyone. Defaults to `external_email`.
          example: external_email
      type: object
      required:
        - new_owner_email
      title: PreflightTransferRequest
      description: The ownership transfer invitation to check.
    OwnershipTransferPreflightResponse:
      properties:
        can_send:
          type: boolean
          title: Can Send
          description: >-
            Whether the invitation can be sent (`true`) or a blocker stops it
            (`false`).
          example: true
        recipient_status:
          type: string
          title: Recipient Status
          description: >-
            Summary of the result. Either `can_send`,
            `recipient_must_sign_in_first` when the recipient doesn't have a
            Base44 account yet but the invitation can still be sent,
            `invalid_email`, or `blocked_policy` for any other blocker.
          example: can_send
        blockers:
          items:
            $ref: '#/components/schemas/OwnershipTransferPreflightIssue'
          type: array
          title: Blockers
          description: >-
            Problems that stop the invitation from being sent. It's empty when
            `can_send` is `true`.
        warnings:
          items:
            $ref: '#/components/schemas/OwnershipTransferPreflightIssue'
          type: array
          title: Warnings
          description: Things to know that don't stop the invitation.
        transfer_package:
          anyOf:
            - $ref: '#/components/schemas/OwnershipTransferPackageSummary'
            - type: 'null'
          description: >-
            What would transfer with the app, or `null` when you can't manage
            ownership transfers for this app.
      type: object
      required:
        - can_send
        - recipient_status
      title: OwnershipTransferPreflightResponse
      description: >-
        Whether an ownership transfer invitation can be sent, and what would
        transfer with the app.
    OwnershipTransferPreflightIssue:
      properties:
        code:
          type: string
          title: Code
          description: >-
            Machine-readable reason, for example `TRANSFER_ALREADY_PENDING`,
            `CANNOT_TRANSFER_TO_SELF`, `NOT_APP_OWNER`, or
            `NEW_OWNER_NOT_FOUND`.
          example: TRANSFER_ALREADY_PENDING
        severity:
          type: string
          title: Severity
          description: Either `blocker` or `warning`.
          example: blocker
        message:
          type: string
          title: Message
          description: Human-readable explanation of the issue.
          example: A pending ownership transfer already exists for Acme CRM.
        field:
          anyOf:
            - type: string
            - type: 'null'
          title: Field
          description: >-
            Name of the input or app field the issue is about, or `null` when it
            isn't about one.
          example: new_owner_email
        details:
          additionalProperties: true
          type: object
          title: Details
          description: >-
            Extra information about the issue, as a JSON object. It's usually
            empty.
          example: {}
      type: object
      required:
        - code
        - severity
        - message
      title: OwnershipTransferPreflightIssue
      description: Machine-readable preflight blocker or warning.
    OwnershipTransferPackageSummary:
      properties:
        mode:
          type: string
          title: Mode
          description: How the app transfers, which is always `transfer_as_built`.
          default: transfer_as_built
          example: transfer_as_built
        app:
          $ref: '#/components/schemas/OwnershipTransferPreflightAppSummary'
          description: The app being transferred.
        recipient_email:
          type: string
          title: Recipient Email
          description: Email address the invitation would go to.
          example: jane@acme.com
        existing_account_required:
          type: boolean
          title: Existing Account Required
          description: >-
            Whether the recipient needs a Base44 account to accept, which is
            always `true`. They can sign up after the invitation is sent.
          default: true
          example: true
        target_workspace:
          type: string
          title: Target Workspace
          description: >-
            Where the app goes by default, which is always
            `recipient_personal_workspace`. The recipient can choose another
            workspace when they accept.
          default: recipient_personal_workspace
          example: recipient_personal_workspace
        active_integration_count:
          type: integer
          title: Active Integration Count
          description: Number of entries in `integrations`.
          default: 0
          example: 1
        integrations:
          items:
            $ref: '#/components/schemas/OwnershipTransferIntegrationSummary'
          type: array
          title: Integrations
          description: The app's connected integrations.
        secrets:
          items:
            $ref: '#/components/schemas/OwnershipTransferSecretSummary'
          type: array
          title: Secrets
          description: The app's secrets, without their values.
        has_google_ads_account:
          type: boolean
          title: Has Google Ads Account
          description: >-
            Whether the app has a Google Ads account that transfers with it
            (`true`) or not (`false`). The new owner has to set up its billing
            again, and any outstanding balance has to be settled before the
            recipient can accept.
          default: false
          example: false
      type: object
      required:
        - app
        - recipient_email
      title: OwnershipTransferPackageSummary
      description: Current v1 transfer package summary.
    OwnershipTransferPreflightAppSummary:
      properties:
        id:
          type: string
          title: Id
          description: ID of the app.
          example: 6820f3a4e7b91d003c45a1f2
        name:
          type: string
          title: Name
          description: Name of the app.
          example: Acme CRM
        source_workspace_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Source Workspace Id
          description: ID of the workspace the app is in now.
          example: 67f2c8e01a3b5d004e92d7a1
        source_owner_id:
          type: string
          title: Source Owner Id
          description: ID of the Base44 user who owns the app now.
          example: 6820f3a4e7b91d003c45a1f0
      type: object
      required:
        - id
        - name
        - source_owner_id
      title: OwnershipTransferPreflightAppSummary
      description: Safe app summary for sender-side transfer review.
    OwnershipTransferIntegrationSummary:
      properties:
        integration_type:
          type: string
          title: Integration Type
          description: >-
            Integration type. Send it as `integration_type` in
            `integration_decisions`.
          example: slack
        display_name:
          type: string
          title: Display Name
          description: Name of the integration as shown in the builder.
          example: Slack
        status:
          type: string
          title: Status
          description: Connection status. Either `active`, `expired`, or `connected`.
          example: active
        can_keep_connected:
          type: boolean
          title: Can Keep Connected
          description: >-
            Whether the integration can stay connected for the new owner
            (`true`), or always disconnects on acceptance because it uses the
            current owner's own account (`false`).
          default: true
          example: true
      type: object
      required:
        - integration_type
        - display_name
        - status
      title: OwnershipTransferIntegrationSummary
      description: Safe sender-facing integration summary.
    OwnershipTransferSecretSummary:
      properties:
        secret_name:
          type: string
          title: Secret Name
          description: >-
            Name of the app secret. Send it as `secret_name` in
            `secret_decisions`.
          example: STRIPE_API_KEY
        has_value:
          type: boolean
          title: Has Value
          description: >-
            Whether the secret has a value that can transfer with the app
            (`true`) or has none (`false`).
          default: true
          example: true
      type: object
      required:
        - secret_name
      title: OwnershipTransferSecretSummary
      description: Safe sender-facing secret summary. Values are never included.
  securitySchemes:
    PersonalAccessTokenAuth:
      type: http
      scheme: bearer
      description: 'Personal access token, sent as `Authorization: Bearer <token>`.'

````