ARFC-1015: Resource Representation for Write Operations
Status
| Draft | Date: 2026-08-17 | Version: 0.1 |
Abstract
This ARFC proposes a general convention for the Codermerlin Academy API: an operation that creates a resource, changes a resource, or establishes an association between resources returns that resource’s (or association’s) current representation in the response envelope, in the same shape a client receives from reading it directly. Today, a specific set of write operations return only a bare acknowledgment, so an integrator must issue a separate read immediately after the write merely to learn what was created or changed. This proposal establishes the convention platform-wide and applies it to fifteen operations identified during a review of the current API surface. Consistent with the platform’s practice of never changing an already-shipped operation’s contract, each affected operation is introduced as a new version, and the version it replaces is deprecated under the platform’s existing deprecation lifecycle.
Table of Contents
- Introduction
- Motivation
- Specification
- 3.1 Core Principle
- 3.2 What “Same Representation” Means
- 3.3 Applicability
- 3.4 Versioning and Compatibility Approach
- 3.5 Affected Operations — Tier 1: Created or Changed Resources
- 3.6 Affected Operations — Tier 2: Associations
- 3.7 Affected Operations — Tier 3: Validated or Normalized Values
- 3.8 Error and Edge-Case Behavior
- Version 1 Scope
- Security Considerations
- Backward Compatibility
- References
- Author
1. Introduction
An integrator who creates, updates, or links a resource through the API needs to know the outcome of that action — its identifier, its current field values, or the state of the association just established. Across most of the API, a write operation already returns this outcome directly: the caller gets the resource back in the same response that confirms the write succeeded.
A specific set of operations does not follow this pattern today. They return a success acknowledgment with no resource data, so the caller must immediately issue a separate read to find out what it just did. This is most acute for operations that generate a new identifier the caller had no way to know in advance.
This ARFC establishes returning the affected resource’s representation as a standing platform convention, and applies it to the fifteen operations identified as not yet following it.
2. Motivation
Current State
The majority of create, update, and link operations in the API already return the resource they act on. A caller that creates a course, replaces a set of advertised skills, or writes an asset receives that resource’s current representation in the same response.
A smaller set of operations — spanning privileges, roles, realms, leagues, tags, and several membership and association endpoints — returns only a bare success acknowledgment. The response envelope is present, but it carries no usable data about the resource or association that was just created, changed, or linked.
Operations that are asynchronous by design (for example, a large asset upload or an AI generation job) are unaffected by this gap: they already return a poll target and status through the envelope’s asynchronous data, and that pattern already satisfies the intent of this proposal for operations of that kind.
Missing Capabilities
- An integrator building a “create” flow (a new privilege, a new role, a renamed realm, an updated league) must perform an extra read immediately after the write merely to obtain the identifier or current field values of what it just changed.
- For operations that mint a brand-new identifier — most notably creating the very first realm in a hierarchy — the caller has no independent way to discover that identifier except a follow-up read, and in the realm-creation case there is no practical prior identifier to read by.
- For operations where the platform may validate or normalize the caller’s input (for example, a typed tag value), the caller cannot confirm what was actually stored without a follow-up read.
- The inconsistency itself is a cost: an integrator cannot assume “writes return their resource” as a platform-wide rule, and must special-case the operations that do not.
Design Goals
- An operation that creates a resource, changes a resource, or establishes an association returns that resource’s or association’s current representation in the envelope’s data.
- The representation returned by a write matches the representation returned by the platform’s corresponding read operation for the same resource — one shape, learned once, valid everywhere it appears.
- No already-shipped operation’s request or response contract changes as a result of this proposal.
- Each affected operation is introduced as a new version; the version it replaces is deprecated under the platform’s existing deprecation lifecycle, with the usual advance notice before it stops serving.
- Establish this as a standing convention for the platform generally, not merely a fix applied to the fifteen operations enumerated here.
3. Specification
3.1 Core Principle
A write operation that creates a resource, changes a resource, or establishes an association between resources returns that resource’s (or that association’s) current representation in the envelope’s data. A caller can act on, or display, the outcome of a write without issuing a subsequent read.
3.2 What “Same Representation” Means
The representation a write returns is the same shape, with the same fields, that a client already receives from the platform’s read operation for that resource. A client that knows how to render a resource from a read response can render it from a write response with no additional logic.
sequenceDiagram
participant Client
participant API
rect rgb(235, 235, 235)
note over Client,API: Today, for the affected operations
Client->>API: POST create privilege
API-->>Client: 200 OK (envelope only, no data)
Client->>API: GET list privileges in realm
API-->>Client: 200 OK (privilege representation)
end
rect rgb(220, 235, 220)
note over Client,API: Under this proposal
Client->>API: POST create privilege (new version)
API-->>Client: 200 OK (envelope + created privilege representation)
end
Where a written resource does not have a dedicated single-item read operation of its own — an association nested under two other resources, for instance — the write returns the same representation used by the operation that lists that association today.
Where a resource’s read representation has itself evolved over time (for example, a representation that gained an additional descriptive field in a later version), the write returns the most current representation, not an earlier one, so that new integrations built against the augmented write are never handed a representation already superseded on the read side.
3.3 Applicability
This convention applies to:
- Operations that create a new resource
- Operations that update an existing resource
- Operations that establish an association between two existing resources (adding a member, linking a privilege, designating a default)
This convention does not apply to:
- Delete operations, which remove a resource rather than produce one to represent
- Operations that are already asynchronous today, which already satisfy the intent of this proposal through the poll target and status carried in the envelope’s asynchronous data
3.4 Versioning and Compatibility Approach
The platform does not change the request or response contract of an operation once it has shipped. Where this proposal augments an existing operation’s response, a new version of that operation is introduced, and the version it replaces is deprecated using the platform’s existing deprecation lifecycle: a deprecation date, a sunset date after which the replaced version stops accepting new integrations, and a termination date after which it stops serving entirely. Integrators are not required to migrate immediately — the replaced version continues to function, with standard advance notice, until its termination date.
Where an operation’s request shape already differs across two currently-shipped versions (this is the case for privilege creation and privilege update, described below), both existing versions are deprecated in favor of a single new version that carries the augmented response. No request-level capability already introduced by the newer of the two existing versions is changed or removed by this proposal — the new version’s request accepts everything the version it replaces accepted.
3.5 Affected Operations — Tier 1: Created or Changed Resources
These operations create a resource, or change a resource’s own fields. Each currently returns no usable resource data.
| Replaces | New Operation | Method | Path | Representation Matches |
|---|---|---|---|---|
createPrivilegeV1, createPrivilegeV2 |
createPrivilegeV3 |
POST | /v3/privileges/{realm-eid} |
The platform’s current privilege representation (as returned by listing privileges in a realm). Every privilege — whether or not the caller supplied an explicit predicate expression — has a resolvable predicate, so this representation applies uniformly. |
updatePrivilegeV1, updatePrivilegeV2 |
updatePrivilegeV3 |
PUT | /v3/privileges/{realm-eid}/{privilege-eid} |
Same as above. |
createRoleV1 |
createRoleV2 |
POST | /v2/roles/{realm-eid} |
The platform’s role representation (as returned by listing roles in a realm). |
updateRoleV1 |
updateRoleV2 |
PUT | /v2/roles/{realm-eid}/{role-eid} |
The platform’s role representation (as returned by reading a single role). |
updateRealmV1 |
updateRealmV2 |
PUT | /v2/realms/{realm-eid} |
The platform’s current realm representation (the same one returned by reading a realm today, which already superseded an earlier, non-enveloped realm representation). |
updateLeagueV1 |
updateLeagueV2 |
PATCH | /v2/leagues/{realm-eid}/{league-eid} |
The platform’s league representation (as returned by reading a single league). |
createTopmostRealmAndAssociatedAdminV1 |
createTopmostRealmAndAssociatedAdminV2 |
POST | /v2/platform/realms/0/create |
The platform’s current realm representation. This is the operation for which the gap matters most: it mints an institution’s very first realm, and today the caller has no prior identifier available to read that realm back by. |
3.6 Affected Operations — Tier 2: Associations
These operations link an existing resource to another existing resource. The caller already supplies both identifiers, so the value of the augmented response is confirming the association’s current state (for example, when it took effect) rather than revealing a new identifier.
| Replaces | New Operation | Method | Path | Representation Matches |
|---|---|---|---|---|
addRoleMemberV1 |
addRoleMemberV2 |
POST | /v2/roles/{realm-eid}/{role-eid}/members |
The representation used when listing a role’s members. |
addRealmMemberV1 |
addRealmMemberV2 |
POST | /v2/realms/{realm-eid}/members/{user-eid} |
The representation used when listing a realm’s members. |
addRolePrivilegeV1 |
addRolePrivilegeV2 |
POST | /v2/roles/{realm-eid}/{role-eid}/privileges/{privilege-eid} |
The representation used when listing a role’s privileges. |
createRealmDefaultRoleV1 |
createRealmDefaultRoleV2 |
POST | /v2/realms/{realm-eid}/default-roles/{role-eid} |
The representation used when listing a realm’s default roles. |
3.7 Affected Operations — Tier 3: Validated or Normalized Values
These operations accept a value the platform validates or normalizes against a declared type. Returning the stored representation lets the caller confirm what was actually recorded.
| Replaces | New Operation | Method | Path | Representation Matches |
|---|---|---|---|---|
setTypedTagForEntityV1 |
setTypedTagForEntityV2 |
PUT | /v2/tags/{realm-eid}/{source}/{entity-eid}/{typed-tag-name} |
The representation used when reading a single typed tag value for an entity. |
setTypedTagForEntityStandaloneV1 |
setTypedTagForEntityStandaloneV2 |
PUT | /v2/tags/{realm-eid}/{source}/{entity-eid}/{typed-tag-name} (standalone authorization variant) |
Same as above. |
The exact route shape for each new version may be adjusted during implementation to match existing API path conventions; the operations and representations above are the client-visible contract this ARFC proposes.
3.8 Error and Edge-Case Behavior
| Condition | Expected behavior |
|---|---|
| The write fails validation or authorization | No representation is returned; the operation responds with its existing error behavior, unchanged by this proposal |
| The written resource is immediately superseded by a concurrent change from another caller | The representation reflects the resource’s state as committed by this operation’s own write, not a later write that may have already occurred |
| The association already exists (for example, a role member who is already a member) | The operation’s existing idempotency or conflict behavior is unchanged; only the representation carried on a successful response is new |
4. Version 1 Scope
Included
- The general convention: writes that create, change, or associate a resource return that resource’s current representation, matching the platform’s read representation for it.
- The thirteen new operations listed in 3.5–3.7, covering all fifteen previously-identified gaps (two of the fifteen converge, for privilege creation and privilege update, onto a single new version each).
- Deprecation of the operation versions each new operation replaces, under the platform’s existing deprecation lifecycle.
Excluded
- Any change to delete-operation responses.
- Any change to the existing asynchronous-operation pattern (poll target and status via the envelope’s asynchronous data), which already satisfies this proposal’s intent for that class of operation.
- Retrofitting this convention onto operations that already follow it — the great majority of the API’s write operations require no change.
- Any change to an already-shipped operation’s request or response contract. Every affected capability is delivered exclusively through a new operation version.
5. Security Considerations
Authorization Parity with Reads
A write’s augmented response is authorized identically to the platform’s read operation for the same resource: a caller only sees, in a write response, fields it would already be permitted to see by reading that resource directly. This proposal grants no caller visibility into any field beyond what the corresponding read already exposes to them.
No New Privilege Actions
This proposal introduces no new privilege actions and changes no existing authorization check. Every new operation version enforces the same privilege requirements as the version it replaces.
Rate Limiting
The new operation versions carry the same rate-limiting behavior as the versions they replace.
6. Backward Compatibility
No already-shipped operation’s request or response contract changes. Every operation identified in this ARFC continues to serve exactly as it does today through the version it currently exposes, until that version’s termination date under the standard deprecation lifecycle.
Integrators are not required to migrate to a new version to keep their existing integrations working. New integrations, and existing integrators who want to remove a follow-up read from their write flows, are expected to adopt the new versions once available.
No migration of existing data or resources is required. This proposal changes only what a response contains, not what a request accepts or what the underlying resource is.
7. References
Related Platform Documentation
8. Author
ĀYŌDÈ Development Team Codermerlin Academy Architecture