> ## Documentation Index
> Fetch the complete documentation index at: https://docs.virtuous.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Organizations & Org Family

> The Organization resource — the parent-child 'organization family' hierarchy, the fields the live API returns, and the patterns for navigating multi-organization customer setups.

An Organization in the Volunteer API represents a customer entity — typically a nonprofit, a corporation's CSR program, or a similar group running a volunteer initiative. Organizations can be arranged in parent-child hierarchies — a national umbrella organization with regional chapter children, a corporation with departmental sub-orgs, an interfaith network with member organizations.

This is the **organization family** concept that determines what data an API token can access: a token issued for a parent organization typically grants access to that organization and its children.

## The two endpoints

| Endpoint              | Method | What it returns                                           |
| --------------------- | ------ | --------------------------------------------------------- |
| `/organizations`      | GET    | The Organization (or family) accessible to the token      |
| `/organizations/{id}` | GET    | A single Organization with its parent/child relationships |

Both are read-only. Organization records are managed in the VOMO admin UI; there's no API path for creating, updating, or deleting Organizations.

<Warning>
  **⚠️ Spec gap (audit #25, #26, #27):** The Organization endpoints have empty `schema: {}` in the OpenAPI spec — the response shape is documented only through inline examples. There is no `OrganizationResource` component schema.

  The fields documented on this page come from the spec's inline examples and live API observations. Treat them as the working contract, but confirm against actual responses for production-critical workflows.
</Warning>

***

## The Organization resource

Based on the spec's inline example, an Organization has these fields:

### Core identity

| Field         | Type           | Description                                                          |
| ------------- | -------------- | -------------------------------------------------------------------- |
| `id`          | integer        | The Organization's stable ID                                         |
| `name`        | string         | Display name                                                         |
| `slug`        | string         | URL-safe identifier (used in some filter parameters like `org_slug`) |
| `type`        | string         | Organization type (e.g., `"NPO"` for nonprofit)                      |
| `description` | string         | Free-text description (may be null)                                  |
| `created_at`  | string         | ISO 8601 datetime                                                    |
| `updated_at`  | string         | ISO 8601 datetime                                                    |
| `deleted_at`  | string or null | If deleted, the deletion timestamp                                   |

### Contact information

| Field     | Type   | Description                    |
| --------- | ------ | ------------------------------ |
| `email`   | string | Primary contact email          |
| `phone`   | string | Contact phone number           |
| `website` | string | The Organization's website URL |

### Organizational metadata

| Field                         | Type           | Description                                                          |
| ----------------------------- | -------------- | -------------------------------------------------------------------- |
| `tax_number`                  | string or null | Tax ID (EIN, etc.)                                                   |
| `org_size`                    | integer        | Approximate size (number of people in the org)                       |
| `background_check_expiration` | string         | How long background checks remain valid (e.g., `"1_YEAR"`)           |
| `hub_experience`              | string         | Hub experience tier (e.g., `"FULL"`)                                 |
| `invitation_token`            | string         | Token for inviting new users to the org                              |
| `user_submitted_projects`     | integer        | Flag indicating whether users can submit their own Projects (0 or 1) |
| `show_categories`             | integer        | Flag for displaying categories (0 or 1)                              |

### Custom monikers

VOMO Organizations support custom display labels that override the default UI vocabulary:

| Field              | Default      | Example custom values                    |
| ------------------ | ------------ | ---------------------------------------- |
| `groups_moniker`   | `"Group"`    | `"Team"`, `"Chapter"`, `"Cohort"`        |
| `project_moniker`  | `"Project"`  | `"Opportunity"`, `"Event"`, `"Activity"` |
| `campaign_moniker` | `"Campaign"` | `"Initiative"`, `"Drive"`, `"Mission"`   |

For partner integrations building user-facing displays, honor these monikers when displaying Organization-specific content. A customer that calls Projects "Events" appreciates seeing "Events" in your UI rather than "Projects."

### Address

The `address` field is an object with detailed location info:

```json theme={null}
{
  "address": {
    "lat": 32.7767,
    "lng": -96.7970,
    "country": "United States",
    "country_short": "US",
    "locality": "Dallas",
    "locality_short": "Dallas",
    "administrative_area_level_1": "Texas",
    "administrative_area_level_1_short": "TX",
    "timezone": "America/Chicago",
    "formatted_address": "Dallas, TX, USA"
  }
}
```

The format follows the Google Places API convention. The `formatted_address` field is the human-readable string suitable for display.

### Logo

The `logo` field is a media object:

```json theme={null}
{
  "logo": {
    "id": 99908,
    "type": "LOGO",
    "urls": {
      "s": "https://web-assets.vomo.org/.../logo-small.jpg",
      "o": "https://web-assets.vomo.org/.../logo-original.jpg",
      "l": "https://web-assets.vomo.org/.../logo-large.jpg"
    },
    "mediable_id": 12345,
    "mediable_type": "organization",
    "created_at": "...",
    "updated_at": "...",
    "deleted_at": null
  }
}
```

The `urls` object provides multiple sizes (`s` = small, `o` = original, `l` = large) for different display contexts.

### Parent and child relationships

The defining fields for the organization family:

| Field                  | Type  | Description                           |
| ---------------------- | ----- | ------------------------------------- |
| `parent_organizations` | array | Organizations this org is a child of  |
| `child_organizations`  | array | Organizations this org is a parent of |

Each entry in these arrays is itself an Organization-shaped object (potentially abbreviated), allowing the partner integration to navigate the hierarchy from any starting point.

***

## The organization family pattern

The parent-child relationships form a tree (or in some cases, a more complex DAG):

```mermaid theme={null}
graph TD
  WayneFoundation["Wayne Foundation<br/>(parent)"]
  GothamOutreach["Gotham Outreach<br/>(child)"]
  WayneManor["Wayne Manor Programs<br/>(child)"]
  WayneMedical["Wayne Medical<br/>(child)"]
  GothamFoodBank["Gotham Food Bank<br/>(grandchild)"]
  GothamSchools["Gotham Schools<br/>(grandchild)"]

  WayneFoundation --> GothamOutreach
  WayneFoundation --> WayneManor
  WayneFoundation --> WayneMedical
  GothamOutreach --> GothamFoodBank
  GothamOutreach --> GothamSchools
```

### What this enables

| Capability                               | Description                                                                       |
| ---------------------------------------- | --------------------------------------------------------------------------------- |
| **Single token, multiple organizations** | A token issued at the parent level grants access to descendant organizations      |
| **Aggregated reporting**                 | Reports can span the full family or filter to specific child orgs                 |
| **Per-org branding and configuration**   | Each org has its own monikers, logos, contact info                                |
| **Resource attribution**                 | Projects, Campaigns, Groups are owned by specific organizations within the family |

### Resource attribution

Most resources in the API include organization attribution:

| Resource | Org attribution fields                                 |
| -------- | ------------------------------------------------------ |
| Project  | `organization`, `organization_id`, `organization_slug` |
| Campaign | `organization`, `organization_slug`                    |
| Group    | (parent organization is implied; not directly exposed) |
| User     | `membership_*` fields scoped to the current org        |

For partner integrations doing per-org reporting or routing:

```javascript JavaScript theme={null}
async function projectsForChildOrg(orgSlug) {
  const params = new URLSearchParams({ org_slug: orgSlug });
  return paginate(`https://api.vomo.org/v1/projects?${params}`);
}

