# CommonGrants Protocol Data Models: Zod Schemas for Grant Opportunities

> Explore CommonGrants protocol data models using Zod schemas. Discover OpportunityBase, OppStatus, OppFunding, and OppTimeline definitions for grant opportunities. Learn more.

- Repository: [U.S. Department of Health & Human Services/simpler-grants-protocol](https://github.com/hhs/simpler-grants-protocol)
- Tags: api-reference
- Published: 2026-03-03

---

**The CommonGrants protocol defines grant opportunity data as Zod schemas, including OpportunityBase, OppStatus, OppFunding, OppTimeline, and filtering/sorting models, implemented in both the Express.js reference API and TypeScript SDK.**

The CommonGrants protocol provides a standardized way to represent grant opportunities through strict TypeScript validation. In the `hhs/simpler-grants-protocol` repository, every element of a grant is modeled as a **Zod schema**, ensuring runtime type safety across the reference API and client SDK.

## Core Data Models in the CommonGrants Protocol

The protocol centers on the **OpportunityBase** model, which composes several embedded entities describing status, funding, timeline, and eligibility.

### OpportunityBase

The **OpportunityBaseSchema** serves as the central object combining all grant metadata. Defined in [`lib/ts-sdk/src/schemas/zod/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/models.ts) (lines 15-45) and mirrored in [`templates/express-js/src/api/schemas/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/templates/express-js/src/api/schemas/models.ts) (lines 78-105), this schema includes:

- A UUID identifier validated by **UuidSchema**
- Title and description fields
- Embedded **OppStatus**, **OppFunding**, and **OppTimeline** objects
- Source URL and custom fields map
- **SystemMetadataSchema** for created/modified timestamps

### OppStatus

The **OppStatusSchema** enumerates opportunity states: `forecasted`, `open`, `closed`, or `custom`. When using the `custom` value, the schema accepts an optional `customValue` string and `description` field. This schema appears in both [`templates/express-js/src/api/schemas/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/templates/express-js/src/api/schemas/models.ts) (lines 14-29) and [`lib/ts-sdk/src/schemas/zod/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/models.ts) (lines 15-26).

### OppFunding

Financial details are encapsulated in **OppFundingSchema**, found in [`lib/ts-sdk/src/schemas/zod/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/models.ts) (lines 69-92). This model tracks:

- Total amount available via **MoneySchema**
- Minimum and maximum award amounts
- Award count ranges (minimum and maximum)
- Optional free-form `details` string

### OppTimeline

The **OppTimelineSchema** holds critical dates including post/open date, close date, and any additional `otherDates` array. This schema ensures consistent date formatting across implementations and appears in both the Express.js and SDK versions of [`models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/models.ts).

### ApplicantType

The **ApplicantTypeSchema** appears exclusively in the SDK at [`lib/ts-sdk/src/schemas/zod/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/models.ts) (lines 32-64). It categorizes eligible applicants as individuals, organizations, or various government levels (local, state, federal), with a `custom` fallback for implementation-specific categories.

## Query and Filter Models

Beyond base opportunity data, the CommonGrants protocol defines schemas for searching and filtering grant listings.

### OppSortBy and OppSorting

The **OppSortByEnum** (or `oppSortByEnum` in Express.js) enumerates sortable fields: `lastModifiedAt`, `title`, `status.value`, `keyDates.closeDate`, and funding amounts. Defined in [`lib/ts-sdk/src/schemas/zod/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/models.ts) (lines 50-61), this enum drives the sorting capabilities of the protocol.

The **OppSortingSchema** wraps a `sortBy` value with an optional `sortOrder` (`asc`/`desc`). The SDK version additionally supports a `customSortBy` string for implementation-specific sorting keys.

### OppDefaultFilters and OppFilters

**OppDefaultFiltersSchema** provides pre-defined filter parameters including status arrays, close-date ranges, and funding amount ranges. **OppFiltersSchema** extends these defaults with an optional `customFilters` map, allowing any implementation-defined filter fields. These schemas enable the searchable API endpoints implemented in [`lib/ts-sdk/src/client/opportunities.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/client/opportunities.ts).

## Reusable Field Schemas

The protocol relies on auxiliary building blocks defined in [`lib/ts-sdk/src/schemas/zod/fields.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/fields.ts) and [`lib/ts-sdk/src/schemas/zod/filters.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/filters.ts):

- **EventSchema**: Represents date events with descriptions
- **MoneySchema**: Combines numeric amount and ISO currency code
- **CustomFieldSchema**: Supports arbitrary key-value pairs with type annotations (`string`, `enum`, etc.)
- **SystemMetadataSchema**: Tracks creation and modification timestamps
- **UuidSchema**: Validates UUIDv4 identifiers
- Filter schemas: `StringArrayFilterSchema`, `DateRangeFilterSchema`, etc.

## Implementation Differences: Express.js vs. TypeScript SDK

While both implementations share logical entity definitions, the **TypeScript SDK** (`lib/ts-sdk`) differs from the **Express.js reference API** (`templates/express-js`) in two key ways:

1. **Nullability**: The SDK uses `nullish()` instead of `optional()`, allowing explicit null values rather than undefined
2. **Applicant Types**: Only the SDK includes the **ApplicantTypeSchema**, as applicant categorization primarily concerns client-side filtering and display logic

## Working with CommonGrants Data Models

Developers interact with these schemas through runtime validation and TypeScript type inference (`z.infer<typeof Schema>`).

### Validate a Raw Opportunity Payload

```typescript
import {
  OpportunityBaseSchema,
} from "@hhs/simpler-grants-protocol-sdk";

// Example payload (could come from an HTTP request, a file, etc.)
const raw = {
  id: "d5f5e5c2-4c8b-4b1f-9a5f-0b9afc3a1e57",
  title: "Community Health Grant 2025",
  status: { value: "open" },
  description: "Funding for community health initiatives.",
  funding: { totalAmountAvailable: { amount: 500000, currency: "USD" } },
  keyDates: { postDate: { date: "2024-11-01T00:00:00Z" } },
  source: "https://example.org/grants/123",
};

try {
  const opportunity = OpportunityBaseSchema.parse(raw);
  console.log("✅ Valid opportunity:", opportunity);
} catch (e) {
  console.error("❌ Validation error:", e);
}

```

This validates the structure against the `OpportunityBaseSchema` definition in [`lib/ts-sdk/src/schemas/zod/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/models.ts).

### Build a Search Request with Sorting and Filters

```typescript
import {
  OppSortingSchema,
  OppFiltersSchema,
} from "@hhs/simpler-grants-protocol-sdk";

const sorting = OppSortingSchema.parse({
  sortBy: "funding.maxAwardAmount",
  sortOrder: "desc",
});

const filters = OppFiltersSchema.parse({
  status: { in: ["open", "forecasted"] },
  totalFundingAvailableRange: { gte: 100000, lte: 1000000 },
});

console.log("Sorting:", sorting);
console.log("Filters:", filters);

```

These schemas correspond to the definitions in [`lib/ts-sdk/src/schemas/zod/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/models.ts) lines 50-89.

### Extend the Base Model with Custom Fields

```typescript
import { OpportunityBaseSchema } from "@hhs/simpler-grants-protocol-sdk";

const opportunityWithCustom = OpportunityBaseSchema.parse({
  id: "a1b2c3d4-5678-90ab-cdef-1234567890ab",
  title: "Education Innovation Grant",
  status: { value: "custom", customValue: "pending_review" },
  description: "Supports novel educational tools.",
  customFields: {
    // Arbitrary key/value pairs defined by the implementer
    programArea: { type: "string", value: "STEM" },
    eligibility: { type: "enum", value: "non-profit" },
  },
});

```

The `customFields` property accepts any key-value pairs conforming to `CustomFieldSchema`, as defined in [`templates/express-js/src/api/schemas/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/templates/express-js/src/api/schemas/models.ts) and mirrored in the SDK.

## Summary

- The **CommonGrants protocol data models** are implemented as Zod schemas in `hhs/simpler-grants-protocol`
- **OpportunityBaseSchema** serves as the central entity, composing status, funding, and timeline data via embedded objects
- **ApplicantTypeSchema**, **OppSortingSchema**, and **OppFiltersSchema** support client-side filtering, sorting, and eligibility categorization
- Schemas exist in parallel implementations: the Express.js reference API and the TypeScript SDK (which uses `nullish` and adds applicant types)
- All models provide runtime validation via `.parse()` and TypeScript type inference for compile-time safety

## Frequently Asked Questions

### What is the primary schema for grant opportunities in the CommonGrants protocol?

The **OpportunityBaseSchema** is the primary schema representing a grant opportunity. Defined in [`lib/ts-sdk/src/schemas/zod/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/models.ts) and [`templates/express-js/src/api/schemas/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/templates/express-js/src/api/schemas/models.ts), it combines metadata (UUID, title, description), embedded objects for status and funding, and system metadata like creation timestamps.

### How does the TypeScript SDK differ from the Express.js reference implementation?

The TypeScript SDK uses `nullish()` instead of `optional()` for optional fields, allowing explicit null values. It also includes the **ApplicantTypeSchema** for categorizing eligible applicants, which does not appear in the Express.js template. Both implementations maintain identical logical structures for core opportunity data.

### Can I add custom fields to grant opportunities?

Yes. The **OpportunityBaseSchema** includes a `customFields` property that accepts any key-value pairs conforming to **CustomFieldSchema**. This allows implementations to extend the base model with program-specific data while maintaining protocol compliance.

### Where are the filter and sorting schemas defined?

The **OppSortingSchema** and **OppFiltersSchema** are defined in [`lib/ts-sdk/src/schemas/zod/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/lib/ts-sdk/src/schemas/zod/models.ts) (lines 50-96) and mirrored in [`templates/express-js/src/api/schemas/models.ts`](https://github.com/hhs/simpler-grants-protocol/blob/main/templates/express-js/src/api/schemas/models.ts). These schemas support querying opportunities by status, funding ranges, dates, and custom filter parameters.