Account Updated Payload
Payload emitted when an identity account is updated. A full snapshot (RFC-030 SS10), not a delta: every field is present on every emission regardless of which the request changed, because the Legacy Bridge converges the monolith onto the payload rather than replaying changes. Shares its shape with the account-created payload, so a consumer can apply either through one path and resolve-or-create from whichever arrives first (RFC-030 SS18) - which matters most for legacy installers, who emit no created event at all: their login sync emits this event when their email, installer type or groups change, once for an account it has never announced, and for every member of a monolith group that is renamed. v1.1.0 adds phoneNumber, language, the role, groups and address; widens accountType to TEXECOM_USER; and types the nullable columns as such (SOFTC-7120). v1.2.0 adds the optional monolithOwned flag (SOFTC-7320).
| $id | https://schemas.texecom-prod.com/v2/events/payload/service/identity/account/updated/v1.2.0.json |
| $schema | http://json-schema.org/draft-07/schema |
Properties
accountId
| Description |
UUID of the account that was updated (identity.accounts.account_id). |
| Type | String |
| Required |
Yes |
| Format |
uuid |
email
| Description |
Email address of the account (identity.accounts.email). Case-insensitive, not unique across accounts. |
| Type | String |
| Required |
Yes |
| Format |
email |
accountType
| Description |
Type of the account (identity.accounts.account_type). |
| Type | String |
| Required |
Yes |
| Enum |
- END_USER
- INSTALLER_USER
- TEXECOM_USER
|
firstName
| Description |
First name of the account holder (identity.accounts.first_name). Null when the column is unset - a snapshot states an empty field rather than omitting it, so a cleared value can converge. |
| Type | [string, null] |
| Required |
Yes |
lastName
| Description |
Last name of the account holder (identity.accounts.last_name). Null when the column is unset. |
| Type | [string, null] |
| Required |
Yes |
phoneNumber
| Description |
Phone number of the account holder (identity.accounts.phone_number). Null when the column is unset. |
| Type | [string, null] |
| Required |
Yes |
language
| Description |
ISO 639-1 language preference (identity.accounts.language). Defaults to 'en' at the column level; always the value the database committed, never one the producer assumed. |
| Type | String |
| Required |
Yes |
| Min Length |
2 |
| Max Length |
2 |
version
| Description |
Version of the account row after this update (identity.accounts.version). Incremented in SQL and read back with RETURNING, so it is the value the database committed. Downstream projections must discard this event if it is not greater than the last version they applied. |
| Type | Integer |
| Required |
Yes |
userType
| Description |
Role of an END_USER account (identity.users.user_type). Exactly one of userType, installerType and texecomUserType is present, selected by accountType. |
| Type | String |
| Required |
No |
| Enum |
- MASTER_END_USER
- STANDARD_END_USER
|
installerType
| Description |
Role of an INSTALLER_USER account (identity.installers.installer_type). Exactly one of userType, installerType and texecomUserType is present, selected by accountType. |
| Type | String |
| Required |
No |
| Enum |
- INSTALLER_ADMIN
- INSTALLER_ENGINEER
|
texecomUserType
| Description |
Role of a TEXECOM_USER account (identity.texecom_users.texecom_user_types). Exactly one of userType, installerType and texecomUserType is present, selected by accountType. |
| Type | String |
| Required |
No |
| Enum |
- TEXECOM_TECH_SUPPORT
- TEXECOM_SUPERVISOR
- SYSTEM_ADMINISTRATOR
|
groups
| Description |
Every group the account currently belongs to - user_groups for END_USER, installer_groups for INSTALLER_USER, always empty for TEXECOM_USER. An array because both membership tables are many-to-many: an installer's whole set is re-synced from the monolith on each legacy login, and end-users can be added to further groups. This is the complete set, not an addition, so a membership that has gone is expressible; consumers should converge on it rather than merge it. |
| Type | Array |
| Required |
Yes |
groups.groupId
| Description |
UUID of the group (identity.{user,installer}_groups.group_id). |
| Type | String |
| Format |
uuid |
groups.name
| Description |
Display name of the group. |
| Type | String |
groups.countryCode
| Description |
ISO 3166-1 alpha-2 country of the group. |
| Type | String |
| Min Length |
2 |
| Max Length |
2 |
groups.currency
| Description |
ISO 4217 currency of the group. |
| Type | String |
| Min Length |
3 |
| Max Length |
3 |
groups.version
| Description |
Version of the group row. The group is its own aggregate: guard on this, not on the account's version. |
| Type | Integer |
groups.monolithGroupId
| Description |
Installer groups only. Non-null means the MONOLITH owns this group, not identity-service: it was vivified locally as a placeholder (name 'Installer Group <prefix>', GB/GBP, empty businessSummary) purely so membership had something to point at. Converging the monolith onto such a row would overwrite real company detail with a stub - check this before treating a group as authoritative. Null for a natively registered group. |
| Type | [string, null] |
groups.businessSummary
| Description |
Installer groups only (identity.installer_groups.business_summary). |
| Type | String |
groups.address
| Description |
Installer groups only - the company's street address (identity.installer_groups.address), a plain string. Not to be confused with the account's top-level `address` object, which is an END_USER's home address. |
| Type | [string, null] |
groups.town
| Description |
Installer groups only (identity.installer_groups.town). |
| Type | [string, null] |
groups.postcode
| Description |
Installer groups only (identity.installer_groups.postcode). |
| Type | [string, null] |
groups.vatNumber
| Description |
Installer groups only (identity.installer_groups.vat_number). |
| Type | [string, null] |
address
| Description |
END_USER accounts only - the account holder's home address, from the identity.users row. Optional parts are explicit nulls so the object has one shape. Distinct from a group's `address`, which is an installer company's. |
| Type | Object |
| Required |
No |
Properties
address.line1
| Type | [string, null] |
| Required |
Yes |
address.line2
| Type | [string, null] |
| Required |
Yes |
address.town
| Type | [string, null] |
| Required |
Yes |
address.county
| Type | [string, null] |
| Required |
Yes |
address.postcode
| Type | [string, null] |
| Required |
Yes |
address.country
| Description |
ISO 3166-1 alpha-2. |
| Type | [string, null] |
| Required |
Yes |
monolithOwned
| Description |
True when the account's identity is owned by the monolith: a legacy installer, whose identity-service credential carries a MonolithId. Their snapshot comes from identity-service's login sync, which only ever writes email, so firstName, lastName and phoneNumber are null while the monolith holds the real values. The Legacy Bridge must not converge the monolith's account facet (firstName, lastName, phoneNumber) onto a true snapshot, or it blanks those names. The role and groups still apply; group ownership is per group, via groups[].monolithGroupId. False for every self-registered account, end user and Texecom staff account. Optional so that a v1.1.0 payload is still valid (SOFTC-7320). |
| Type | Boolean |
| Required |
No |
Schema
{
"$id": "https://schemas.texecom-prod.com/v2/events/payload/service/identity/account/updated/v1.2.0.json",
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Account Updated Payload",
"description": "Payload emitted when an identity account is updated. A full snapshot (RFC-030 SS10), not a delta: every field is present on every emission regardless of which the request changed, because the Legacy Bridge converges the monolith onto the payload rather than replaying changes. Shares its shape with the account-created payload, so a consumer can apply either through one path and resolve-or-create from whichever arrives first (RFC-030 SS18) - which matters most for legacy installers, who emit no created event at all: their login sync emits this event when their email, installer type or groups change, once for an account it has never announced, and for every member of a monolith group that is renamed. v1.1.0 adds phoneNumber, language, the role, groups and address; widens accountType to TEXECOM_USER; and types the nullable columns as such (SOFTC-7120). v1.2.0 adds the optional monolithOwned flag (SOFTC-7320).",
"type": "object",
"properties": {
"accountId": {
"description": "UUID of the account that was updated (identity.accounts.account_id).",
"type": "string",
"format": "uuid"
},
"email": {
"description": "Email address of the account (identity.accounts.email). Case-insensitive, not unique across accounts.",
"type": "string",
"format": "email"
},
"accountType": {
"description": "Type of the account (identity.accounts.account_type).",
"type": "string",
"enum": [
"END_USER",
"INSTALLER_USER",
"TEXECOM_USER"
]
},
"firstName": {
"description": "First name of the account holder (identity.accounts.first_name). Null when the column is unset - a snapshot states an empty field rather than omitting it, so a cleared value can converge.",
"type": [
"string",
"null"
]
},
"lastName": {
"description": "Last name of the account holder (identity.accounts.last_name). Null when the column is unset.",
"type": [
"string",
"null"
]
},
"phoneNumber": {
"description": "Phone number of the account holder (identity.accounts.phone_number). Null when the column is unset.",
"type": [
"string",
"null"
]
},
"language": {
"description": "ISO 639-1 language preference (identity.accounts.language). Defaults to 'en' at the column level; always the value the database committed, never one the producer assumed.",
"type": "string",
"minLength": 2,
"maxLength": 2
},
"version": {
"description": "Version of the account row after this update (identity.accounts.version). Incremented in SQL and read back with RETURNING, so it is the value the database committed. Downstream projections must discard this event if it is not greater than the last version they applied.",
"type": "integer",
"minimum": 0
},
"userType": {
"description": "Role of an END_USER account (identity.users.user_type). Exactly one of userType, installerType and texecomUserType is present, selected by accountType.",
"type": "string",
"enum": [
"MASTER_END_USER",
"STANDARD_END_USER"
]
},
"installerType": {
"description": "Role of an INSTALLER_USER account (identity.installers.installer_type). Exactly one of userType, installerType and texecomUserType is present, selected by accountType.",
"type": "string",
"enum": [
"INSTALLER_ADMIN",
"INSTALLER_ENGINEER"
]
},
"texecomUserType": {
"description": "Role of a TEXECOM_USER account (identity.texecom_users.texecom_user_types). Exactly one of userType, installerType and texecomUserType is present, selected by accountType.",
"type": "string",
"enum": [
"TEXECOM_TECH_SUPPORT",
"TEXECOM_SUPERVISOR",
"SYSTEM_ADMINISTRATOR"
]
},
"groups": {
"description": "Every group the account currently belongs to - user_groups for END_USER, installer_groups for INSTALLER_USER, always empty for TEXECOM_USER. An array because both membership tables are many-to-many: an installer's whole set is re-synced from the monolith on each legacy login, and end-users can be added to further groups. This is the complete set, not an addition, so a membership that has gone is expressible; consumers should converge on it rather than merge it.",
"type": "array",
"items": {
"type": "object",
"properties": {
"groupId": {
"description": "UUID of the group (identity.{user,installer}_groups.group_id).",
"type": "string",
"format": "uuid"
},
"name": {
"description": "Display name of the group.",
"type": "string"
},
"countryCode": {
"description": "ISO 3166-1 alpha-2 country of the group.",
"type": "string",
"minLength": 2,
"maxLength": 2
},
"currency": {
"description": "ISO 4217 currency of the group.",
"type": "string",
"minLength": 3,
"maxLength": 3
},
"version": {
"description": "Version of the group row. The group is its own aggregate: guard on this, not on the account's version.",
"type": "integer",
"minimum": 0
},
"monolithGroupId": {
"description": "Installer groups only. Non-null means the MONOLITH owns this group, not identity-service: it was vivified locally as a placeholder (name 'Installer Group <prefix>', GB/GBP, empty businessSummary) purely so membership had something to point at. Converging the monolith onto such a row would overwrite real company detail with a stub - check this before treating a group as authoritative. Null for a natively registered group.",
"type": [
"string",
"null"
]
},
"businessSummary": {
"description": "Installer groups only (identity.installer_groups.business_summary).",
"type": "string"
},
"address": {
"description": "Installer groups only - the company's street address (identity.installer_groups.address), a plain string. Not to be confused with the account's top-level `address` object, which is an END_USER's home address.",
"type": [
"string",
"null"
]
},
"town": {
"description": "Installer groups only (identity.installer_groups.town).",
"type": [
"string",
"null"
]
},
"postcode": {
"description": "Installer groups only (identity.installer_groups.postcode).",
"type": [
"string",
"null"
]
},
"vatNumber": {
"description": "Installer groups only (identity.installer_groups.vat_number).",
"type": [
"string",
"null"
]
}
},
"required": [
"groupId",
"name",
"countryCode",
"currency",
"version"
],
"additionalProperties": false
}
},
"address": {
"description": "END_USER accounts only - the account holder's home address, from the identity.users row. Optional parts are explicit nulls so the object has one shape. Distinct from a group's `address`, which is an installer company's.",
"type": "object",
"properties": {
"line1": {
"type": [
"string",
"null"
]
},
"line2": {
"type": [
"string",
"null"
]
},
"town": {
"type": [
"string",
"null"
]
},
"county": {
"type": [
"string",
"null"
]
},
"postcode": {
"type": [
"string",
"null"
]
},
"country": {
"description": "ISO 3166-1 alpha-2.",
"type": [
"string",
"null"
]
}
},
"required": [
"line1",
"line2",
"town",
"county",
"postcode",
"country"
],
"additionalProperties": false
},
"monolithOwned": {
"description": "True when the account's identity is owned by the monolith: a legacy installer, whose identity-service credential carries a MonolithId. Their snapshot comes from identity-service's login sync, which only ever writes email, so firstName, lastName and phoneNumber are null while the monolith holds the real values. The Legacy Bridge must not converge the monolith's account facet (firstName, lastName, phoneNumber) onto a true snapshot, or it blanks those names. The role and groups still apply; group ownership is per group, via groups[].monolithGroupId. False for every self-registered account, end user and Texecom staff account. Optional so that a v1.1.0 payload is still valid (SOFTC-7320).",
"type": "boolean"
}
},
"required": [
"accountId",
"email",
"accountType",
"firstName",
"lastName",
"phoneNumber",
"language",
"version",
"groups"
],
"oneOf": [
{
"properties": {
"accountType": {
"const": "END_USER"
}
},
"required": [
"userType"
],
"not": {
"anyOf": [
{
"required": [
"installerType"
]
},
{
"required": [
"texecomUserType"
]
}
]
}
},
{
"properties": {
"accountType": {
"const": "INSTALLER_USER"
}
},
"required": [
"installerType"
],
"not": {
"anyOf": [
{
"required": [
"userType"
]
},
{
"required": [
"texecomUserType"
]
}
]
}
},
{
"properties": {
"accountType": {
"const": "TEXECOM_USER"
}
},
"required": [
"texecomUserType"
],
"not": {
"anyOf": [
{
"required": [
"userType"
]
},
{
"required": [
"installerType"
]
}
]
}
}
],
"additionalProperties": false
}