Building an Integration

This guide covers the patterns you'll need to build a robust Rex integration — authentication, CRUD operations, pagination, error handling, and rate limit management.

Authentication setup

Store your API key as an environment variable, never in source code:

export REX_API_KEY="rex_live_..."
export REX_URL="https://acme.rexgtm.com"

Node.js / TypeScript

const REX_URL = process.env.REX_URL;
const REX_API_KEY = process.env.REX_API_KEY;

async function rex(path: string, options?: RequestInit) {
  const res = await fetch(`${REX_URL}${path}`, {
    ...options,
    headers: {
      "X-Api-Key": REX_API_KEY!,
      "Content-Type": "application/json",
      ...options?.headers,
    },
  });
  if (!res.ok) {
    const err = await res.json();
    throw new Error(`Rex API error: ${err.error.code} — ${err.error.message}`);
  }
  return res.json();
}

Python

import os, requests

REX_URL = os.environ["REX_URL"]
REX_API_KEY = os.environ["REX_API_KEY"]
HEADERS = {"X-Api-Key": REX_API_KEY, "Content-Type": "application/json"}

def rex(method, path, **kwargs):
    resp = requests.request(method, f"{REX_URL}{path}", headers=HEADERS, **kwargs)
    resp.raise_for_status()
    return resp.json()

CRUD operations

Create

const contact = await rex("/contacts", {
  method: "POST",
  body: JSON.stringify({
    email: "ada@example.com",
    first_name: "Ada",
    last_name: "Lovelace",
    stage: "lead",
  }),
});
console.log(contact.data.id); // "cont_01HQ..."

Read

const contact = await rex("/contacts/cont_01HQ...");
console.log(contact.data.email);

Update

await rex("/contacts/cont_01HQ...", {
  method: "PATCH",
  body: JSON.stringify({ stage: "prospect" }),
});

Delete

await rex("/contacts/cont_01HQ...", { method: "DELETE" });

Paginating through results

Rex uses cursor-based pagination. Here's how to iterate through all pages:

async function fetchAll(path: string) {
  const results = [];
  let cursor: string | undefined;

  do {
    const params = new URLSearchParams({ limit: "100" });
    if (cursor) params.set("cursor", cursor);

    const res = await rex(`${path}?${params}`);
    results.push(...res.data);
    cursor = res.meta.has_more ? res.meta.cursor : undefined;
  } while (cursor);

  return results;
}

const allContacts = await fetchAll("/contacts");

Handling errors

Rex returns structured errors. Always check the error.code field for programmatic handling:

try {
  await rex("/contacts", {
    method: "POST",
    body: JSON.stringify({ email: "invalid" }),
  });
} catch (err) {
  // err.message: "Rex API error: validation_failed — Invalid email address"
}

Common error codes:

HTTP StatusCodeMeaning
400validation_failedRequest body didn't pass validation
401unauthorizedMissing or invalid API key
403forbiddenKey scope doesn't allow this operation
404not_foundEntity doesn't exist
409conflictDuplicate or version conflict
429rate_limit_exceededToo many requests
500internal_errorSomething broke on our end

Rate limit handling

Implement backoff when you hit rate limits:

async function rexWithRetry(path: string, options?: RequestInit, retries = 3) {
  for (let i = 0; i < retries; i++) {
    const res = await fetch(`${REX_URL}${path}`, {
      ...options,
      headers: {
        "X-Api-Key": REX_API_KEY!,
        "Content-Type": "application/json",
        ...options?.headers,
      },
    });

    if (res.status === 429) {
      const retryAfter = parseInt(res.headers.get("Retry-After") || "5", 10);
      await new Promise((r) => setTimeout(r, retryAfter * 1000));
      continue;
    }

    if (!res.ok) {
      const err = await res.json();
      throw new Error(`${err.error.code}: ${err.error.message}`);
    }

    return res.json();
  }
  throw new Error("Max retries exceeded");
}

What's next?

Was this page helpful?

·