Saltar al contenido

Transfer organization ownership

Este contenido aún no está disponible en su idioma.

Moving an organization to a different owner has no screen in the application. Organization owners cannot do it themselves, so it is an operator action, run either from the instance’s command line or through the Colonel API.

This page is the procedure. For what your users see, and what they will have read before they ask you, see Ownership and transfer.

  • Shell access to a configured application environment, run from the repository root, with the datastore reachable. The command boots the application.
  • The organization’s identifier. ORG accepts the organization’s public ID (extid), its internal ID (objid), a Stripe customer or subscription ID, or one of its custom-domain hostnames.
  • The incoming owner’s identifier. NEW_OWNER accepts an email address, a customer extid, or a Rodauth account ID.
  • The incoming owner must already be an active member of the organization. The transfer does not create the membership. One confirmation is not allowed to both create a membership and hand it ownership.
  • For the API route instead of the shell: the colonel role, and the organization’s name to type as confirmation.

bin/ots org ... and bin/ots organization ... are aliases for bin/ots organizations .... All three reach the same command.

Read the organization before changing it:

Terminal window
bin/ots organizations show ORG

If the incoming owner is not listed as an active member, add them. The add is also preview-by-default, so this is two commands:

Terminal window
bin/ots memberships add ORG CUSTOMER
bin/ots memberships add ORG CUSTOMER --apply

Attempting a transfer to a non-member exits non-zero and tells you to run memberships add first. It changes nothing.

Terminal window
bin/ots organizations transfer-ownership ORG NEW_OWNER

A bare run is a preview. It writes no organization or membership data, prints the plan, and exits. The plan names the current owner, the incoming owner, the role the outgoing owner will land on, and how many owner memberships would be demoted.

Read the demotion count. The data model permits more than one owner membership, and the transfer demotes every owner other than the incoming one. A count above one means the organization was already in a state worth understanding before you write to it.

A preview is recorded as an observation on the access trail rather than as a change.

Terminal window
bin/ots organizations transfer-ownership ORG NEW_OWNER --apply
Option Effect
--apply The write gate. -y, --yes and -f are aliases for it.
--dry-run Restates the default. It does not add a preview that a bare run lacks.
--demote-to ROLE The outgoing owner’s role afterwards: admin (the default) or member. owner is refused.
--json Machine-readable output. A non-success status still exits non-zero.

The write gate on this command is not a property of every command on the instance. Elsewhere in the CLI the same intent is spelled --run, --confirm or --repair, and some commands write with no gate at all. Check the CLI reference for the command in front of you rather than assuming this one’s spelling.

POST /api/colonel/organizations/:org_id/transfer-ownership

The request body takes new_owner and an optional demote_to. The route requires the colonel role, and the logic re-checks it. The confirmation it asks for is the organization’s name, not its ID.

This route has no preview. The shared operation defaults to a dry run, and this adapter pins that off, so the call applies as soon as the confirmation matches. An API call is not the equivalent of a bare CLI run.

  • The command prints the organization ID, the previous owner’s public ID (or (none)) and the new owner’s public ID, then the number of previous owners demoted and the role they were demoted to.
  • Exit status is 0.
  • bin/ots organizations show ORG lists the new owner with the owner role, and the previous owner with the role you chose.
  • bin/ots organizations doctor ORG reports no membership_role_sync finding.
  • The audit trail gains one organization.transfer_ownership event with a success result, plus one membership.set_role event per role change the transfer made. Three events for a routine transfer is the expected shape, not a double recording.

If the previous owner pointer referred to no live customer, the command says so and reports that it repaired it. That is a successful outcome, not a warning to act on.

If the named member is already the sole owner and the stored owner pointer already agrees, the command reports that and exits 0 without writing. A partial state, where the member has the owner role but the stored pointer disagrees, deliberately does not short-circuit: the transfer proceeds and repairs the pointer.

None of these are oversights, so do not plan around changing them.

  • The outgoing owner is not removed. They are demoted. There is no remove value for --demote-to. Removing them would break the account checks for anyone whose default organization points at this one.
  • Billing does not move. The Stripe customer, subscription, billing email and email hash are organization fields. The subscription already belongs to the organization rather than to the person who owned it.
  • The organization’s contact email is not changed. It is the organization’s identity in a uniqueness index, and changing it is separate work.
  • The record of who created the organization is never rewritten. It is immutable audit state. A consequence worth knowing: on a transferred organization the owner pointer and the creator record permanently disagree, so the housekeeping chore that reconciles them logs a warning for that organization indefinitely. That is expected.
  • Admins and members keep their roles. Only the two owner-side roles move.

Two refusals exit non-zero and change nothing:

Printed reason Meaning What to do
not an active member The incoming owner has no active membership. Run memberships add ORG CUSTOMER --apply, then transfer.
--demote-to must be one of The role you named is not a demotable role. Use admin or member.

Both are recorded on the audit trail as a failed attempt, and both print the same guidance whether you were previewing or applying.

The ordering is forced rather than chosen, and a partial failure only makes sense once you know it. The sole-owner guard refuses to demote the only owner, and at the start of a transfer the outgoing owner is the only owner. So the operation promotes the incoming owner first, then moves the stored owner pointer, then demotes the others.

Between the first and third steps the organization has two owner memberships. During that window bin/ots organizations doctor reports its membership_role_sync check as a finding, and that check is marked unrepairable, so the doctor will not fix it.

If a step after the promotion raises, the operation attempts a best-effort rollback before re-raising: it restores the stored owner pointer and the incoming owner’s previous role. Memberships that were already demoted stay demoted. The failure is audited, and the transfer is not recorded as a success.

Recovery is to re-run the transfer, not to run the doctor.

Terminal window
bin/ots organizations transfer-ownership ORG NEW_OWNER --apply

The operation is idempotent and demotes every other active owner, so a second applied run finishes the job. Keep --apply on the repeat: a bare rerun only previews, which will not close the window.

If the rollback itself fails, the command logs that separately and still reports the original failure. Read both lines, because the organization may be left in the two-owner state.

An owner membership with no backing customer

Section titled “An owner membership with no backing customer”

If an outgoing owner membership refers to a customer record that no longer exists, the transfer logs that it skipped that membership and continues. The remaining stale membership is for bin/ots organizations doctor to report; the transfer will not fabricate a customer in order to demote one.

  • There is no undo. To reverse a completed transfer, run a new transfer in the other direction. The previous owner must still be an active member for that to be accepted, which they will be, because a transfer demotes rather than removes them.
  • Re-running converges, but it does not restore the outgoing owner’s original role. Each run demotes to whatever --demote-to says on that run, defaulting to admin. If the outgoing owner was a plain member before a mistaken transfer, reversing the transfer will not put them back there.
  • A rolled-back failure writes no success event, so the audit trail is not a reliable record of whether a partial write survived. Check the organization with organizations show and organizations doctor rather than inferring its state from an exit status.