Skip to content

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.

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.

temprix-client.ts
const ENDPOINT = "https://graphql.euc1.temprix.app/graphql"; // your workspace's region
const 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.

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.

away-on-date.ts
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.

Goal: put a team’s coverage for the next twelve weeks into a dashboard already in use, refreshed daily.

Operation: coverageOutlook.

team-coverage.ts
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.

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:

request-leave.graphql
mutation RequestLeave($data: CreateMemberEventInput!) {
createMemberEvent(data: $data) {
id
}
}
request-leave-variables.json
{
"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:

approve-or-reject.graphql
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.

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.

create-rota.graphql
mutation CreateRota($data: CreateMemberEventInput!) {
createMemberEvent(data: $data) {
id
}
}
create-rota-variables.json
{
"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:

move-occurrence.graphql
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:

cancel-occurrence.graphql
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: id is set and memberEvent is null.
  • With flag: this or fromThis, the series still exists with fewer occurrences: memberEvent is the updated series and id is null.

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.

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.