For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
DashboardGet an API key
Get StartedGuidesAPI ReferenceSDKsChangelog
Get StartedGuidesAPI ReferenceSDKsChangelog
  • Common workflows
    • Create your first customer
    • Onboarding phases
    • Integration sync
    • display_id vs id
    • Bulk operations
    • Test mode
LogoLogo
DashboardGet an API key
On this page
  • Prerequisites
  • The script
  • What happened
  • Trying it again safely
  • Cleaning up
  • Next
Common workflows

Create your first customer

From key to customer + contact + onboarding in one script.

|View as Markdown|Open in Claude|
Was this page helpful?
Next

Onboarding phases

Built with

This guide pushes a complete customer record through the API in a single Node script. You end with a customer, a primary contact, and an active onboarding — visible in https://my.pivotal.app/customers/<slug> when the script finishes.

Prerequisites

  • A live API key (pivotal_…) from Admin > API Keys.
  • Node 20+ or Bun. The script uses native fetch.
  • A scratch slug — pick something that won’t collide with a real customer in your workspace. We’ll use aurora-demo.

Export the key once:

$export PIVOTAL_API_KEY=pivotal_…

The script

create-customer.ts
1const API = "https://my.pivotal.app/api/v1";
2const KEY = process.env.PIVOTAL_API_KEY!;
3if (!KEY) throw new Error("PIVOTAL_API_KEY is required");
4
5const h = {
6 Authorization: `Bearer ${KEY}`,
7 "Content-Type": "application/json",
8};
9
10async function call<T>(path: string, init?: RequestInit): Promise<T> {
11 const res = await fetch(`${API}${path}`, { ...init, headers: { ...h, ...(init?.headers ?? {}) } });
12 const body = await res.json();
13 if (!res.ok) {
14 console.error("API error:", body);
15 throw new Error(`${res.status} ${body?.error?.code ?? "unknown"}`);
16 }
17 return body as T;
18}
19
20// 1. Create the customer
21const customer = await call<{ id: string; display_id: number; slug: string }>(
22 "/customers",
23 {
24 method: "POST",
25 headers: { "Idempotency-Key": "first-customer-aurora-demo" },
26 body: JSON.stringify({
27 name: "Aurora Outfitters",
28 slug: "aurora-demo",
29 domain: "aurora-demo.com",
30 status: "onboarding",
31 }),
32 },
33);
34console.log(`Customer: ${customer.display_id} (${customer.slug})`);
35
36// 2. Attach a primary contact
37const contact = await call<{ id: string; display_id: number; email: string }>(
38 `/customers/${customer.display_id}/contacts`,
39 {
40 method: "POST",
41 body: JSON.stringify({
42 name: "Mira Chen",
43 email: "mira@aurora-demo.com",
44 title: "Head of Ops",
45 is_primary: true,
46 labels: ["billing", "technical"],
47 }),
48 },
49);
50console.log(`Contact: ${contact.display_id} (${contact.email})`);
51
52// 3. Open an onboarding
53const onboarding = await call<{ id: string; display_id: number; phase: string; state: string }>(
54 "/onboardings",
55 {
56 method: "POST",
57 headers: { "Idempotency-Key": "first-customer-aurora-demo-onb" },
58 body: JSON.stringify({
59 customer_id: customer.display_id.toString(),
60 phase: "before_getting_started",
61 state: "active",
62 target_launch_date: "2026-07-15T00:00:00Z",
63 }),
64 },
65);
66console.log(`Onboarding: ${onboarding.display_id} (${onboarding.phase})`);
67
68console.log(`\nOpen https://my.pivotal.app/customers/${customer.display_id}-${customer.slug}`);

Run it:

$bun create-customer.ts
$# or: ts-node create-customer.ts

What happened

Three rows landed:

  1. A customer in your workspace with display_id assigned by Pivotal — the small integer that will appear in the URL.
  2. A contact under that customer. Because is_primary: true, the customer detail page surfaces her at the top of the contacts card.
  3. An onboarding at the before_getting_started phase with a launch date 6+ weeks out.

The customer’s activity feed now has three rows, each labelled API: <key-name> as the actor. The same events flow into the per-workspace audit log at Admin > Logs.

Trying it again safely

Re-run the script. Because we sent Idempotency-Key on both creates, the second pass returns the cached responses instead of creating duplicates. Nice property to lean on inside webhook handlers and cron jobs.

Drop the Idempotency-Key headers and re-run — you’ll get 409 slug_taken on the customer create. That’s the expected behavior. Pick a fresh slug or send a PATCH instead.

Cleaning up

The aurora-demo customer is soft-deletable:

$curl -X DELETE "https://my.pivotal.app/api/v1/customers/${DISPLAY_ID}" \
> -H "Authorization: Bearer $PIVOTAL_API_KEY"

The associated contact and onboarding survive the delete but disappear from list views (the parent customer is gone from list views too). Reach out if you need a hard delete.

Next

  • Set up two-way sync with your CRM in Integration sync.
  • Drive an onboarding through its phases in Onboarding phases.
  • Build the same flow in test mode — see Test mode.