TfP Docs

OrganizationMembership schema

Generated from the canonical schema schemas/organization-membership.schema.json by scripts/generate-schema-docs.mjs — do not edit this page by hand.

A User's association with an Organization. Users may belong to many Organizations at once, and at most one membership per user/Organization is active (ended_at null) at a time — leaving and rejoining creates a new record, and history is never rewritten. Membership can be established three ways (recorded as provenance): verifying an email at the Organization's canonical verified domain, an admin adding the User directly, or an admin approving the User's membership request. Membership is affiliation only: it is not administrative authority (see OrganizationAdmin) and does not attribute the User's work to the Organization.

Schema version: 1.0.0 · $id: https://tokensforprogress.org/schemas/organization-membership.schema.json

Fields

id

Type: string · Required: yes · Mutable: no · Pattern: ^mem_[0-9a-z]{10,32}$

Stable opaque identifier for this membership record.

Example: "mem_01jf90bb2link"

user_id

Type: string · Required: yes · Mutable: no · Pattern: ^usr_[0-9a-z]{10,32}$

The member User.

Example: "usr_01jf8xw3tempo"

organization_id

Type: string · Required: yes · Mutable: no · Pattern: ^org_[0-9a-z]{10,32}$

The Organization the User is associated with.

Example: "org_01jf90aa1mit0"

role

Type: string \| null · Required: no · Mutable: yes

Optional free-text descriptive role ('Research Scientist', 'PhD Student'), editable by Organization admins. Descriptive only — changing it never alters permissions; administrative capability lives on OrganizationAdmin.

Example: "PhD student"

joined_at

Type: string · Required: yes · Mutable: no

RFC 3339 UTC timestamp when the membership began on TfP.

Example: "2026-08-31T15:04:11Z"

ended_at

Type: string \| null · Required: no · Mutable: yes

RFC 3339 UTC timestamp when the membership ended, or null while current. Set once when the association ends; historical attribution referencing this Organization is unaffected.

Example: null

established_via

Type: domain_email | admin | request_approval · Required: yes · Mutable: no

How the membership was established: domain_email (the User verified an email at the Organization's canonical domain), admin (an admin added the User), request_approval (an admin approved the User's request).

Example: "domain_email"

established_by_user_id

Type: string · Required: yes · Mutable: no · Pattern: ^usr_[0-9a-z]{10,32}$

Who established the membership: the joining User for domain_email, the acting admin for admin, the approving admin for request_approval.

Example: "usr_01jf8xw3tempo"

JSON Schema source

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://tokensforprogress.org/schemas/organization-membership.schema.json",
  "title": "OrganizationMembership",
  "description": "A User's association with an Organization. Users may belong to many Organizations at once, and at most one membership per user/Organization is active (ended_at null) at a time — leaving and rejoining creates a new record, and history is never rewritten. Membership can be established three ways (recorded as provenance): verifying an email at the Organization's canonical verified domain, an admin adding the User directly, or an admin approving the User's membership request. Membership is affiliation only: it is not administrative authority (see OrganizationAdmin) and does not attribute the User's work to the Organization.",
  "x-tfp-version": "1.0.0",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "id",
    "user_id",
    "organization_id",
    "joined_at",
    "established_via",
    "established_by_user_id"
  ],
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^mem_[0-9a-z]{10,32}$",
      "x-mutable": false,
      "description": "Stable opaque identifier for this membership record.",
      "examples": [
        "mem_01jf90bb2link"
      ]
    },
    "user_id": {
      "type": "string",
      "pattern": "^usr_[0-9a-z]{10,32}$",
      "x-mutable": false,
      "description": "The member User.",
      "examples": [
        "usr_01jf8xw3tempo"
      ]
    },
    "organization_id": {
      "type": "string",
      "pattern": "^org_[0-9a-z]{10,32}$",
      "x-mutable": false,
      "description": "The Organization the User is associated with.",
      "examples": [
        "org_01jf90aa1mit0"
      ]
    },
    "role": {
      "type": [
        "string",
        "null"
      ],
      "maxLength": 80,
      "x-mutable": true,
      "description": "Optional free-text descriptive role ('Research Scientist', 'PhD Student'), editable by Organization admins. Descriptive only — changing it never alters permissions; administrative capability lives on OrganizationAdmin.",
      "examples": [
        "PhD student"
      ]
    },
    "joined_at": {
      "type": "string",
      "format": "date-time",
      "x-mutable": false,
      "description": "RFC 3339 UTC timestamp when the membership began on TfP.",
      "examples": [
        "2026-08-31T15:04:11Z"
      ]
    },
    "ended_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "x-mutable": true,
      "description": "RFC 3339 UTC timestamp when the membership ended, or null while current. Set once when the association ends; historical attribution referencing this Organization is unaffected.",
      "examples": [
        null
      ]
    },
    "established_via": {
      "type": "string",
      "enum": [
        "domain_email",
        "admin",
        "request_approval"
      ],
      "x-mutable": false,
      "description": "How the membership was established: domain_email (the User verified an email at the Organization's canonical domain), admin (an admin added the User), request_approval (an admin approved the User's request).",
      "examples": [
        "domain_email"
      ]
    },
    "established_by_user_id": {
      "type": "string",
      "pattern": "^usr_[0-9a-z]{10,32}$",
      "x-mutable": false,
      "description": "Who established the membership: the joining User for domain_email, the acting admin for admin, the approving admin for request_approval.",
      "examples": [
        "usr_01jf8xw3tempo"
      ]
    }
  }
}