async function campaignsForChildOrg(orgSlug) {
  const params = new URLSearchParams({ org_slug: orgSlug });
  return paginate(`https://api.vomo.org/v1/campaigns?${params}`);
}
```

The `org_slug` query parameter on Project and Campaign endpoints accepts comma-separated values for multi-org queries.

***

## Listing organizations

```bash cURL theme={null}
curl https://api.vomo.org/v1/organizations \
  -H "Authorization: Bearer $VOMO_API_TOKEN"
```

<Note>
  **⚠️ Spec gap (audit #25):** The `GET /organizations` endpoint's summary describes returning "organizations in your organizations family," but the spec's example shows a single Organization object — not an array of Organizations. The pagination semantics aren't clearly documented.

  Confirm against the live API whether this endpoint returns a single Organization (the parent), a paginated list of the family, or something else. The patterns below assume it returns the family hierarchy in some form.
</Note>

For partner integrations that need to know the full organization family their token can access, this is the entry point.

```javascript JavaScript theme={null}
async function getOrganizationFamily() {
  const response = await fetch(
    'https://api.vomo.org/v1/organizations',
    { headers: { Authorization: `Bearer ${token}` } }
  );

  if (!response.ok) throw new Error(`Failed: ${response.status}`);

  const result = await response.json();
  return result.data;
}
```

If the response shape is "the parent Organization with its children listed," walk the `child_organizations` array. If it's "a list of organizations the token can access," paginate the result.

***

## Fetching a single organization

```bash cURL theme={null}
curl https://api.vomo.org/v1/organizations/12345 \
  -H "Authorization: Bearer $VOMO_API_TOKEN"
