Gigs, products, ensembles

Most systems revolve around the customer. Not here. Here the complexity starts, ends, and loops all around the gig.

The gig

A gig is one engagement: a date, a venue, a party name, a contract number, a status and a type. Everything else hangs off it.

Statuses and types are the band's own lists, at /gig-statuses and /gig-types; a gig points at one of each with gig_status_id and gig_type_id. A status can be called anything ("Hold", "Contract sent"), and each belongs to one category the system understands: inquiry, proposal, booked, completed or cancelled. A new band starts with one status per category and a short list of types.

Hangs off a gig Route What it is
Products /gigs/{id}/products The segments contracted for this gig, e.g. Ceremony, Cocktail Hour, Reception.
Roles /gigs/{id}/roles Every staffing slot on the gig, across all products.
Contacts /gigs/{id}/contacts The people connected to it, with the role each plays (Bride, Planner, Photographer, …).

Products

Ceremony, cocktail hour, reception. Those are your products — the catalog of what you sell (/products). Each has prices (/products/{id}/prices) and a list of the ensembles that can fulfil it (/products/{id}/ensembles). A production product (sound and lighting) can point at the entertainment product it supports. A product that names a performer — a requested performer, overtime for one person — sells as a line that points at a staffing slot.

When a gig is sold, a product is instantiated on it as a gig product: a line, price × quantity, with its start and end time, location, and, once staffed, the ensemble that plays it. The money side of a gig product is described under Pricing.

Pricing

The shapes are Stripe's. A price (/prices) is one number for a product, in minor units (cents), or a list of tiers — never a formula. It can be for one ensemble (the 10-piece's reception price) or for the product however it is played. Its numbers are write-once: to change a price you retire it (status: inactive) and mint another, so a line sold last year still points at the number it was sold at.

# the 10-piece's reception price, and a tiered one: $220 for the first 8 lights, $27.50 each after
curl -X POST $BASE/products/$RECEPTION/prices -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"label":"10-piece","ensemble_id":"…","unit_amount":1390000,"lookup_key":"reception.10pc"}'
curl -X POST $BASE/products/$UPLIGHTS/prices -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"billing_scheme":"tiered","tiers":[{"up_to":8,"flat_amount":22000},{"up_to":null,"unit_amount":2750}],"lookup_key":"uplights"}'

A gig product is a line. Adding one resolves the catalog price for its product and ensemble and copies unit_amount onto the line (price_id keeps the provenance); give price_id to pick one, or unit_amount with no price for an ad hoc line; a product with no price and no amount goes on the gig at no charge. quantity multiplies; a tiered price prices the quantity itself. A child line (parent_gig_product_id) with display: folded prints inside its parent — travel inside the band's price. Every line returns amount_subtotal, amount_discount and amount_total, computed; ?expand=price returns the price inline.

A line of a product that names a performer must carry gig_role_id, the slot it pays for. That is how a slot becomes client-requested: /gig-roles/{id}/products lists the lines paying for it, and there is no flag to keep in step.

A coupon (/coupons) is a reason the band named once — Preferred planner 5%, Repeat client 10%, Included 100% — with a level: line, gig or either. A gig discount (/gigs/{id}/discounts, /gig-products/{id}/discounts) applies one: on a line (gig_product_id) or on the gig. The coupon's numbers and label are copied onto the discount at the time. Line discounts come off first, each on what is left of its line; gig-level discounts come off what is left of the discountable lines of the same contract. Each discount returns the amount it took off, computed.

A gig contract (/gigs/{id}/contracts) is a signed contract or addendum (kind), imported from the band's paper: its number and signed_at as printed, and the PDF as attachment_id. Orpheus doesn't draft or send contracts; it records what was signed. Lines and discounts carry gig_contract_id, so a contract's amount_total is what it sold, and it should equal the total the PDF prints. A misread is fixed in place. The number of the gig's contract (not an addendum) is copied onto the gig as contract_number.

A gig payment (/gigs/{id}/payments) is a slice of the total: a fixed amount, or a percent_of_total of its contract (or the gig) that follows the total as lines change. Retainer at signing, balance a month out, the addendum due with the balance.

