API Explorer

On Behalf Of

An application acting for a User, such as an AI agent, calls OBP with a Consent and authenticates as a consent user, an identity OBP creates for that Consent. user_id is the caller (the consent user); on_behalf_of_user_id is the User who granted the Consent. OBP derives the latter from the Consent, never from the request.

Rows stored against a consent user would end with its Consent, so each column that stores a user id has a policy for calls made by a consent user:

  • the caller: store the consent user (audit records, and the Consent's own permissions).
  • the User acted for: store the User who granted the Consent. Some tables keep both ids; see Note.
  • refused: a consent user may not do this (create a Consent, a Consumer or a token).

A request naming a consent user as its target User gets 400 InvalidUserId. v7.0.0 responses that show both ids use user_id and on_behalf_of_user_id, e.g. Dynamic Entity record metadata.

Columns that store a user id

Today is what this instance stores now: 18 of the 75 columns whose policy is "the User acted for" or "refused" apply it. "Not written by a consent user" means no endpoint writes that column with a consent user's id.

Record Column Policy Today Note
AccountAccess user_fk the caller the caller views copied from the consent JWT each request; has lifecycle GC
Entitlement mUserId the caller the caller only when createdByProcess == consent_user: the consent engine copying the consent's own scope
Entitlement mGrantedByUserId the caller the caller audit: who granted
UserLocks UserId the caller the caller lock the authenticated user
ExpectedChallengeAnswer ExpectedUserId the caller the caller consent and signing-basket authorisation: the caller IS the person authorising, so the challenge is theirs
ExpectedChallengeAnswer ExpectedUserId the User acted for the User acted for payment SCA: the challenge belongs to the human whose money moves, never to the agent that started the payment
ChatMessage SenderUserId the caller the caller sender = the authenticated user is truthful
Metric userId the caller the caller record both: on-behalf-of via consent_reference_id at read time
MetricArchive userId the caller the caller as Metric_UserId
ConnectorTrace userId the caller the caller as Metric_UserId
DynamicDataAccess GrantedBy the caller the caller audit: who granted
AuthUser user the caller the caller login row -> its own ResourceUser; not attribution
OpenIDConnectToken AuthUserPrimaryKey the caller the caller token belongs to the login; not attribution
UserRefreshes mUserId the caller the caller operational: refresh of the authenticated user's own account list
PlatformApp MarkedByUserId the caller the caller audit: who marked the Consumer as a platform app
ConsumerAccountAccess GrantedByUserId the caller the caller audit: who gave the Consumer access to the view, as Entitlement_GrantedByUserId
GroupMembership CreatedByUserId the caller the caller audit: who added the member, as Entitlement_GrantedByUserId
AssetStatusHistory ChangedByUserId the caller the caller audit: who changed the asset's status, the question a regulator asks
RetentionPolicy CreatedByUserId the caller the caller audit: who created the retention policy
RetentionHold PlacedByUserId, ReleasedByUserId the caller the caller audit: who placed, and who released, a legal hold
RetentionRun RequestedByUserId, ApprovedByUserId the caller the caller audit: who requested a run and who approved it; the two must differ, which is only meaningful for the users who actually acted
RetentionArchive OwnerUserId the caller the caller copied from the archived resource as it was stored there; never written from the caller
RetentionRunItem OwnerUserId the caller the caller disposal register: copied from the disposed resource as it was stored there; never written from the caller
TransactionRequest mUserId, mOnBehalfOfUserId the User acted for the User acted for record both: mUserId = userId, mOnBehalfOfUserId = onBehalfOfUserId
Entitlement mUserId the User acted for the User acted for the role holder; the consent-engine case is Entitlement_UserId_ConsentScope
GroupMembership UserId the User acted for not written by a consent user the member, who holds the Roles the Group grants; as Entitlement_UserId
AccountHolders user the User acted for the User acted for the human holds the account; one held by a per-consent identity strands when the consent dies
UserCustomerLink mUserId the User acted for the User acted for a Customer is linked to a human; a link on an agent identity dies with its Consent
AccountApplication mUserId the User acted for the caller explicit target: user_id comes from the request and is guarded at the endpoint, so the provider redirect is unreachable -- see ON_BEHALF_OF_USER_ID_PLAN.md row 14
AccountAccessRequest RequestorUserId the User acted for the caller who asked for access; the request outlives the session it was made in
AccountAccessRequest TargetUserId the User acted for not written by a consent user explicit target: a consent user named here is rejected at the endpoint
AccountAccessRequest CheckerUserId the User acted for the caller who approved; maker/checker evidence has to name a human
DynamicChangeRequest RequestorUserId the User acted for the caller maker of a dynamic-code change
DynamicChangeRequest CheckerUserId the User acted for the caller checker; must differ from the requestor
EntitlementRequest mUserId the User acted for the caller who asked for the role; the grant that follows lands on a human
UserScope mUserId the User acted for the caller the scope holder
ApiCollection UserId the User acted for the caller the user's own saved collection, created through POST /my/api-collections
UserAttribute UserId the User acted for the caller the user's own attribute, created through the /my/ endpoints
UserAgreement UserId the User acted for the caller the user's own acceptance of terms
UserInitAction UserId the User acted for the caller the user's own onboarding action
UserAuthContext mUserId the User acted for the caller consent copies the on-behalf-of user's contexts into ConsentAuthContext separately
UserAuthContextUpdate mUserId the User acted for the caller as UserAuthContext_UserId
DynamicEntity UserId the User acted for the User acted for the definition's creator; a definition outlives the Consent that created it
DynamicData UserId the User acted for the User acted for personal rows, and the one reference where the redirect MUST be symmetric: MapppedDynamicDataProvider resolves on save/update/get/delete alike, because a row keyed by this column on both sides is otherwise written by an agent and then invisible to it
DynamicData CreatedByUserId, CreatedByOnBehalfOfUserId the User acted for the User acted for record both: who made the call that created the record, agent included, and the person it was made for; published in the v7.0.0 record metadata
DynamicData UpdatedByUserId, UpdatedByOnBehalfOfUserId the User acted for the User acted for record both: as DynamicData_CreatedByUserId, for the call that last saved the record
DynamicDataAccess UserId the User acted for the caller row-level ACL. Deliberately still on the consent user today, because the bootstrap grant and the allows check have to agree with each other -- rows strand, nothing leaks. A later Phase 2 row; see the plan
DynamicEndpoint UserId the User acted for the caller the dynamic endpoint's creator; outlives the Consent
DynamicResourceDoc CreatedByUserId, UpdatedByUserId the User acted for the caller and UpdatedByUserId; a dynamic artefact outlives the Consent that created it
DynamicMessageDoc CreatedByUserId, UpdatedByUserId the User acted for the caller and UpdatedByUserId; a dynamic artefact outlives the Consent that created it
ConnectorMethod CreatedByUserId, UpdatedByUserId the User acted for the caller and UpdatedByUserId; a dynamic artefact outlives the Consent that created it
AbacRule CreatedByUserId, UpdatedByUserId the User acted for the caller and UpdatedByUserId; an access rule outlives the Consent that created it
Counterparty mCreatedByUserId, mCreatedByOnBehalfOfUserId the User acted for the User acted for record both: a counterparty controls where money may be sent, so the actor stays on mCreatedByUserId (and is published as created_by_user_id) while the human goes to mCreatedByOnBehalfOfUserId
CounterpartyWhereTag user the User acted for the caller who tagged the counterparty's location
ApiProductSubscription CreatedByUserId the User acted for the caller a subscription outlives the Consent that took it out
DynamicGlossaryItem CreatedByUserId the User acted for the caller outlives the Consent that created it
DomainApi CreatedByUserId the User acted for the User acted for the Domain API's creator; a published API outlives the Consent that created it
DynamicEntityWebhook CreatedByUserId, CreatedByOnBehalfOfUserId the User acted for the User acted for record both, like Counterparty: a webhook sends data out of OBP and outlives the Consent that made it, so the agent stays on CreatedByUserId (published as created_by_user_id) and the person it acted for goes to CreatedByOnBehalfOfUserId
AccountCreation CreatedByUserId, CreatedByOnBehalfOfUserId the User acted for the User acted for record both, like Counterparty: who made the call that created the account, agent included, and the person it was made for; published in the v7.0.0 Create Account metadata. The holder is AccountHolders_User
Bank CreatedByUserId the User acted for the User acted for creator grant already resolved at the endpoint
SelfServiceBank CreatedByUserId the User acted for the caller as Bank_CreatedByUserId; the Create My Bank quota counts these rows per creator
Tenant CreatedByUserId the User acted for the caller outlives the Consent that created it
Asset CreatedByUserId the User acted for the User acted for outlives the Consent that created it, as RoutingScheme_CreatedByUserId; seeded rows hold system:asset-seed
File UserId, OnBehalfOfUserId the User acted for the User acted for record both: UserId = who uploaded (the agent), OnBehalfOfUserId = the human, who owns the file
File ScanStatusUpdatedByUserId the caller the caller audit: who set the scan status, the scanner or the staff member who decided
FileAttachment UserId, OnBehalfOfUserId the User acted for the User acted for record both: UserId = who attached (the agent), OnBehalfOfUserId = the human
FileAccess GrantedByUserId, OnBehalfOfUserId the User acted for the User acted for record both: GrantedByUserId = who granted (the agent), OnBehalfOfUserId = the file's owner it acted for
FileAccess GranteeUserId the User acted for not written by a consent user explicit target: the User given access is named in the request; access given to a consent user would die with its Consent
PayeeLookup CreatedByUserId the User acted for the caller outlives the Consent that created it
RoutingScheme CreatedByUserId the User acted for the caller outlives the Consent that created it
UtilityPaymentCallback CreatedByUserId the User acted for the caller outlives the Consent that created it
StandingOrder UserId the User acted for the caller the payer; a standing order keeps executing long after any Consent expires
DirectDebit UserId the User acted for the caller the payer; a mandate keeps executing long after any Consent expires
Mandate CreatedByUserId, UpdatedByUserId the User acted for the caller and UpdatedByUserId; a mandate authorises payment and outlives the Consent that created it
SignatoryPanel UserIds the User acted for the caller list of user ids
AccountWebhook mCreatedByUserId the User acted for the caller published as created_by_user_id on v3.1.0 and v4.0.0; nothing reads it as an ownership key, so the human is not locked out -- but a webhook outlives the Consent that made it and keeps sending account events, so the human behind it should be recorded. Record-both, like Counterparty. See todo/webhook_attribution.md
SystemAccountNotificationWebhook CreatedByUserId the User acted for the caller as AccountWebhook_CreatedByUserId
BankAccountNotificationWebhook CreatedByUserId the User acted for the caller as AccountWebhook_CreatedByUserId
ChatRoom CreatedByUserId the User acted for the caller Portal chat: a human's room
Participant UserId the User acted for the caller the person in the room
Reaction UserId the User acted for the caller the person who reacted
ChatEmailDigestState UserId the User acted for the caller the person the digest is for
ChatMessage MentionedUserIds the User acted for the caller explicit targets, humans by construction
CrmEvent mUserId the User acted for the caller the user the event concerns
KycCheck user the User acted for the caller the customer's user
KycCheck mStaffUserId the User acted for the caller staff = human operator
KycDocument user the User acted for the caller the customer's user
KycStatus user the User acted for the caller the customer's user
SocialMedia user the User acted for the caller the customer's user
CustomerMessage user the User acted for the caller the customer's user
Meeting mCustomerUserId the User acted for the caller the customer side of the meeting
Meeting mStaffUserId the User acted for the caller staff = human operator
Tag user the User acted for the caller the author of the annotation; it outlives the Consent
WhereTag user the User acted for the caller the author of the annotation; it outlives the Consent
TransactionImage user the User acted for the caller the author of the annotation; it outlives the Consent
Consent mUserId refused the caller (not refused) a consent user creating a consent = nested delegation
Consumer createdByUserId refused the caller (not refused) credentials outlive the consent
Token userForeignKey refused the caller (not refused) credentials outlive the consent