```

Returns the Organization with full detail, including its parent and child organization arrays.

```javascript JavaScript theme={null}
async function getOrganization(orgId) {
  const response = await fetch(
    `https://api.vomo.org/v1/organizations/${orgId}`,
    { headers: { Authorization: `Bearer ${token}` } }
  );

  if (response.status === 404) return null;
  if (!response.ok) throw new Error(`Failed: ${response.status}`);

  const result = await response.json();
  return parseOrganization(result.data);
}
```

Useful when you have a specific Organization ID (from a Project's `organization_id`, from a previous family fetch, etc.) and need the full record.

***

## Common workflows

### Map child organizations for routing

For partner integrations that route data based on which org within the family it belongs to:

```javascript JavaScript theme={null}
async function buildOrgRoutingMap() {
  const family = await getOrganizationFamily();
  const routingMap = new Map();

  // Map by slug for easy lookup
  function mapOrg(org) {
    routingMap.set(org.slug, {
      id: org.id,
      name: org.name,
      slug: org.slug,
      monikers: {
        groups: org.groups_moniker,
        project: org.project_moniker,
        campaign: org.campaign_moniker,
      },
    });

    for (const child of org.child_organizations ?? []) {
      mapOrg(child);
    }
  }

  mapOrg(family);
  return routingMap;
}

// Usage
const routingMap = await buildOrgRoutingMap();
const orgInfo = routingMap.get('gotham-food-bank');
```

The slug-based lookup makes routing on `project.organization_slug` straightforward.

### Display Organization monikers consistently

For UIs that display Organization-specific content:

```javascript JavaScript theme={null}
function getOrgLabel(org, kind) {
  switch (kind) {
    case 'group':
      return org.groups_moniker ?? 'Group';
    case 'project':
      return org.project_moniker ?? 'Project';
    case 'campaign':
      return org.campaign_moniker ?? 'Campaign';
    default:
      return capitalize(kind);
  }
}

// Usage
const projectLabel = getOrgLabel(currentOrg, 'project');
// e.g., "Event" for orgs that customize
```

A customer that calls their Projects "Events" gets to see "Events" everywhere in your integration.

### Aggregate across the family vs. filter to one org

For reporting integrations:

```javascript JavaScript theme={null}
// Aggregate across the entire family (default token scope)
async function totalActiveProjectsAcrossFamily() {
  const params = new URLSearchParams({ active: 'true', published: 'true' });
  const all = await paginate(`https://api.vomo.org/v1/projects?${params}`);
  return all.length;
}

// Filter to one specific org
async function activeProjectsForOrg(orgSlug) {
  const params = new URLSearchParams({
    active: 'true',
    published: 'true',
    org_slug: orgSlug,
  });
  return paginate(`https://api.vomo.org/v1/projects?${params}`);
}

// Filter to multiple specific orgs
async function activeProjectsForOrgs(orgSlugs) {
  const params = new URLSearchParams({
    active: 'true',
    published: 'true',
    org_slug: orgSlugs.join(','),
  });
  return paginate(`https://api.vomo.org/v1/projects?${params}`);
}
```

The `org_slug` parameter on Project and Campaign endpoints is the canonical way to filter — comma-separated lists are supported.

### Detect the token's "home" organization

The Organization the API token was issued for is typically the parent. To detect it programmatically:

```javascript JavaScript theme={null}
async function getHomeOrganization() {
  const family = await getOrganizationFamily();
  // If the family response returns a single Organization, that's the home
  // If it returns a hierarchy, the root (no parent_organizations) is the home
  if (Array.isArray(family.parent_organizations) && family.parent_organizations.length === 0) {
    return family;
  }
  // ...otherwise navigate up to the topmost org
  return family;
}
```

Useful for displaying "you're integrated with \[Home Org Name]" in your partner UI.

***

## ID, slug, and the family

A few practical points about Organization identifiers:

| Identifier      | When to use                                       |
| --------------- | ------------------------------------------------- |
| `id` (integer)  | Direct API references — `GET /organizations/{id}` |
| `slug` (string) | Filter parameters — `?org_slug=wayne-foundation`  |
| `name` (string) | Display only — never used as a key                |

The slug is human-readable and stable across the Organization's lifetime; the ID is the numeric primary key. Both are stable, but slugs are easier for humans to work with (and easier to communicate via documentation or settings UIs).

### Filtering across multiple orgs

The `org_slug` query parameter on Project and Campaign endpoints accepts comma-separated values:

```bash theme={null}
GET /projects?org_slug=gotham-outreach,wayne-manor-programs
```

This returns Projects belonging to either of the two child organizations. Useful for partner integrations whose customer wants a subset of their family included in a specific workflow.

***

## What this means for multi-org customers

Partner integrations serving customers with parent-child organization structures should:

| Practice                                         | Description                                                                    |
| ------------------------------------------------ | ------------------------------------------------------------------------------ |
| Surface the family structure in your UI          | Let the customer see what organizations the integration has access to          |
| Allow per-org configuration where it makes sense | Different child orgs may have different sync destinations, templates, or rules |
| Use slug-based filtering for routing             | Easier to read in code and configuration than numeric IDs                      |
| Honor monikers in user-facing displays           | The customer customized the labels for a reason                                |
| Don't assume one-token-equals-one-org            | Multi-org customers' single token can span many organizations                  |

The integration's design should accommodate single-org customers (most common) without making things complex, while also being able to handle multi-org families when they appear.

***

## A reference Organizations client

Given the spec gaps around the Organization shape, the reference client treats fields defensively:

```javascript JavaScript theme={null}
class VomoOrganizations {
  constructor({ token }) {
    this.token = token;
    this.baseUrl = 'https://api.vomo.org/v1';
  }

