TfP Docs

OrganizationMembershipRequest schema

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

A User's request to join an Organization, resolved by an Organization admin. At most one pending request per user/Organization; active members cannot request; the requester may cancel their own pending request; resolved requests remain historical and cannot be re-resolved; a pending request is automatically marked superseded when membership arrives through another route. Approval creates exactly one OrganizationMembership (established_via request_approval).

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

Fields

id

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

Stable opaque identifier.

Example: "req_01jf93aa1jown"

organization_id

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

The Organization being asked to join.

Example: "org_01jf90aa1mit0"

user_id

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

The requesting User.

Example: "usr_01jf8xw3tempo"

status

Type: pending | approved | declined | cancelled | superseded · Required: yes · Mutable: yes

pending → approved/declined (by an admin), cancelled (by the requester), or superseded (membership was established through another route — domain email, admin add, or invite — while the request was pending). Terminal states are historical.

Example: "pending"

note

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

Optional short note from the requester, shown to admins.

Example: "Visiting researcher this term — no lab email yet."

requested_at

Type: string · Required: yes · Mutable: no

RFC 3339 UTC timestamp of the request.

Example: "2026-08-31T18:00:00Z"

resolved_at

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

When the request left pending, or null while pending.

Example: null

resolved_by_user_id

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

The admin who approved/declined, or the requester for cancellations. Null while pending.

Example: null

membership_id

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

The resulting OrganizationMembership when approved; null otherwise.

Example: null

JSON Schema source

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://tokensforprogress.org/schemas/organization-membership-request.schema.json",
  "title": "OrganizationMembershipRequest",
  "description": "A User's request to join an Organization, resolved by an Organization admin. At most one pending request per user/Organization; active members cannot request; the requester may cancel their own pending request; resolved requests remain historical and cannot be re-resolved; a pending request is automatically marked superseded when membership arrives through another route. Approval creates exactly one OrganizationMembership (established_via request_approval).",
  "x-tfp-version": "1.0.0",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "id",
    "organization_id",
    "user_id",
    "status",
    "requested_at"
  ],
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^req_[0-9a-z]{10,32}$",
      "x-mutable": false,
      "description": "Stable opaque identifier.",
      "examples": [
        "req_01jf93aa1jown"
      ]
    },
    "organization_id": {
      "type": "string",
      "pattern": "^org_[0-9a-z]{10,32}$",
      "x-mutable": false,
      "description": "The Organization being asked to join.",
      "examples": [
        "org_01jf90aa1mit0"
      ]
    },
    "user_id": {
      "type": "string",
      "pattern": "^usr_[0-9a-z]{10,32}$",
      "x-mutable": false,
      "description": "The requesting User.",
      "examples": [
        "usr_01jf8xw3tempo"
      ]
    },
    "status": {
      "type": "string",
      "enum": [
        "pending",
        "approved",
        "declined",
        "cancelled",
        "superseded"
      ],
      "x-mutable": true,
      "description": "pending → approved/declined (by an admin), cancelled (by the requester), or superseded (membership was established through another route — domain email, admin add, or invite — while the request was pending). Terminal states are historical.",
      "examples": [
        "pending"
      ]
    },
    "note": {
      "type": [
        "string",
        "null"
      ],
      "maxLength": 280,
      "x-mutable": false,
      "description": "Optional short note from the requester, shown to admins.",
      "examples": [
        "Visiting researcher this term — no lab email yet."
      ]
    },
    "requested_at": {
      "type": "string",
      "format": "date-time",
      "x-mutable": false,
      "description": "RFC 3339 UTC timestamp of the request.",
      "examples": [
        "2026-08-31T18:00:00Z"
      ]
    },
    "resolved_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "x-mutable": true,
      "description": "When the request left pending, or null while pending.",
      "examples": [
        null
      ]
    },
    "resolved_by_user_id": {
      "type": [
        "string",
        "null"
      ],
      "pattern": "^usr_[0-9a-z]{10,32}$",
      "x-mutable": true,
      "description": "The admin who approved/declined, or the requester for cancellations. Null while pending.",
      "examples": [
        null
      ]
    },
    "membership_id": {
      "type": [
        "string",
        "null"
      ],
      "pattern": "^mem_[0-9a-z]{10,32}$",
      "x-mutable": true,
      "description": "The resulting OrganizationMembership when approved; null otherwise.",
      "examples": [
        null
      ]
    }
  }
}