# import a signed contract: the contract, the 10-piece and its slots, a requested performer on a slot, discounts, payments
curl -X POST $BASE/gigs/$GIG/contracts … -d '{"label":"Performance Agreement","number":"271106-IE1","signed_at":"2026-08-13T15:00:00Z"}'
curl -X POST $BASE/gigs/$GIG/products … -d '{"product_id":"$RECEPTION","ensemble_id":"$TEN","gig_contract_id":"$CONTRACT"}'  # → unit_amount 1390000
curl -X POST $BASE/gig-products/$BAND_LINE/apply-ensemble … -d '{"ensemble_id":"$TEN"}'                            # the slots
curl -X POST $BASE/gigs/$GIG/products … -d '{"product_id":"$CRP","gig_role_id":"$DRUMS_SLOT","gig_contract_id":"$CONTRACT"}'  # 500 each
curl -X POST $BASE/gig-products/$CRP_LINE/discounts … -d '{"gig_id":"$GIG","coupon_id":"$INCLUDED"}'               # the first one included
curl -X POST $BASE/gigs/$GIG/discounts … -d '{"coupon_id":"$PLANNER","gig_contract_id":"$CONTRACT"}'               # 5% off the rest
curl -X POST $BASE/gigs/$GIG/payments … -d '{"label":"Retainer","percent_of_total":50,"gig_contract_id":"$CONTRACT"}'

Amounts are integers in minor units everywhere on the client side. The pay side (rates, gig_roles.computed_*) keeps its own units for now.

Ensembles

Your bands, from the 2-piece cocktail hour players to the 17-piece reception band. An ensemble is a reusable lineup (/ensembles) built from roles (/ensembles/{id}/roles): Drums, Bass, Lead Vocal, FOH Engineer. Roles come from a catalog (/roles) grouped into teams (/teams) — Rhythm Section, Horns, Vocals, Production.

You staff them, repeat them, make and re-make them each year. The ensemble is a template; a gig gets a copy.

Staffing a gig, end to end

# 1. put the Reception product on the gig
curl -X POST $BASE/gigs/$GIG/products -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"product_id":"…","label":"Reception","status":"Confirmed","start_time":"20:00","end_time":"23:30"}'

# 2. apply the 10-piece: every role in the ensemble becomes a gig role on this product
curl -X POST $BASE/gig-products/$GIG_PRODUCT/apply-ensemble -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"ensemble_id":"…"}'

# 3. invite a drummer to the Drums slot; rounds are numbered per role
curl -X POST $BASE/gig-roles/$ROLE/invite -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"staff_member_id":"…","notes":"Call 17:00"}'

# 4. record the answer; accepted assigns the person to the role
curl -X POST $BASE/gig-role-invitations/$INVITATION/respond -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"response":"accepted"}'

A new gig opens with one unfilled gig role for every catalog role marked default_on_gig (a new band starts with Band Leader and Coordinator). These sit on the gig itself, not on a product, and are staffed like any other slot.

Applying an ensemble copies its roles into gig_roles; editing the ensemble later never rewrites a gig that has already been staffed. A gig role remembers its latest invitation, the assigned staff member, call time, and the computed pay lines once a rate is applied.

Staff

Staff members (/staff-members) are the people who work gigs. Their capabilities are staff_member_roles (which catalog roles they can fill, with a call order), their unavailability is staff_member_blockouts, and their pay structure per product is staff_member_product_rates. Each is reachable under the person: /staff-members/{id}/roles, /blockouts, /product-rates, /gig-roles.

Contacts

Contacts (/contacts) are the client side: couples, parents, planners, vendors. Identity only. The role a contact plays is a fact about the pairing with a gig and lives on the gig contact, never on the contact.

Custom fields

A band can add its own fields to any table. Each one is declared at /custom-fields and stored as a real column, so it is validated, returned, filtered and sorted exactly like a built-in field.

# add a "Deposit" number field to gigs; its key becomes x_deposit
curl -X POST https://api.hireorpheus.com/api/v1/custom-fields \
  -H 'Authorization: Bearer <token>' -H 'Content-Type: application/json' \
  -d '{"label":"Deposit","entity":"gigs","field_type":"number"}'

# use it like any other field
curl -X PATCH https://api.hireorpheus.com/api/v1/gigs/<id> … -d '{"x_deposit":1500}'
curl -G https://api.hireorpheus.com/api/v1/gigs --data-urlencode 'filter[x_deposit][greater_than]=1000'
  • Keys always start with x_, so a custom field can never clash with a field added to the core model later. Send a key or let it be derived from the label.
  • Types: rich_text, number, currency, date, time, checkbox, select, multi_select, url, email, phone_number, relation. A select or multi_select needs options; a relation needs the table it points at.
  • The table, key and type are fixed once created. The label, options, description, order and required can change.
  • required is checked when a record is created; existing records are not affected.
  • DELETE retires a field: it disappears from the API, and its data is kept. Set status back to active to restore it.
  • The OpenAPI document served by a band's own API (/openapi.json) includes that band's custom fields. This reference shows the core model only.

