Event Types

The relationship.upsert and relationship.revoke event types.

The SIG v0.1 protocol defines two event types. Both share the common event fields and add type-specific fields described below.

relationship.upsert

Creates a new relationship or updates an existing one. If a relationship.upsert event is emitted with the same relationship_id as a prior event, the new event fully replaces the previous state for that relationship.

Type-Specific Fields

FieldTypeRequiredDescription
relationship_typestringYesThe kind of relationship. One of: "employee", "founder", "contractor", "advisor", "investor", "admin_delegate", "other".
statusstringYesMust be "active" in v0.1.
rolesstring[]YesArray of role strings describing the subject’s roles (e.g. ["engineer", "team-lead"]). May be empty.
valid_fromstring | nullYesStart of the validity window in RFC 3339 UTC, or null if the relationship has no defined start date.
valid_untilstring | nullYesEnd of the validity window in RFC 3339 UTC, or null if the relationship has no defined end date.
displayobjectNoOptional display metadata for human-readable presentation.
display.titlestringNoJob title or role title (e.g. "Senior Engineer").
display.departmentstringNoDepartment or team name (e.g. "Platform Engineering").
display.labelstringNoFreeform label for display purposes.
reasonstringNoOptional human-readable reason for this attestation.
metadataobjectNoOptional key-value pairs for issuer-specific data.

Example: relationship.upsert Event

The following shows the decoded payload of a relationship.upsert event. In the feed, this payload would be base64url-encoded inside a JWS Flattened Serialization envelope.

{
  "spec_version": "sig/0.1",
  "event_id": "01JAXR5Z0001EXAMPLE00001",
  "event_type": "relationship.upsert",
  "issuer": "did:web:acme.example",
  "issued_at": "2026-02-15T10:00:00Z",
  "sequence": 1,
  "relationship_id": "rel-alice-acme-001",
  "subject": "did:web:alice.example",
  "visibility": "public",
  "relationship_type": "employee",
  "status": "active",
  "roles": ["engineer", "team-lead"],
  "valid_from": "2025-03-01T00:00:00Z",
  "valid_until": null,
  "display": {
    "title": "Senior Engineer",
    "department": "Platform Engineering"
  }
}

The complete JWS Flattened Serialization envelope for this event would look like:

{
  "protected": "eyJhbGciOiJFZERTQSIsImtpZCI6ImtleS0xIiwidHlwIjoib3JlLWV2ZW50K2p3cyJ9",
  "payload": "eyJzcGVjX3ZlcnNpb24iOiJvcmUvMC4xIiwiZXZlbnRfaWQiOiIwMUpBWFI1WjAwMDFFWEFNUExFMDAwMDEiLCJldmVudF90eXBlIjoicmVsYXRpb25zaGlwLnVwc2VydCIsImlzc3VlciI6ImRpZDp3ZWI6YWNtZS5leGFtcGxlIiwiaXNzdWVkX2F0IjoiMjAyNi0wMi0xNVQxMDowMDowMFoiLCJzZXF1ZW5jZSI6MSwicmVsYXRpb25zaGlwX2lkIjoicmVsLWFsaWNlLWFjbWUtMDAxIiwic3ViamVjdCI6ImRpZDp3ZWI6YWxpY2UuZXhhbXBsZSIsInZpc2liaWxpdHkiOiJwdWJsaWMiLCJyZWxhdGlvbnNoaXBfdHlwZSI6ImVtcGxveWVlIiwic3RhdHVzIjoiYWN0aXZlIiwicm9sZXMiOlsiZW5naW5lZXIiLCJ0ZWFtLWxlYWQiXSwidmFsaWRfZnJvbSI6IjIwMjUtMDMtMDFUMDA6MDA6MDBaIiwidmFsaWRfdW50aWwiOm51bGwsImRpc3BsYXkiOnsidGl0bGUiOiJTZW5pb3IgRW5naW5lZXIiLCJkZXBhcnRtZW50IjoiUGxhdGZvcm0gRW5naW5lZXJpbmcifX0",
  "signature": "abc123signatureexample..."
}

