Customer operations
Inspect customers, contain access, migrate subscriptions, support roles, manage proposals and promo codes, and operate preview personas safely.
Inspect customers, contain access, migrate subscriptions, support roles, manage proposals and promo codes, and operate preview personas safely.
Customer records, subscriptions, credentials, role assignments, proposals,
promo codes, and test personas are platform-owned operational state. Plans,
route grants, limits, and the raw product-permission vocabulary remain
repository-owned contract state under business/. Each subscribing
organization owns its product roles: it composes them from that raw vocabulary,
chooses its default, and assigns roles or direct grants to its members. The
Business SDK does not define customer roles.
Use structured output for every write and read the affected state back. A successful request means the platform accepted that operation; it does not prove that a later asynchronous workflow has converged.
farthershore consumer list acme --format json
farthershore consumer list acme --env preview --format json
The response is capped at 500 customers. Use its id as the subscriberId in
subsequent commands. Do not substitute an email, external identity, owner
organization, or subscription id. The response also exposes status, current
plan, onLatestPlan, Managed RBAC roles, members, and each member's assigned or
stale role keys.
Block is containment and revokes active API keys. There is currently no CLI unblock command, so treat it as durable until the platform exposes an explicit restoration path:
farthershore consumer block acme <subscriberId> --yes --format json
farthershore consumer list acme --format json
Blocking suspends the customer and revokes active API keys. It is idempotent;
the response tells you whether the customer was already blocked and how many
keys were revoked. Read the customer list back and require SUSPENDED. This
proves control-plane state, not edge propagation: verify the compromised
credential is denied at the gateway before calling containment complete. A
downstream propagation failure can occur after the blocked response; retain
request and audit evidence and escalate if access remains possible.
Remove is destructive and has no restore command:
farthershore consumer remove acme <subscriberId> --yes --format json
farthershore consumer list acme --format json
Confirm the business and subscriber id immediately before running it. Removal
tears down subscriptions, credentials, and related live state. Require explicit
approval, preserve any required audit evidence first, and verify that the id no
longer appears afterward when it was present in the bounded pre-write list.
Because consumer list is capped at 500, absence from that list alone is not
proof of removal; retain the structured removal response and audit evidence.
Move one customer to the active head of its existing plan lineage:
farthershore consumer migrate-latest acme <subscriberId> \
--idempotency-key <persisted-consumer-migration-attempt-key> --format json
farthershore consumer list acme --format json
The command does not choose an unrelated plan. It may report that the customer is already at the head. Commercial-release cohort migration is deferred until the post-launch migrate-to-latest workflow.
Managed-RBAC enforcement needs two flags: the product-wide one
(farthershore business rbac enable) and the per-subscriber one. The
subscribing organization owns the second flag and normally sets it at its
portal's Settings → Team page (/settings/team); these commands are the
builder-plane equivalent, over the same service layer:
farthershore consumer list acme --format json # find the subscriber id
farthershore consumer rbac enable acme <subscriberId> \
--default-role reader --format json
farthershore consumer rbac disable acme <subscriberId> --format json
rbac.enabled on the subscriber's consumer list row is the read-back. With
the product flag off every call answers 400 RBAC_NOT_ENABLED_BY_PRODUCT; when
the customer organization has mandatory change control on, a direct
enable/disable answers 409 GOVERNED_BY_CHANGE_SET and must go through
proposal create instead. Disabling is an access expansion — permission
resolution can return ['*'] — not a repair for one denied member.
Support path over live customer state; the customer organization is still the owner of its role definitions.
farthershore consumer rbac roles list acme <subscriberId> --format json
farthershore consumer rbac roles create acme <subscriberId> support \
--name "Support" --permissions "tickets:read,tickets:write" --format json
farthershore consumer rbac roles update acme <subscriberId> support \
--permissions "tickets:read" --format json
farthershore consumer rbac roles delete acme <subscriberId> support --yes
--permissions is set-replace: the list becomes the role's whole grant.
Permissions must come from the business's derived catalog
(400 UNKNOWN_PERMISSION otherwise), and the reserved key owner can never be
created, edited, or deleted (409 OWNER_ROLE_IMMUTABLE) — owners always hold
every permission. Deleting a role strips it from every member and credential
that holds it and clears it as the organization default, so read the role list
and consumer list members back afterward. Role edits republish live-bound key
claims asynchronously: retest EXISTING credentials once the change reaches the
edge, not only newly issued ones. When the customer must consent to the change,
use proposal create rather than these direct writes.
This builder CLI command is a support override over live customer state, not the normal ownership path. Subscriber owners/admins create roles, choose defaults, and assign members through their customer access-control surface. Do not use the support override to preconfigure a persona workspace or to make the builder the author of customer roles.
First inspect the accepted role vocabulary and the current assignment:
farthershore business rbac acme --format json
farthershore consumer list acme --format json
Then replace the member's complete assignment:
farthershore consumer rbac assign acme <subscriberId> <userExternalId> \
--roles admin,analyst \
--format json
farthershore consumer rbac assign acme <subscriberId> <userExternalId> \
--roles "" \
--format json
This is set-replace, not additive. An empty string clears the assignment. Role
keys must already exist in the customer organization's accepted role set. Read
the returned membership first, then read consumer list back when the customer
is in its bounded result and compare members[].businessRoleKeys exactly; stale
keys grant nothing and should be investigated.
An agent can create, simulate, and inspect a governed ChangeSet. Approval and application are separate customer-organization decisions.
farthershore proposal create acme <subscriberId> \
--intent "Replace the support role permissions" \
--operations '[{"type":"update_role_permissions","schemaVersion":1,"target":{"resourceType":"subscriber_business_role","resourceId":"support"},"payload":{"permissions":["tickets:read"]}}]' \
--idempotency-key proposal-support-v1 \
--format json
farthershore proposal preview acme <subscriberId> <changeSetId> --format json
farthershore proposal get acme <subscriberId> <changeSetId> --format json
farthershore proposal list acme <subscriberId> --format json
Treat proposal preview as a write: it stores the simulation but does not apply
the operations. Read the ChangeSet after preview and report its risk,
simulation, approvals, and status. Do not represent a proposal or approval as
applied state; only the later status and customer state prove that.
Promo codes are live checkout state, not Business program declarations.
farthershore promo-code list acme --format json
farthershore promo-code create acme \
--code LAUNCH25 \
--kind percent_off \
--percent 25 \
--duration-months 3 \
--plan <planId> \
--idempotency-key <persisted-promo-code-create-attempt-key> \
--format json
farthershore promo-code list acme --format json
Kinds are percent_off, amount_off, and free_months. Use
--amount-cents for a fixed amount; free_months accepts no amount flag.
Omitting --plan on create targets all launch plans. A spec update rotates the
provider-side coupon, so promo-code update requires the complete kind,
duration, and amount specification rather than a partial economic edit.
farthershore promo-code archive acme <promoCodeId> --format json
farthershore promo-code reactivate acme <promoCodeId> --format json
farthershore promo-code list acme --format json
Always read the list back and verify status, applicability window, plan scope, and redemption cap. Archiving prevents future use; it does not rewrite prior redemptions.
Personas exist only for environments configured with the test-persona customer authentication strategy. They are temporary users in a real subscriber-owned workspace and therefore exercise the same account membership, product-role, token-minting, and gateway authorization paths as human users.
# The first persona creates the temporary subscriber workspace and must own it.
farthershore persona bootstrap acme --env preview --plan starter --format json --idempotency-key <persisted-persona-bootstrap-attempt-key>
farthershore persona list acme --env preview --format json
# Sign the owner persona into the hosted portal. In that customer session,
# enable subscriber RBAC, compose roles from the live permission catalog,
# and choose a default.
farthershore persona login acme <ownerPersonaId> --env preview --format json
# Add another temporary user to the same subscriber. Omit --role to use the
# subscriber's default, or name existing subscriber-owned role keys explicitly.
farthershore persona bootstrap acme --env preview --plan starter \
--subscriber-id <subscriberId> --account-role member \
--role viewer analyst \
--format json --idempotency-key <persisted-member-bootstrap-attempt-key>
farthershore persona login acme <memberPersonaId> --env preview --format json
farthershore persona rotate acme <personaId> --env preview --format json --idempotency-key <persisted-persona-rotate-attempt-key>
farthershore persona delete acme <personaId> --env preview --format json
Bootstrap and rotate return a test key once. Do not log or commit it. Browser
login opens the platform-owned /persona-sign-in path. Its 60-second,
single-use, host-bound handoff is fragment-only until that same-origin page
exchanges it for a server-owned HttpOnly cookie; the portal bundle never sees a
bearer, access key, or token-bearing URL. The first persona defaults to account owner;
personas joining its stable subscriberId default to account member. Account
roles govern workspace administration and are separate from subscriber-authored
product roles. Enabling RBAC creates no product roles and selects no default.
To exercise a frontend checkout locally as one of these personas — real environment, hot reload, signed in through the same server-owned cookie — run the live dev server (CLI 0.33.5+):
farthershore frontend dev --live --business acme --env preview --persona <personaId> --format json
See
Local live preview as a persona.
Prove a permission change with both personas: the member's constrained request
must receive the gateway's typed permission_denied while the owner's is
forwarded. Deleting a persona or its environment revokes its sessions and any
local preview lease it held.
New persona workspaces do not auto-enable the subscriber setting; that choice
belongs to the owner in the customer access-control surface.
Applying a new commit to the environment branch flashes its compiled contract
and invalidates the old environment subscriptions and plan ids. Persona
identities survive in a detached state: run persona login with the same id,
then choose a plan from the newly compiled branch contract. The portal must not
offer or recover a production plan, and an old bookmarked plan id must be
treated as unavailable rather than substituted. The old raw test key remains
invalid after the flash. Once the persona is reattached, use persona rotate
to mint a replacement gateway credential; the persona's product-role bindings
and explicit scope ceiling carry forward without widening.
Deletion revokes the persona credential and browser access before removing its
test-owned user and membership. If it was the last persona, the disposable
subscriber organization is removed too; otherwise ownership is transferred to
a remaining persona when necessary. Environment deletion performs the same
Core identity cleanup and also removes edge/runtime state, the hosted route, and
the managed environment branch. Verify persona operations with persona list
and with actual allowed and denied preview requests.
farthershore audit-log business-list <organizationId> <businessId> --format json when you need actor and decision evidence.