  async getFamily() {
    const response = await this._fetch(`${this.baseUrl}/organizations`);
    if (!response.ok) throw new Error(`Failed: ${response.status}`);
    const result = await response.json();
    return this._parseOrg(result.data);
  }

  async getById(orgId) {
    const response = await this._fetch(`${this.baseUrl}/organizations/${orgId}`);
    if (response.status === 404) return null;
    if (!response.ok) throw new Error(`Failed: ${response.status}`);
    const result = await response.json();
    return this._parseOrg(result.data);
  }

  _fetch(url, options = {}) {
    return fetch(url, {
      ...options,
      headers: {
        Authorization: `Bearer ${this.token}`,
        Accept: 'application/json',
        ...options.headers,
      },
    });
  }

  _parseOrg(raw) {
    return {
      id: raw.id,
      name: raw.name,
      slug: raw.slug,
      type: raw.type,
      description: raw.description ?? null,
      email: raw.email,
      phone: raw.phone,
      website: raw.website,
      taxNumber: raw.tax_number ?? null,
      orgSize: raw.org_size,
      backgroundCheckExpiration: raw.background_check_expiration,
      hubExperience: raw.hub_experience,
      invitationToken: raw.invitation_token,
      address: raw.address ?? null,
      logo: raw.logo ? this._parseLogo(raw.logo) : null,
      monikers: {
        groups: raw.groups_moniker ?? 'Group',
        project: raw.project_moniker ?? 'Project',
        campaign: raw.campaign_moniker ?? 'Campaign',
      },
      createdAt: raw.created_at ? new Date(raw.created_at) : null,
      updatedAt: raw.updated_at ? new Date(raw.updated_at) : null,
      deletedAt: raw.deleted_at ? new Date(raw.deleted_at) : null,
      childOrganizations: (raw.child_organizations ?? []).map(this._parseOrg.bind(this)),
      parentOrganizations: (raw.parent_organizations ?? []).map(this._parseOrg.bind(this)),
    };
  }

  _parseLogo(raw) {
    return {
      id: raw.id,
      type: raw.type,
      urls: raw.urls ?? {},
      mediableId: raw.mediable_id,
      mediableType: raw.mediable_type,
    };
  }
}
```

The `?? null` and `?? defaults` throughout reflect the spec gap reality — fields may be missing or have unexpected shapes, and the parser handles both gracefully.

***

## Where to go next

<CardGroup cols={2}>
  <Card title="Campaigns" icon="bullhorn" href="/volunteer/concepts/campaigns">
    The Campaign resource — Campaigns are owned by specific organizations within the family.
  </Card>

  <Card title="Groups" icon="users" href="/volunteer/concepts/groups">
    Groups belong to specific organizations within the family.
  </Card>

  <Card title="Projects and Project Dates" icon="calendar-days" href="/volunteer/concepts/projects-and-project-dates">
    Projects are attributed to organizations via `organization_slug`.
  </Card>

  <Card title="The Volunteer Data Model" icon="diagram-project" href="/volunteer/concepts/data-model">
    The full data model context for organizations.
  </Card>
</CardGroup>
