> ## Documentation Index
> Fetch the complete documentation index at: https://getprescience.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Run the core Partner API flow in test mode.

This walkthrough takes a test key from first ping to an enrolled employer: **ping → create group → upload census → quote → enroll → read the account**. Every request is copy-pasteable, and every response shown is what the API returns for this census.

<Note>
  You need a test API key (`psk_test_...`). Prescience support issues keys; there is no self-serve key creation yet. Email [partners@getprescience.com](mailto:partners@getprescience.com) if you don't have one.
</Note>

<Steps>
  <Step title="Check your key">
    Export your key and hit `/ping`. It confirms the key is valid and tells you which mode you're in.

    <CodeGroup>
      ```bash curl theme={null}
      export PRESCIENCE_API_KEY="psk_test_..."

      curl https://www.getprescience.com/api/partner/v1/ping \
        -H "Authorization: Bearer $PRESCIENCE_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      const BASE = "https://www.getprescience.com/api/partner/v1";
      const headers = {
        Authorization: `Bearer ${process.env.PRESCIENCE_API_KEY}`,
        "Content-Type": "application/json",
      };

      const ping = await fetch(`${BASE}/ping`, { headers }).then((r) => r.json());
      console.log(ping);
      ```

      ```python Python theme={null}
      import os
      import requests

      BASE = "https://www.getprescience.com/api/partner/v1"
      session = requests.Session()
      session.headers.update({
          "Authorization": f"Bearer {os.environ['PRESCIENCE_API_KEY']}",
          "Content-Type": "application/json",
      })

      print(session.get(f"{BASE}/ping").json())
      ```
    </CodeGroup>

    ```json Response theme={null}
    { "ok": true, "partner": { "id": "prt_7f3e9a2c54d1", "name": "Example HR" }, "mode": "test" }
    ```
  </Step>

  <Step title="Create a group">
    A group is one employer. `companyName` and `domain` are required; `currentCoverage.pepmCents` (what the employer pays per employee per month today) powers the savings comparison on the quote, so send it if you have it.

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://www.getprescience.com/api/partner/v1/groups \
        -H "Authorization: Bearer $PRESCIENCE_API_KEY" \
        -H "Content-Type: application/json" \
        -H "Idempotency-Key: create-acme-group-1" \
        -d '{
          "externalId": "co_4821",
          "companyName": "Acme Inc",
          "domain": "acme.com",
          "address": { "line1": "548 Market St", "city": "San Francisco", "state": "CA", "zip": "94104" },
          "contact": { "name": "Dana Park", "email": "dana@acme.com", "title": "Head of People" },
          "currentCoverage": {
            "planType": "fully_insured",
            "carrier": "Anthem",
            "pepmCents": 80000,
            "renewalDate": "2026-09-01"
          }
        }'
      ```

      ```typescript TypeScript theme={null}
      const group = await fetch(`${BASE}/groups`, {
        method: "POST",
        headers: { ...headers, "Idempotency-Key": "create-acme-group-1" },
        body: JSON.stringify({
          externalId: "co_4821",
          companyName: "Acme Inc",
          domain: "acme.com",
          address: { line1: "548 Market St", city: "San Francisco", state: "CA", zip: "94104" },
          contact: { name: "Dana Park", email: "dana@acme.com", title: "Head of People" },
          currentCoverage: {
            planType: "fully_insured",
            carrier: "Anthem",
            pepmCents: 80000,
            renewalDate: "2026-09-01",
          },
        }),
      }).then((r) => r.json());

      console.log(group.id); // grp_8c2f41d09a3e
      ```

      ```python Python theme={null}
      group = session.post(
          f"{BASE}/groups",
          headers={"Idempotency-Key": "create-acme-group-1"},
          json={
              "externalId": "co_4821",
              "companyName": "Acme Inc",
              "domain": "acme.com",
              "address": {"line1": "548 Market St", "city": "San Francisco", "state": "CA", "zip": "94104"},
              "contact": {"name": "Dana Park", "email": "dana@acme.com", "title": "Head of People"},
              "currentCoverage": {
                  "planType": "fully_insured",
                  "carrier": "Anthem",
                  "pepmCents": 80000,
                  "renewalDate": "2026-09-01",
              },
          },
      ).json()

      print(group["id"])  # grp_8c2f41d09a3e
      ```
    </CodeGroup>

    ```json Response (201) theme={null}
    {
      "id": "grp_8c2f41d09a3e",
      "mode": "test",
      "status": "created",
      "companyName": "Acme Inc",
      "domain": "acme.com",
      "externalId": "co_4821",
      "address": { "line1": "548 Market St", "city": "San Francisco", "state": "CA", "zip": "94104" },
      "contact": { "name": "Dana Park", "email": "dana@acme.com", "title": "Head of People" },
      "currentCoverage": { "planType": "fully_insured", "carrier": "Anthem", "pepmCents": 80000, "renewalDate": "2026-09-01" },
      "censusMemberCount": 0,
      "companyState": null,
      "createdAt": "2026-06-10T17:04:11Z",
      "updatedAt": "2026-06-10T17:04:11Z"
    }
    ```
  </Step>

  <Step title="Upload the census">
    `PUT /census` replaces the full census. To quote, each member only needs a `zip` and one of `dob` | `age`. You almost certainly have both already. Names and emails are required later, at enrollment.

    This is Acme's real census: 12 employees, 19 covered lives.

    <CodeGroup>
      ```bash curl theme={null}
      curl -X PUT https://www.getprescience.com/api/partner/v1/groups/grp_8c2f41d09a3e/census \
        -H "Authorization: Bearer $PRESCIENCE_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "members": [
            { "externalId": "emp_0001", "firstName": "Jordan", "lastName": "Reyes", "email": "jordan@acme.com", "dob": "1992-03-14", "zip": "94110", "employmentType": "full_time", "hireDate": "2024-03-01" },
            { "externalId": "emp_0002", "firstName": "Priya", "lastName": "Natarajan", "email": "priya@acme.com", "dob": "1997-06-21", "zip": "94110", "employmentType": "full_time", "hireDate": "2025-01-20" },
            { "externalId": "emp_0003", "firstName": "Sam", "lastName": "Whitfield", "email": "sam@acme.com", "dob": "1985-01-30", "zip": "94110", "employmentType": "full_time", "hireDate": "2022-08-15" },
            { "externalId": "emp_0004", "firstName": "Elena", "lastName": "Sokolov", "email": "elena@acme.com", "dob": "1998-11-08", "zip": "94110", "employmentType": "full_time", "hireDate": "2025-06-02" },
            { "externalId": "emp_0005", "firstName": "Marcus", "lastName": "Tran", "email": "marcus@acme.com", "dob": "1988-05-17", "zip": "94110", "employmentType": "full_time", "hireDate": "2023-02-27" },
            { "externalId": "emp_0006", "firstName": "Aiko", "lastName": "Tanaka", "email": "aiko@acme.com", "dob": "2001-02-09", "zip": "94110", "employmentType": "full_time", "hireDate": "2025-09-08" },
            { "externalId": "emp_0007", "firstName": "Dev", "lastName": "Sharma", "email": "dev@acme.com", "dob": "1995-04-26", "zip": "94110", "employmentType": "full_time", "hireDate": "2024-11-11" },
            { "externalId": "emp_0008", "firstName": "Grace", "lastName": "Okafor", "email": "grace@acme.com", "dob": "1990-07-03", "zip": "94110", "employmentType": "full_time", "hireDate": "2023-07-15",
              "dependents": [ { "relationship": "spouse", "dob": "1989-08-12", "name": "Femi Okafor" } ] },
            { "externalId": "emp_0009", "firstName": "Tom", "lastName": "Becker", "email": "tom@acme.com", "dob": "1974-02-27", "zip": "94110", "employmentType": "full_time", "hireDate": "2022-01-10",
              "dependents": [ { "relationship": "spouse", "dob": "1976-05-19", "name": "Anja Becker" } ] },
            { "externalId": "emp_0010", "firstName": "Lena", "lastName": "Fischer", "email": "lena@acme.com", "dob": "1993-01-15", "zip": "94110", "employmentType": "full_time", "hireDate": "2024-09-30",
              "dependents": [ { "relationship": "child", "dob": "2019-10-02", "name": "Mia Fischer" } ] },
            { "externalId": "emp_0011", "firstName": "Noah", "lastName": "Kim", "email": "noah@acme.com", "dob": "1982-06-30", "zip": "94110", "employmentType": "full_time", "hireDate": "2022-05-23",
              "dependents": [ { "relationship": "spouse", "dob": "1983-04-11", "name": "Sarah Kim" }, { "relationship": "child", "dob": "2015-09-21", "name": "Eli Kim" } ] },
            { "externalId": "emp_0012", "firstName": "Rachel", "lastName": "Adler", "email": "rachel@acme.com", "dob": "1987-03-08", "zip": "94110", "employmentType": "full_time", "hireDate": "2023-10-02",
              "dependents": [ { "relationship": "spouse", "dob": "1986-07-22", "name": "Ben Adler" }, { "relationship": "child", "dob": "2018-12-05", "name": "Maya Adler" } ] }
          ]
        }'
      ```

      ```typescript TypeScript theme={null}
      // Map straight from the employee records you already store.
      const members = [/* the same 12 members as the curl tab */];

      const census = await fetch(`${BASE}/groups/${group.id}/census`, {
        method: "PUT",
        headers,
        body: JSON.stringify({ members }),
      }).then((r) => r.json());

      console.log(census); // { received: 12, accepted: 12, ... }
      ```

      ```python Python theme={null}
      # Map straight from the employee records you already store.
      members = [...]  # the same 12 members as the curl tab

      census = session.put(f"{BASE}/groups/{group['id']}/census", json={"members": members}).json()
      print(census)  # { "received": 12, "accepted": 12, ... }
      ```
    </CodeGroup>

    ```json Response (200) theme={null}
    {
      "groupId": "grp_8c2f41d09a3e",
      "received": 12,
      "accepted": 12,
      "memberCount": 12,
      "errors": [],
      "warnings": []
    }
    ```

    Bad rows never block good ones: they come back in `errors` with the row index while everything else is stored. Details in the [census guide](/guides/census).
  </Step>

  <Step title="Create the quote">
    One POST, empty body, full quote in the same response. The plan year start defaults to the first of the month at least 60 days out, and the savings baseline comes from the `pepmCents` you set on the group.

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://www.getprescience.com/api/partner/v1/groups/grp_8c2f41d09a3e/quotes \
        -H "Authorization: Bearer $PRESCIENCE_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{}'
      ```

      ```typescript TypeScript theme={null}
      const quote = await fetch(`${BASE}/groups/${group.id}/quotes`, {
        method: "POST",
        headers,
        body: JSON.stringify({}),
      }).then((r) => r.json());

      console.log(quote.monthly.totalCents); // 660096 = $6,600.96/mo for 19 covered lives
      ```

      ```python Python theme={null}
      quote = session.post(f"{BASE}/groups/{group['id']}/quotes", json={}).json()
      print(quote["monthly"]["totalCents"])  # 660096 = $6,600.96/mo for 19 covered lives
      ```
    </CodeGroup>

    ```json Response (201) theme={null}
    {
      "id": "qt_5b9e2c7f10ad",
      "groupId": "grp_8c2f41d09a3e",
      "mode": "test",
      "status": "ready",
      "pricingBasis": "indicative",
      "plan": { "id": "diamond", "name": "Prescience Diamond" },
      "planYearStartDate": "2026-09-01",
      "expiresAt": "2026-07-10T17:22:05Z",
      "census": {
        "employees": 12,
        "coveredLives": 19,
        "tiers": { "employeeOnly": 7, "employeeSpouse": 2, "employeeChildren": 1, "family": 2 }
      },
      "monthly": {
        "totalCents": 660096,
        "pepmCents": 55008,
        "byTier": {
          "employeeOnly": { "count": 7, "avgCents": 34047 },
          "employeeSpouse": { "count": 2, "avgCents": 89166 },
          "employeeChildren": { "count": 1, "avgCents": 53987 },
          "family": { "count": 2, "avgCents": 94724 }
        }
      },
      "annual": { "totalCents": 7921152 },
      "employeeContribution": { "premiumCents": 0 },
      "comparison": {
        "priorPepmCents": 80000,
        "savingsMonthlyCents": 299904,
        "savingsAnnualCents": 3598848,
        "savingsPct": 31
      },
      "fundingBreakdown": {
        "expectedClaimsPct": 78,
        "stopLossPct": 12,
        "careNavigationPct": 10,
        "adminFeesPct": 0,
        "note": "Illustrative split of the premium-equivalent."
      },
      "assumptions": [
        "Rates are indicative pending underwriting confirmation.",
        "Census accepted as submitted; material changes re-rate."
      ],
      "pricing": { "source": "code_default" },
      "createdAt": "2026-06-10T17:22:05Z"
    }
    ```

    Read it like this: `monthly.totalCents` is the all-in monthly amount, `monthly.pepmCents` is the employee-normalized amount, and `comparison` is calculated from the prior-plan PEPM sent on the group. Field-by-field details are in the [quotes guide](/guides/quotes).

    <Warning>
      Quotes are **indicative pending underwriting confirmation**. They become binding only at enrollment plus underwriting sign-off. Say so wherever you display them.
    </Warning>
  </Step>

  <Step title="Enroll the employer">
    When the employer accepts the quote, create the enrollment. The signatory is the employer admin who made the selection; Prescience provisions them as the company's admin in the Prescience employer portal and runs onboarding there (the default `onboardingMode: "hosted"`; see the [enrollment guide](/guides/enrollment#onboarding-pathways)).

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://www.getprescience.com/api/partner/v1/groups/grp_8c2f41d09a3e/enrollments \
        -H "Authorization: Bearer $PRESCIENCE_API_KEY" \
        -H "Content-Type: application/json" \
        -H "Idempotency-Key: enroll-acme-1" \
        -d '{
          "quoteId": "qt_5b9e2c7f10ad",
          "signatory": { "name": "Dana Park", "email": "dana@acme.com", "title": "Head of People" }
        }'
      ```

      ```typescript TypeScript theme={null}
      const enrollment = await fetch(`${BASE}/groups/${group.id}/enrollments`, {
        method: "POST",
        headers: { ...headers, "Idempotency-Key": "enroll-acme-1" },
        body: JSON.stringify({
          quoteId: quote.id,
          signatory: { name: "Dana Park", email: "dana@acme.com", title: "Head of People" },
        }),
      }).then((r) => r.json());

      console.log(enrollment.status); // "onboarding"
      ```

      ```python Python theme={null}
      enrollment = session.post(
          f"{BASE}/groups/{group['id']}/enrollments",
          headers={"Idempotency-Key": "enroll-acme-1"},
          json={
              "quoteId": quote["id"],
              "signatory": {"name": "Dana Park", "email": "dana@acme.com", "title": "Head of People"},
          },
      ).json()
      print(enrollment["status"])  # "onboarding"
      ```
    </CodeGroup>

    ```json Response (201) theme={null}
    {
      "id": "enr_3f7a92c81b40",
      "groupId": "grp_8c2f41d09a3e",
      "mode": "test",
      "status": "onboarding",
      "quoteId": "qt_5b9e2c7f10ad",
      "startDate": "2026-09-01",
      "companyDomain": "acme.com",
      "companyState": "sandbox",
      "onboardingMode": "hosted",
      "employerPortal": {
        "provisioned": true,
        "signinUrl": "https://www.getprescience.com/signin",
        "inviteSuppressed": true
      },
      "onboarding": { "percentComplete": 0, "blockingRemaining": 0 },
      "nextSteps": [
        "Prescience onboarding call scheduled with the employer",
        "Employer completes KYB + plan setup in the Prescience portal",
        "Prescience activates the group (state → prod)"
      ],
      "createdAt": "2026-06-12T09:14:32Z"
    }
    ```

    From here, Prescience runs onboarding, KYB, banking, and plan setup with the employer directly in the Prescience employer portal, then activation (`sandbox` → `prod`). No code on your side; poll `GET /enrollments/{enrollmentId}` or listen for webhooks. `employerPortal` confirms the signatory's portal account: this is a test-mode key, so `inviteSuppressed` is `true`. Test mode never sends outbound email; in live mode the signatory receives a set-password invite. The sandbox company is fully functional but flagged `test: true` and never activated; no money moves.
  </Step>

  <Step title="Read the account">
    Once enrolled, `GET /account` returns funding, premium-equivalent totals, spend by category, and savings, all aggregate and de-identified.

    <CodeGroup>
      ```bash curl theme={null}
      curl https://www.getprescience.com/api/partner/v1/groups/grp_8c2f41d09a3e/account \
        -H "Authorization: Bearer $PRESCIENCE_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      const account = await fetch(`${BASE}/groups/${group.id}/account`, { headers })
        .then((r) => r.json());
      console.log(account.company.state); // "sandbox" now, "prod" after activation
      ```

      ```python Python theme={null}
      account = session.get(f"{BASE}/groups/{group['id']}/account").json()
      print(account["company"]["state"])  # "sandbox" now, "prod" after activation
      ```
    </CodeGroup>

    The [benefits tab guide](/guides/benefits-tab) maps every field to a UI element, with the full in-force payload for this group.
  </Step>
</Steps>

## Where to next

<CardGroup cols={2}>
  <Card title="Read plan metadata" icon="store" href="/guides/marketplace-listing">
    Static plan fields plus group-specific quote pricing.
  </Card>

  <Card title="Set up webhooks" icon="webhook" href="/guides/webhooks">
    Signed events for enrollment, activation, and finalized quotes.
  </Card>

  <Card title="Sync your census" icon="refresh-cw" href="/guides/census-sync">
    New hires, terminations, and COBRA: one call each.
  </Card>

  <Card title="Go live" icon="shield-check" href="/resources/compliance">
    BAA, data handling, and what changes between test and live.
  </Card>
</CardGroup>
