Promo Codes
MLH promo codes let sponsors and event organizers hand out perks — free credits, discounts, or access links — to participants. Three resources work together:
- A promo code pool groups related codes and holds the settings they share: who the codes come from (an MLH sponsorship or an event), how many times each person can redeem, when they expire, and where the codes are distributed.
- A promo code is an individual code that belongs to a pool. It carries the
actual
codestring, its own redemption limit, and whether it's active. - A promo code redemption records one user using one code — a read-only audit trail of who redeemed what, and when.
All requests are authenticated — see Authentication — and follow the shared conventions in Making requests for pagination, filtering, and expanding related data.
Promo Code Pools
A pool is the container you create first; every promo code points back to a pool
through its promo_code_pool_id.
Endpoints
| Method | Path | What it does |
|---|---|---|
GET | /v4/promo_code_pools | List promo code pools |
GET | /v4/promo_code_pools/{id} | Get one promo code pool |
POST | /v4/promo_code_pools | Create a promo code pool |
PATCH / PUT | /v4/promo_code_pools/{id} | Update a promo code pool |
DELETE | /v4/promo_code_pools/{id} | Delete a promo code pool |
GET | /v4/promo_code_pools/new | Get a blank set of pool attributes |
GET | /v4/promo_code_pools/{id}/edit | Get an existing pool's attributes, prefilled |
The new and {id}/edit endpoints are form helpers. They don't create or
change anything — they return a plain attributes object you can use to build a
create or edit form. new returns a blank template of the fields you can set;
{id}/edit returns the same shape prefilled with an existing pool's values.
Fields
Set these in the request body on create and update. On reads, created_at,
updated_at, and expires_at come back as Unix timestamps (integers,
seconds); in the request body, datetimes are sent as ISO 8601 strings.
| Field | Required | What it does |
|---|---|---|
label | Optional | A short human-readable name for the pool. Example: "GHW 2026 Credits". |
description | Optional | A longer description of what the codes are for. Example: "$50 in cloud credits for GHW participants". |
code_type | Optional | How codes in the pool are shaped. One of individualized (a unique code per person) or shared (one code many people use). Example: "individualized". |
access_type | Optional | How participants get a code. One of automatic (handed out by the system) or manual (distributed by hand). Example: "automatic". |
mlh_sponsorship_id | Optional | The ID of the MLH sponsorship this pool belongs to. Example: "mspon_a1b2c3". See MLH Sponsorships. |
event_id | Optional | The ID of the event this pool is tied to. Example: "evt_x9y8z7". See Events. |
per_user_limit | Optional | How many times a single user may redeem from this pool. Example: 1. |
link_expiration_days | Optional | How many days a generated redemption link stays valid. Example: 7. |
redemption_url | Optional | The URL a participant is sent to in order to redeem. Example: "https://sponsor.example.com/redeem". |
restrictions | Optional | Free-form text describing any limits on who can use the codes. Example: "US students only". |
distribution_channels | Optional | An array of channels the codes are distributed through. Example: ["email", "dashboard"]. |
active | Optional | Whether the pool is currently active. Example: true. |
expires_at | Optional | When the pool expires. ISO 8601 string on write; Unix timestamp on read. Example: "2026-12-31T23:59:59Z". |
id | Optional | The pool's unique ID. Read-only; set by the API. |
created_at | Optional | When the pool was created. Read-only on responses (Unix timestamp). |
updated_at | Optional | When the pool was last updated. Read-only on responses (Unix timestamp). |
List results can be filtered by code_type, access_type, active,
mlh_sponsorship, event, created_at, updated_at, and expires_at, and
expanded with expand. See Making requests.
Scopes
Requests authenticate with OAuth 2.0 or a bearer JWT, as described in Authentication. Request only the scopes your integration needs — see the full list of scopes.
Example
Create a pool:
POST /v4/promo_code_pools
Content-Type: application/json
{
"label": "GHW 2026 Credits",
"description": "$50 in cloud credits for GHW participants",
"code_type": "individualized",
"access_type": "automatic",
"mlh_sponsorship_id": "mspon_a1b2c3",
"per_user_limit": 1,
"active": true,
"expires_at": "2026-12-31T23:59:59Z"
}
Promo Codes
A promo code is an individual code inside a pool. Create the pool first, then create codes that reference it.
Endpoints
| Method | Path | What it does |
|---|---|---|
GET | /v4/promo_codes | List promo codes |
GET | /v4/promo_codes/{id} | Get one promo code |
POST | /v4/promo_codes | Create a promo code |
PATCH / PUT | /v4/promo_codes/{id} | Update a promo code |
Fields
Set these in the request body on create and update. On reads, created_at,
updated_at, and expires_at come back as Unix timestamps (integers,
seconds); in the request body, datetimes are sent as ISO 8601 strings.
| Field | Required | What it does |
|---|---|---|
code | Optional | The code string a participant enters or follows. Example: "MLH-GHW-A1B2C3". |
promo_code_pool_id | Optional | The ID of the pool this code belongs to. Example: "pcp_a1b2c3". |
redemption_limit | Optional | The maximum number of times this specific code can be redeemed. Example: 100. |
active | Optional | Whether the code can currently be redeemed. Example: true. |
expires_at | Optional | When this code stops working. ISO 8601 string on write; Unix timestamp on read. Example: "2026-12-31T23:59:59Z". |
mlh_sponsorship | Optional | The ID of the associated MLH sponsorship. Example: "mspon_a1b2c3". See MLH Sponsorships. |
events | Optional | An array of event IDs the code applies to. Example: ["evt_x9y8z7"]. See Events. |
id | Optional | The code's unique ID. Read-only; set by the API. |
created_at | Optional | When the code was created. Read-only on responses (Unix timestamp). |
updated_at | Optional | When the code was last updated. Read-only on responses (Unix timestamp). |
When you read a code, promo_code_pool_id is the pool's ID; promo_code_pool
holds the expanded pool when you request it. List results can be filtered by
code, redemption_limit, active, created_at, updated_at, and
expires_at. Use expand[] to inline related records — the expandable
associations are promo_code_pool, mlh_sponsorship, and events:
GET /v4/promo_codes/{id}?expand[]=promo_code_pool&expand[]=events
See Making requests for how
expand works across the API.
Scopes
Requests authenticate with OAuth 2.0 or a bearer JWT, as described in Authentication. Request only the scopes your integration needs — see the full list of scopes.
Example
Create a code in a pool:
POST /v4/promo_codes
Content-Type: application/json
{
"code": "MLH-GHW-A1B2C3",
"promo_code_pool_id": "pcp_a1b2c3",
"redemption_limit": 100,
"active": true,
"expires_at": "2026-12-31T23:59:59Z"
}
Promo Code Redemptions
A redemption records one user redeeming one promo code. This resource is read-only — redemptions are created by the redemption flow, not through the API. You can list them and get one by ID.
Endpoints
| Method | Path | What it does |
|---|---|---|
GET | /v4/promo_code_redemptions | List promo code redemptions |
GET | /v4/promo_code_redemptions/{id} | Get one promo code redemption |
Fields
All fields are read-only. created_at, updated_at, and redeemed_at are
returned as Unix timestamps (integers, seconds).
| Field | Type | Notes |
|---|---|---|
id | string | The redemption's unique ID. |
promo_code_id | string | The ID of the promo code that was redeemed. |
user_id | string | The ID of the user who redeemed it. |
event_id | string | The ID of the event the redemption happened at, if any. See Events. |
redeemed_at | integer | When the code was redeemed, as a Unix timestamp (seconds). |
created_at | integer | When the redemption record was created, as a Unix timestamp (seconds). |
updated_at | integer | When the redemption record was last updated, as a Unix timestamp (seconds). |
List results can be filtered by promo_code, user, event, and redeemed_at,
and expanded with expand. See Making requests.
Scopes
Requests authenticate with OAuth 2.0 or a bearer JWT, as described in Authentication. Request only the scopes your integration needs — see the full list of scopes.
Related
- V4 API overview
- Making requests — pagination, filtering, expanding, and errors.
- Authentication
- MLH Sponsorships
- Events