Examples
Each example is a complete integration to adapt: the goal, the operations it uses, the GraphQL, and
TypeScript where paging or several steps are involved. All runnable samples on this page are
TypeScript. They use fetch and no client library.
The IDs in the examples ("1001", "1024" and so on) stand in for real ones. Look them up with
eventTypes, members
and teams.
Shared setup
Section titled “Shared setup”Every example below uses these two helpers in temprix-client.ts. request sends one operation and
throws on errors. listAll reads a whole list by following nextToken, as described on
Conventions.
const ENDPOINT = "https://graphql.euc1.temprix.app/graphql"; // your workspace's regionconst TOKEN = process.env.TEMPRIX_API_KEY!;
type Page<T> = { items: T[]; nextToken: string | null };
async function request<T>( query: string, variables: Record<string, unknown> = {},): Promise<T> { const res = await fetch(ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", Authorization: TOKEN }, body: JSON.stringify({ query, variables }), }); if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = await res.json(); if (body.errors?.length) throw new Error(JSON.stringify(body.errors)); return body.data as T;}
// The query must declare $nextToken and return { items, nextToken } under `field`.async function listAll<T>( field: string, query: string, variables: Record<string, unknown> = {},): Promise<T[]> { const items: T[] = []; let nextToken: string | null = null; do { const data: Record<string, Page<T>> = await request< Record<string, Page<T>> >(query, { ...variables, nextToken, }); items.push(...data[field].items); nextToken = data[field].nextToken; } while (nextToken !== null); return items;}Examples 1 and 2 need a read-only key. Examples 3 and 4 write, so they need a key with read and write access. See API keys.
Who is away on a date
Section titled “Who is away on a date”Goal: for one team and one day, list who is partly or fully unavailable, and why. Useful for flagging work owned by someone who is away.
Operations: teamMembers,
memberEvents,
groupEvents,
members.
Events carry memberId, not the member, so the example reads the team’s members first and resolves
names at the end. A one-day range has the same from and to.
import { listAll } from "./temprix-client";
const TEAM_MEMBERS = ` query ($teamId: ID!, $nextToken: String) { teamMembers(teamId: $teamId, nextToken: $nextToken) { items { memberId } nextToken } }`;
const MEMBER_EVENTS = ` query ($day: AWSDate!, $memberIds: [ID!], $nextToken: String) { memberEvents(between: { from: $day, to: $day }, memberIdIn: $memberIds, nextToken: $nextToken) { items { memberId eventTypeId availability } nextToken } }`;
const GROUP_EVENTS = ` query ($day: AWSDate!, $memberIds: [ID!], $nextToken: String) { groupEvents(between: { from: $day, to: $day }, memberIdIn: $memberIds, nextToken: $nextToken) { items { title availability countryCode subdivisionCode locality } nextToken } }`;
const MEMBERS = ` query ($ids: [ID!], $nextToken: String) { members(idIn: $ids, nextToken: $nextToken) { items { id displayName } nextToken } }`;
export async function awayOn(teamId: string, day: string) { const links = await listAll<{ memberId: string }>( "teamMembers", TEAM_MEMBERS, { teamId }, ); const memberIds = links.map((l) => l.memberId);
const [events, holidays, members] = await Promise.all([ listAll<{ memberId: string; eventTypeId: string; availability: number; }>("memberEvents", MEMBER_EVENTS, { day, memberIds }), listAll<{ title: string; availability: number }>( "groupEvents", GROUP_EVENTS, { day, memberIds }, ), listAll<{ id: string; displayName: string }>("members", MEMBERS, { ids: memberIds, }), ]);
const away = members .map((m) => ({ name: m.displayName, events: events.filter( (e) => e.memberId === m.id && e.availability < 1, ), })) .filter((m) => m.events.length > 0);
return { away, groupEvents: holidays.filter((h) => h.availability < 1) };}Group events match members by location, not by team. groupEvents with memberIdIn returns the
ones that reach any of those members. To see which members a group event reaches, compare its
countryCode, subdivisionCode and locality with the members’. The rule is on
Group events.
Where a member has several events on the same day, the lowest availability applies. Values are
never added or multiplied. This example does not account for non-working weekdays or a member’s
reduced baseline. If the team’s headcount is needed rather than a list of names, use the next
example.
Coverage in a dashboard
Section titled “Coverage in a dashboard”Goal: put a team’s coverage for the next twelve weeks into a dashboard already in use, refreshed daily.
Operation: coverageOutlook.
import { request } from "./temprix-client";
const TEAM_COVERAGE = ` query ($teamId: ID!, $from: AWSDate!, $to: AWSDate!) { coverageOutlook( teamId: $teamId between: { from: $from, to: $to } granularity: week coverageMetric: membersAvailable ) { target teamSize periods { date coverage isWorkingDay } } }`;
function addDays(isoDate: string, days: number): string { const d = new Date(`${isoDate}T00:00:00Z`); d.setUTCDate(d.getUTCDate() + days); return d.toISOString().slice(0, 10);}
export async function nextTwelveWeeks(teamId: string) { const from = new Date().toISOString().slice(0, 10); // today, UTC const to = addDays(from, 83); // 84 days, both ends included
const { coverageOutlook } = await request<{ coverageOutlook: { target: number; teamSize: number; periods: { date: string; coverage: number; isWorkingDay: boolean; }[]; }; }>(TEAM_COVERAGE, { teamId, from, to });
return coverageOutlook.periods.map((p) => ({ week: p.date, people: p.coverage, belowTarget: p.coverage < coverageOutlook.target, }));}coverage is in people. A week’s value is the average of the included days in it.
The range is forward-looking. It can start at most seven days before today, in UTC, and span up to 180 days. For past periods, export the report as CSV from Coverage Outlook.
Leave from an approval flow
Section titled “Leave from an approval flow”Goal: leave approved in an HR system appears in Temprix without anyone entering it twice.
Operations: createMemberEvent,
confirmMemberEvent,
deleteMemberEvent.
There are two ways to model it. Create the event as unconfirmed when the request is submitted, then confirm it on approval or delete it on rejection. Or create it already confirmed, only once it is approved. Choose the first if managers should see requested leave while they plan.
When a request is submitted:
mutation RequestLeave($data: CreateMemberEventInput!) { createMemberEvent(data: $data) { id }}{ "data": { "memberId": "1024", "eventTypeId": "1001", "startDate": "2026-10-12", "endDate": "2026-10-16", "timeZone": "Europe/Amsterdam", "availability": 0.0, "confirmed": false, "notes": "hr-request:48213" }}endDate is the last day away, so this is Monday 12 to Friday 16 October. Store the returned id
against the HR request.
When the request is approved, or rejected or withdrawn:
mutation Approve($id: ID!) { confirmMemberEvent(id: $id) { id confirmed }}
mutation Reject($id: ID!) { deleteMemberEvent(id: $id) { id }}Retries. createMemberEvent has no idempotency key. If a create times out, it may still have
succeeded, and sending it again makes a duplicate. Before retrying, look for the event that was
attempted: query memberEvents for that member and those dates, and match on the reference in
notes. notes is visible to people in the app, so keep the reference short and meaningful.
A rota from a workforce tool
Section titled “A rota from a workforce tool”Goal: a rotation planned in another tool, such as alternate weeks on second-line support, appears on each member’s timeline and in coverage.
Operations: createMemberEvent,
updateMemberEvent,
deleteMemberEvent.
Create the rotation as one recurring series. This one covers Monday to Friday, every other week, six
times, starting 5 October 2026. At 0.5 the member counts as half available for their usual work on
those days.
mutation CreateRota($data: CreateMemberEventInput!) { createMemberEvent(data: $data) { id }}{ "data": { "memberId": "1024", "eventTypeId": "1003", "startDate": "2026-10-05", "endDate": "2026-10-09", "timeZone": "Europe/Amsterdam", "availability": 0.5, "confirmed": true, "recurrence": { "frequency": "weekly", "interval": 2, "count": 6 } }}The series is stored once. memberEvents returns its
occurrences inside the range asked for, each with an occurrenceIndex starting at 1.
To change part of the series, pass the occurrence id from memberEvents. For virtual occurrences
it is the series id, a hyphen, and the 1-based occurrenceIndex (for example 1001-3). Pass a
flag:
flag |
Affects |
|---|---|
this |
Only that occurrence |
fromThis |
That occurrence and every one after it |
all |
The whole series |
Moving the third rotation (2 to 6 November) a week later, leaving the others alone:
mutation MoveOne($id: ID!) { updateMemberEvent( id: $id flag: this data: { startDate: "2026-11-09", endDate: "2026-11-13" } ) { id }}Deleting returns one of two things, and both should be selected:
mutation Cancel($id: ID!, $flag: RecurrenceChangeFlag) { deleteMemberEvent(id: $id, flag: $flag) { id memberEvent { id recurrenceDetails { deletedOccurrences } } }}- With
flag: all, or on an event that does not recur, the series is gone:idis set andmemberEventisnull. - With
flag: thisorfromThis, the series still exists with fewer occurrences:memberEventis the updated series andidisnull.
If the other tool owns the rota, keep its own reference in notes, as in the previous example, so
the next sync can find the series again.
Reading a full list
Section titled “Reading a full list”Every list in the API is paged, including short ones. Use listAll from temprix-client.ts above,
or the loop on Conventions, for every list read. Stop only
when nextToken is null. A page with no items is not the last page.