Updating a Relationship

To update a relationship, emit a new relationship.upsert event with the same relationship_id and a higher sequence number. The new event fully replaces the derived state for that relationship. For example, to change Alice’s role:

{
  "spec_version": "sig/0.1",
  "event_id": "01JAXR5Z0001EXAMPLE00002",
  "event_type": "relationship.upsert",
  "issuer": "did:web:acme.example",
  "issued_at": "2026-02-20T14:00:00Z",
  "sequence": 2,
  "relationship_id": "rel-alice-acme-001",
  "subject": "did:web:alice.example",
  "visibility": "public",
  "relationship_type": "employee",
  "status": "active",
  "roles": ["staff-engineer", "team-lead", "architect"],
  "valid_from": "2025-03-01T00:00:00Z",
  "valid_until": null,
  "display": {
    "title": "Staff Engineer",
    "department": "Platform Engineering"
  },
  "reason": "Promoted to Staff Engineer"
}

relationship.revoke

Revokes a previously attested relationship. After a revocation event is processed, the derived status of the relationship becomes "revoked".

Type-Specific Fields

FieldTypeRequiredDescription
revokes_relationship_idstringYesThe relationship_id being revoked. Must equal the relationship_id field in the same event.
reason_codestringYesMachine-readable reason for the revocation. One of: "employment_ended", "contract_ended", "permission_revoked", "superseded", "admin_action", "error_correction", "other".
effective_atstringYesThe timestamp at which the revocation takes effect, in RFC 3339 UTC. May be in the past (backdated) or the current time.
reasonstringNoOptional human-readable explanation for the revocation.
metadataobjectNoOptional key-value pairs for issuer-specific data.

The constraint that revokes_relationship_id must equal relationship_id ensures that the revocation event is always co-located with the relationship it revokes in the by_relationship_id map during state derivation.

Example: relationship.revoke Event

{
  "spec_version": "sig/0.1",
  "event_id": "01JAXR5Z0001EXAMPLE00003",
  "event_type": "relationship.revoke",
  "issuer": "did:web:acme.example",
  "issued_at": "2026-02-27T09:00:00Z",
  "sequence": 3,
  "relationship_id": "rel-alice-acme-001",
  "subject": "did:web:alice.example",
  "visibility": "public",
  "revokes_relationship_id": "rel-alice-acme-001",
  "reason_code": "employment_ended",
  "effective_at": "2026-02-28T00:00:00Z",
  "reason": "Employee resignation, last day February 28"
}

After this event is processed, the derived state for rel-alice-acme-001 would be:

{
  "issuer": "did:web:acme.example",
  "relationship_id": "rel-alice-acme-001",
  "subject": "did:web:alice.example",
  "relationship_type": "employee",
  "roles": ["staff-engineer", "team-lead", "architect"],
  "valid_from": "2025-03-01T00:00:00Z",
  "valid_until": null,
  "status": "revoked",
  "revoked_reason_code": "employment_ended",
  "revoked_effective_at": "2026-02-28T00:00:00Z",
  "last_sequence": 3
}

Event Ordering and Consistency

Events must be appended to the feed in strict sequence order. Consumers process events sequentially and apply them to the relationship state map. The following rules apply:

  • Sequence numbers are monotonically increasing and start at 1. There must be no gaps.
  • A relationship.revoke for a relationship_id that has no prior relationship.upsert is an error. Consumers should reject or ignore such events.
  • Multiple upserts for the same relationship_id are valid. Each upsert fully replaces the derived state for that relationship.
  • A relationship.upsert after a relationship.revoke for the same relationship_id re-activates the relationship. The status returns to "active" and the revocation fields are cleared.
  • event_id values must be unique across the entire feed. Duplicate event IDs indicate a malformed feed.