Revision
A revision is a record of a single change to a resource. It captures the merge patch that was submitted, a snapshot of the record with the change applied, and the lifecycle status of the change. Revisions make up the change ledger used for organization profile syncing: every write, whether a direct PATCH or a submitted POST /changes, records a revision, and GET /orgs/{orgId}/changes lists them newest first. A revision’s submission facts (its patch and source) never change, but its status can transition until it reaches a terminal state.
createdAt marks when the change was submitted and orders the ledger, while lastModifiedAt marks when the status last changed.
Revision
Section titled “Revision”The generic, untyped record of a change, where patch and snapshot are open objects. It is defined as a template, RevisionT<SnapshotT, PatchT>, that a concrete resource binds to its own snapshot and patch types (see OrgRevision).
| Property | Type | Required | Description |
|---|---|---|---|
| id | uuid | Yes | Globally unique id for the revision |
| status | RevisionStatus | Yes | The lifecycle status of the change |
| source | string | No | The source system the change came from |
| patch | unknown | No | The merge patch that was submitted |
| snapshot | unknown | No | A full snapshot of the record with the change applied |
| createdAt | utcDateTime | Yes | The timestamp (in UTC) at which the record was created. |
| lastModifiedAt | utcDateTime | Yes | The timestamp (in UTC) at which the record was last modified. |
Formats
Section titled “Formats”A JSON example of this model.
{ "id": "01912a8b-7c3d-7890-abcd-ef1234567890", "status": { "value": "accepted", "description": "The change was applied." }, "source": "grants.gov", "patch": { "mission": "To expand access to community health resources." }, "snapshot": { "id": "01912a8b-7c3d-7890-abcd-ef1234567890", "name": "Example Nonprofit", "mission": "To expand access to community health resources." }, "createdAt": "2026-06-20T14:30:00Z", "lastModifiedAt": "2026-06-20T14:30:00Z"}The JSON Schema for this model.
$schema: https://json-schema.org/draft/2020-12/schema$id: Revision.yamltype: objectproperties: id: $ref: uuid.yaml description: Globally unique id for the revision status: $ref: RevisionStatus.yaml description: The lifecycle status of the change source: type: string description: The source system the change came from patch: description: The merge patch that was submitted snapshot: description: A full snapshot of the record with the change applied createdAt: type: string format: date-time description: The timestamp (in UTC) at which the record was created. lastModifiedAt: type: string format: date-time description: The timestamp (in UTC) at which the record was last modified.required: - id - status - createdAt - lastModifiedAtunevaluatedProperties: not: {}examples: - id: 01912a8b-7c3d-7890-abcd-ef1234567890 status: value: accepted description: The change was applied. source: grants.gov patch: mission: To expand access to community health resources. snapshot: id: 01912a8b-7c3d-7890-abcd-ef1234567890 name: Example Nonprofit mission: To expand access to community health resources. createdAt: 2026-06-20T14:30:00Z lastModifiedAt: 2026-06-20T14:30:00Zdescription: |- A record of a single change to a resource.
A revision captures the merge patch that was submitted, a snapshot of the record with the change applied, and the lifecycle status of the change. Revisions make up a change ledger: `createdAt` marks when the change was submitted and orders the ledger, while `lastModifiedAt` marks when the `status` last changed. The submission facts (`patch` and `source`) never change, but the `status` can transition until it reaches a terminal state.
Here `patch` and `snapshot` are open objects. A concrete resource binds its own types with `RevisionT` (see `OrgRevision`, which binds `OrganizationBase` and `OrgPatchData`).The TypeSpec code for this model.
* `OrganizationBase`). * @template PatchT The type of the merge patch body (for example `OrgPatchData`). */@Versioning.added(CommonGrants.Versions.v0_4)model RevisionT<SnapshotT, PatchT> { /** Globally unique id for the revision */ @visibility(Lifecycle.Read) id: uuid;
/** The lifecycle status of the change */ status: RevisionStatus;
/** The source system the change came from */ source?: string;
/** The merge patch that was submitted */ patch?: PatchT;
/** A full snapshot of the record with the change applied */ snapshot?: SnapshotT;
// createdAt = when the change was submitted (immutable, orders the ledger) // lastModifiedAt = when the status last changed ...SystemMetadata;}
/** A record of a single change to a resource. * * A revision captures the merge patch that was submitted, a snapshot of the * record with the change applied, and the lifecycle status of the change. * Revisions make up a change ledger: `createdAt` marks when the change was * submitted and orders the ledger, while `lastModifiedAt` marks when the * `status` last changed. The submission facts (`patch` and `source`) never * change, but the `status` can transition until it reaches a terminal state. * * Here `patch` and `snapshot` are open objects. A concrete resource binds its * own types with `RevisionT` (see `OrgRevision`, which binds `OrganizationBase` * and `OrgPatchData`). */@example(Examples.Revision.revision)@Versioning.added(CommonGrants.Versions.v0_4)model Revision is RevisionT<unknown, unknown>;
// #########################################################// Examples// #########################################################
/** Examples of the Revision and RevisionStatus models */namespace Examples.Revision { const acceptedStatus = #{ value: RevisionStatusOptions.accepted,Changelog
Section titled “Changelog”| Version | Changes | Schema |
|---|---|---|
| 0.4.0 |
| Revision.yaml |
RevisionStatus
Section titled “RevisionStatus”The lifecycle status of a change, with a value from a fixed set of options, an optional customValue for implementation-defined states, and an optional human-readable description.
A change moves once from the non-terminal pending state into one of three terminal states. A direct PATCH is created already accepted. A terminal revision is never restated, so an accepted change never later becomes superseded.
| Status | Terminal | Reached by |
|---|---|---|
pending |
No | POST /orgs/{orgId}/changes when the receiver queues for review |
accepted |
Yes | PATCH (applied immediately), or a pending change approved |
denied |
Yes | a pending change rejected (carries a reason) |
superseded |
Yes | a pending change made moot by a competing change accepted first |
custom |
— | an implementation-defined state; the receiver documents it |
How a receiver resolves a pending change (a human review, a policy engine, a batch job) is out of scope for this contract, so there is no review endpoint. The receiver drives the transition and reports the outcome through the change’s status.
| Property | Type | Required | Description |
|---|---|---|---|
| value | RevisionStatusOptions | Yes | The selected value, from a predefined set of options |
| customValue | string | No | A custom value, used when the selected value is the `custom` option |
| description | string | No | A human-readable description of the value |
Formats
Section titled “Formats”A JSON example of this model.
{ "value": "custom", "customValue": "escalated", "description": "The change was escalated for additional review."}The JSON Schema for this model.
$schema: https://json-schema.org/draft/2020-12/schema$id: RevisionStatus.yamltype: objectproperties: value: $ref: RevisionStatusOptions.yaml description: The selected value, from a predefined set of options customValue: type: string description: A custom value, used when the selected value is the `custom` option description: type: string description: A human-readable description of the valuerequired: - valueunevaluatedProperties: not: {}examples: - value: custom customValue: escalated description: The change was escalated for additional review. - value: accepted description: The change was applied.description: |- The status of a revision, including its lifecycle state and an optional human-readable description.The TypeSpec code for this model.
/** The status of a revision, including its lifecycle state and an optional * human-readable description. */@example(Examples.Revision.acceptedStatus)@example(Examples.Revision.customStatus)@Versioning.added(CommonGrants.Versions.v0_4)model RevisionStatus is Fields.ExtensibleEnumT<RevisionStatusOptions>;
// #########################################################// Revision// #########################################################
/** A template for building a typed revision record. * * Maintainers bind `RevisionT` to a specific resource's snapshot and patch * types so `patch` and `snapshot` are typed rather than open objects, as inChangelog
Section titled “Changelog”| Version | Changes | Schema |
|---|---|---|
| 0.4.0 |
| RevisionStatus.yaml |
RevisionStatusOptions
Section titled “RevisionStatusOptions”The fixed set of values a RevisionStatus can take: pending, accepted, denied, superseded, and custom.
Formats
Section titled “Formats”A JSON example of this model.
"pending"The JSON Schema for this model.
$schema: https://json-schema.org/draft/2020-12/schema$id: RevisionStatusOptions.yamltype: stringenum: - pending - accepted - denied - superseded - customdescription: |- The set of values accepted for a revision's status: - `pending`: Non-terminal. The change is queued for review. - `accepted`: Terminal. The change was applied, either immediately via a direct `PATCH` or after a `pending` change was approved. - `denied`: Terminal. The change was rejected, with a reason. - `superseded`: Terminal. A competing change was accepted first, so this change no longer applies. - `custom`: An implementation-defined status. The receiver documents whether it is terminal.
A revision moves once from `pending` into a terminal state; a terminal revision is never restated. How a receiver resolves a `pending` change (a human review, a policy engine, a batch job) is out of scope for this contract.The TypeSpec code for this model.
/** The set of values accepted for a revision's status: * - `pending`: Non-terminal. The change is queued for review. * - `accepted`: Terminal. The change was applied, either immediately via a * direct `PATCH` or after a `pending` change was approved. * - `denied`: Terminal. The change was rejected, with a reason. * - `superseded`: Terminal. A competing change was accepted first, so this * change no longer applies. * - `custom`: An implementation-defined status. The receiver documents whether * it is terminal. * * A revision moves once from `pending` into a terminal state; a terminal * revision is never restated. How a receiver resolves a `pending` change (a * human review, a policy engine, a batch job) is out of scope for this contract. */@Versioning.added(CommonGrants.Versions.v0_4)enum RevisionStatusOptions { pending, accepted, denied, superseded, custom,}Changelog
Section titled “Changelog”| Version | Changes | Schema |
|---|---|---|
| 0.4.0 |
| RevisionStatusOptions.yaml |
OrgRevision
Section titled “OrgRevision”A Revision bound to organization profiles, where patch is an OrgPatchData merge patch and snapshot is an OrganizationBase with that change applied. Returned by the organization write and changes routes.
| Property | Type | Required | Description |
|---|---|---|---|
| id | uuid | Yes | Globally unique id for the revision |
| status | RevisionStatus | Yes | The lifecycle status of the change |
| source | string | No | The source system the change came from |
| patch | OrgPatchData | No | The merge patch that was submitted |
| snapshot | OrganizationBase | No | A full snapshot of the record with the change applied |
| createdAt | utcDateTime | Yes | The timestamp (in UTC) at which the record was created. |
| lastModifiedAt | utcDateTime | Yes | The timestamp (in UTC) at which the record was last modified. |
Formats
Section titled “Formats”A JSON example of this model.
{ "id": "01912a8b-7c3d-7890-abcd-ef1234567890", "status": { "value": "accepted", "description": "The change was applied." }, "source": "grants.gov", "patch": { "mission": "To provide support and resources to the community." }, "snapshot": { "id": "083b4567-e89d-42c8-a439-6c1234567890", "name": "Example Organization", "orgType": { "term": "Hospital", "class": "Organization types", "description": "Institutions with the primary purpose of providing in-patient physical and mental health services...", "code": "EO000000" }, "identifiers": { "org:us:ein": { "registry": { "code": "org:us:ein", "url": "https://commongrants.org/registries/org-us-ein" }, "id": "123456789" }, "org:us:uei": { "registry": { "code": "org:us:uei", "url": "https://commongrants.org/registries/org-us-uei" }, "id": "AB0123456789" } }, "addresses": { "primary": { "street1": "456 Main St", "street2": "Suite 100", "city": "Anytown", "stateOrProvince": "CA", "country": "US", "postalCode": "12345" }, "otherAddresses": { "satellite": { "street1": "456 Main St", "street2": "Suite 100", "city": "Anytown", "stateOrProvince": "CA", "country": "US", "postalCode": "12345" }, "international": { "street1": "123 Rue Principale", "city": "Montreal", "stateOrProvince": "QC", "country": "CA", "postalCode": "H2Y 1C6" } } }, "phones": { "primary": { "countryCode": "+1", "number": "444-456-1230", "isMobile": true }, "fax": { "countryCode": "+1", "number": "555-123-4567", "extension": "123", "isMobile": false }, "otherPhones": { "support": { "countryCode": "+1", "number": "333-456-1230", "isMobile": false }, "marketing": { "countryCode": "+1", "number": "444-456-1230", "isMobile": true } } }, "emails": { "primary": "info@example.com", "otherEmails": { "support": "support@example.com", "marketing": "marketing@example.com" } }, "mission": "To provide support and resources to the community.", "yearFounded": "2024", "socials": { "website": "https://www.example.com", "facebook": "https://www.facebook.com/example", "twitterOrX": "https://x.com/example", "instagram": "https://www.instagram.com/example", "linkedin": "https://www.linkedin.com/company/example", "otherSocials": { "youtube": "https://www.youtube.com/example" } } }, "createdAt": "2026-06-20T14:30:00Z", "lastModifiedAt": "2026-06-20T14:30:00Z"}The JSON Schema for this model.
$schema: https://json-schema.org/draft/2020-12/schema$id: OrgRevision.yamltype: objectproperties: id: $ref: uuid.yaml description: Globally unique id for the revision status: $ref: RevisionStatus.yaml description: The lifecycle status of the change source: type: string description: The source system the change came from patch: $ref: OrgPatchData.yaml description: The merge patch that was submitted snapshot: $ref: OrganizationBase.yaml description: A full snapshot of the record with the change applied createdAt: type: string format: date-time description: The timestamp (in UTC) at which the record was created. lastModifiedAt: type: string format: date-time description: The timestamp (in UTC) at which the record was last modified.required: - id - status - createdAt - lastModifiedAtunevaluatedProperties: not: {}examples: - id: 01912a8b-7c3d-7890-abcd-ef1234567890 status: value: accepted description: The change was applied. source: grants.gov patch: mission: To provide support and resources to the community. snapshot: id: 083b4567-e89d-42c8-a439-6c1234567890 name: Example Organization orgType: term: Hospital class: Organization types description: Institutions with the primary purpose of providing in-patient physical and mental health services... code: EO000000 identifiers: org:us:ein: registry: code: org:us:ein url: https://commongrants.org/registries/org-us-ein id: "123456789" org:us:uei: registry: code: org:us:uei url: https://commongrants.org/registries/org-us-uei id: AB0123456789 addresses: primary: street1: 456 Main St street2: Suite 100 city: Anytown stateOrProvince: CA country: US postalCode: "12345" otherAddresses: satellite: street1: 456 Main St street2: Suite 100 city: Anytown stateOrProvince: CA country: US postalCode: "12345" international: street1: 123 Rue Principale city: Montreal stateOrProvince: QC country: CA postalCode: H2Y 1C6 phones: primary: countryCode: "+1" number: 444-456-1230 isMobile: true fax: countryCode: "+1" number: 555-123-4567 extension: "123" isMobile: false otherPhones: support: countryCode: "+1" number: 333-456-1230 isMobile: false marketing: countryCode: "+1" number: 444-456-1230 isMobile: true emails: primary: info@example.com otherEmails: support: support@example.com marketing: marketing@example.com mission: To provide support and resources to the community. yearFounded: "2024" socials: website: https://www.example.com facebook: https://www.facebook.com/example twitterOrX: https://x.com/example instagram: https://www.instagram.com/example linkedin: https://www.linkedin.com/company/example otherSocials: youtube: https://www.youtube.com/example createdAt: 2026-06-20T14:30:00Z lastModifiedAt: 2026-06-20T14:30:00Zdescription: |- A single change to an organization profile, recorded in the change ledger.
The `patch` is an `OrgPatchData` merge patch, and the `snapshot` is the organization profile with that change applied.The TypeSpec code for this model.
@Versioning.added(CommonGrants.Versions.v0_4) model PCSOrgType { ...Fields.PCSOrgType; }
/** A collection of addresses. */ @Versioning.added(CommonGrants.Versions.v0_4) model AddressCollection {Changelog
Section titled “Changelog”| Version | Changes | Schema |
|---|---|---|
| 0.4.0 |
| OrgRevision.yaml |