Moments and songs

Moments (/moments) are the band's list of named points in an event: "Processional" in the ceremony, "First dance" and "Cake cutting" in the reception. A moment can belong to the product it happens during (/products/{id}/moments) and has a usual running order.

When a product is added to a gig, that product's moments marked default_on_product are copied onto it as gig moments (/gig-products/{id}/moments): this wedding's first dance, with its own time, order and notes. The copy is made once, so editing the band's list later never rewrites a gig. Add a rarely used moment from the list by posting its moment_id, or a one-off by posting just a label.

Songs (/songs) are the band's library. The core model keeps a title and an artist; key, tempo, variant and anything else a band tracks are custom fields. List only what the band offers with filter[status]=active.

Gig songs (/gig-products/{id}/songs) are the songs on one gig, each on a named list:

list What it is
request A song the couple asked for, usually for a moment such as the first dance
set_list The set list: a set, an order, a singer
favorite A song the couple picked from the library
playlist A song or playlist link for a part the band does not perform
do_not_play A song the couple never wants to hear

A request starts with only what the couple wrote, and nothing is added to the library. The band leader then answers "do we already have it?":

# yes: connect the library song
curl -X POST …/gig-songs/<id>/connect -d '{"song_id":"…"}'

# no, or it needs its own version: create a song in production and connect it
curl -X POST …/gig-songs/<id>/create-song -d '{"producer_id":"…"}'

# the producer finishes: the song joins the library
curl -X POST …/songs/<song id>/track-complete

Every request carries a read-only stage worked out from those facts: needs_decision, in_production, ready, then complete or set_aside (/gig-songs/{id}/complete, /set-aside, /reopen). Favorites and the set list only accept active songs.

Equipment

Production runs on the same pattern as everything else: the band's list, and a copy on the gig.

Equipment types (/equipment-types) are the band's kinds of equipment: uplights, pin spots, stage decks, speakers, drum kits, box trucks. Each has a category (lighting, sound, backline, stage, vehicle, other) and a tracking:

tracking What it means
quantity Counted. The band has quantity_owned of them, and a gig books a number: 12 uplights.
unit Each one is named. The units are rows in equipment (/equipment): truck CV 2, the company drum kit. A gig books a unit by name.

A unit has an identifier (plate, serial, asset tag), ownership (owned or rented) and a status: in_repair and retired units can't be booked onto new gigs. A plate or a VIN that only one band cares about is a custom field on equipment.

Product equipment (/products/{id}/equipment) is a product's default kit: the Elegant Lighting Rig is 12 uplights and 2 pin spots. When the product is added to a gig, its kit is copied onto the gig, the way its default moments are, and edited freely from there.

Gig equipment (/gigs/{id}/equipment, /gig-products/{id}/equipment) is everything going to a gig: a counted kind with a quantity, or one unit by equipment_id, optionally for one of the gig's products, with the staff_member_id responsible for it (the truck's driver, the rig's tech). A unit can be on one gig a day; booking it twice is a 409.

# the truck, with its driver
curl -X POST $BASE/gigs/$GIG/equipment -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"equipment_id":"…","staff_member_id":"…"}'

# load it, and bring it back
curl -X POST $BASE/gig-equipment/<id>/load -H "$AUTH"
curl -X POST $BASE/gig-equipment/<id>/return -H "$AUTH"

Every row carries a read-only status worked out from those calls: planned, loaded, then returned, with when and by which staff member (loaded_at, loaded_by_id, returned_at, returned_by_id). The staff member is the one the signed-in account is linked to, or name another in the body ({"loaded_by_id":"…"}, {"returned_by_id":"…"}) when the office marks it for the crew. /reset puts it back to planned.

How many uplights are booked on a Saturday, and is that more than the band has?

curl -X POST $BASE/equipment-types/<id>/availability -H "$AUTH" -H 'Content-Type: application/json' -d '{"date":"2027-06-12"}'
# { "owned": 40, "booked": 42, "available": -2, "gigs": [ … ] }

Equipment issues (/equipment/{id}/issues) are what's wrong with, or due on, a unit: a lift gate that sticks, an oil change, a registration to renew. Each has a kind (repair, maintenance, damage, paperwork, other), optionally the gig it happened on, and a read-only status, open until /resolve (/reopen undoes it).

Attachments

Attachments (/attachments) are files on any record: a truck's registration, a gobo design, a venue's load-in map. An attachment holds a link to where the file lives (url, content_type, size_bytes); uploading the file is up to you. Post one under the record it belongs to, e.g. /equipment/{id}/attachments, and its entity and record_id are filled in.