---
name: unified-quickstart
description: >-
  Add Unified.to integrations to an app: activate integrations in the Sandbox,
  embed the Authorization component, store connection IDs, make the first
  Unified API call with an official SDK, and (optionally) subscribe to
  webhooks. Use when a developer asks to "add Unified.to", "add integrations
  with HubSpot / Salesforce / Greenhouse / BambooHR / QuickBooks / …", or to
  connect their product to their customers' SaaS apps through one API.
license: MIT
metadata:
  category: quickstart
  api_base: https://api.unified.to
  docs: https://docs.unified.to/quick-start
  docs_index: https://docs.unified.to/llms.txt
  mcp: https://docs.unified.to/mcp/core
---

# Add Unified.to integrations to an app

Unified.to puts one real-time REST API and one data model in front of hundreds
of SaaS integrations across CRM, ATS, HRIS, accounting, commerce, messaging,
ticketing, storage, calendar, and more. Your app authorizes an end-user's
account once, gets a **connection ID**, and calls
`https://api.unified.to/{category}/{connection_id}/{object}`. The same code
works for every integration in a category.

This skill takes an app from nothing to a working, verified integration in the
**Sandbox** (synthetic data). It summarizes the official quick start at
https://docs.unified.to/quick-start. When anything here is unclear or looks out
of date, the docs are the source of truth: search them (see Step 0) instead of
guessing.

## Before you start: agree on a plan

Inspect the project first (language, framework, where server code lives, how
env vars and the database are handled). Then show the developer a short
checklist and **wait for approval** before editing code:

1. **Use case & categories:** what the app needs (e.g. "read CRM contacts",
   "create ATS candidates"). Map it to a category and object (`crm/contact`,
   `ats/candidate`, `hris/employee`, `accounting/invoice`, …).
2. **Integrations:** which vendors to offer first (e.g. HubSpot, Salesforce).
3. **Region:** where the Unified.to workspace lives. US is the default (`https://api.unified.to`),
   EU is `https://api-eu.unified.to` and AU is `https://api-au.unified.to`.
4. **Where connection IDs are stored:** usually a table or column keyed by the
   app's user or account ID.
5. **Files you will add or change:** env config, SDK client, auth UI, the
   callback route, the first API call, and optionally a webhook route.

If the project already talks to vendor APIs directly (e.g. its own HubSpot
client), say so and propose which calls move to Unified.to. Don't delete
existing integrations without approval.

## Step 0: Tools

**Unified.to Core MCP server (recommended).** If tools such as
`search_docs`, `list_unified_integrations`, `list_unified_connections`, and
`list_unified_apicalls` are available, use them throughout. They manage the
developer's workspace and answer questions from the docs. If they are not
available, suggest the developer adds the server (https://docs.unified.to/mcp/core):

- US: `https://mcp-api.unified.to/mcp`, EU: `https://mcp-api-eu.unified.to/mcp`
- Auth: header `Authorization: bearer <workspace API key>`. Prefer the header
  and an environment variable over the `?token=` URL form.

The workspace API key gives full access to the Unified.to account. Never
write it into committed config files.

**Docs for agents.** Index: https://docs.unified.to/llms.txt. Every docs page
has a Markdown version (append `.md`), and the full docs are available as a single
file at https://docs.unified.to/llms-full.txt. The OpenAPI spec is at
https://api.unified.to/openapi.json, with one file per category at
`https://api.unified.to/docs/openapi-{category}.json`.

**Category skills.** After this quick start, load the matching skill for
object shapes and category-specific patterns (list below under "Next").

## Step 1: Workspace credentials (the developer does this)

An agent cannot create the account. Ask the developer to:

1. Sign up or log in at https://app.unified.to/login.
2. Open **Settings → API Keys** and copy the **Workspace ID**, **API Key**, and
   **workspace secret** (used to verify signatures).

Add them to the project's env config (`.env`, secrets manager, etc.) and make
sure `.env` is git-ignored:

```bash
UNIFIED_WORKSPACE_ID=...
UNIFIED_API_KEY=...          # server-side only, never sent to the browser
UNIFIED_WORKSPACE_SECRET=... # server-side only
UNIFIED_ENV=Sandbox          # switch to Production at launch
```

## Step 2: Activate integrations in the Sandbox

The Sandbox (also called "Synthetic Data") returns mock data and accepts any mock
credentials, so no vendor developer accounts are needed yet
(https://docs.unified.to/concepts/sandbox).

Ask the developer to open **Integrations** in app.unified.to, pick **Sandbox**
from the environment dropdown (top right), search for each integration, and
click **Activate**. Credentials can be left empty.

Verify with `list_unified_integrations` (Core MCP) or
`GET https://api.unified.to/unified/integration/workspace/{workspace_id}?env=Sandbox&active=true`
that they are active. Integrations are tied to the environment
they were activated in.

## Step 3: Install the SDK

Prefer an official SDK over raw HTTP (https://docs.unified.to/reference/sdks):

| Language   | Package                                                      |
| ---------- | ------------------------------------------------------------ |
| TypeScript | `npm install @unified-api/typescript-sdk`                    |
| Python     | https://github.com/unified-to/unified-python-sdk             |
| PHP        | https://github.com/unified-to/unified-php-sdk                |
| Java       | `to.unified:unified-java-sdk` (Maven Central)                |
| Go         | https://github.com/unified-to/unified-go-sdk                 |
| C#         | `UnifiedTo` (NuGet)                                          |
| Ruby       | https://github.com/unified-to/unified-ruby-sdk               |

Other languages can generate a client from https://api.unified.to/openapi.json.
SDKs use the language's naming convention (e.g. camelCase in TypeScript), while
the docs use snake_case.

Create one server-side client:

```typescript
import { UnifiedTo } from '@unified-api/typescript-sdk';

export const unified = new UnifiedTo({
    security: { jwt: process.env.UNIFIED_API_KEY! },
    // EU/AU workspaces: serverURL: 'https://api-eu.unified.to' or 'https://api-au.unified.to'
});
```

## Step 4: Let end-users authorize their accounts

The end-user picks an integration, authorizes it, and is redirected back to
the app with a new connection ID
(https://docs.unified.to/concepts/embedded-components). Choose one:

**A. Embedded Authorization component (fastest).** Plain JS:

```html
<script src="https://api.unified.to/docs/unified.js?wid=YOUR_WORKSPACE_ID&did=unified_widget&env=Sandbox"></script>
<div id="unified_widget"></div>
```

Framework packages are also available: `@unified-api/react-directory`,
`@unified-api/vuejs-directory`, `@unified-api/angular-directory`, and
`@unified-api/svelte-directory`. The developer can set options (scopes,
redirects) and copy code at https://app.unified.to/embed.

**B. Authorization URL (full control, e.g. your own integration picker).**

```
https://api.unified.to/unified/integration/auth/{workspace_id}/{integration_type}
  ?redirect=1
  &env=Sandbox
  &success_redirect=https://yourapp.com/integrations/callback
  &failure_redirect=https://yourapp.com/integrations/callback
  &scopes=crm_contact_read,crm_company_read
  &state=<opaque value tied to the signed-in user>
  &external_xref=<your user or account ID>
```

**Always request only the scopes you need.** If you leave scopes out, every
possible scope is requested (https://docs.unified.to/concepts/scopes).

**Callback route (server-side).** On success, Unified.to appends `id` (the
connection ID), `state`, `nonce`, `type`, and `sig`. On failure it appends
`error` to `failure_redirect`. Verify the signature, confirm `state` belongs to
the signed-in user, and then store the connection ID against that user
(https://docs.unified.to/guides/how_to_associate_a_connection_id_with_your_end_user):

```typescript
import { createHmac, timingSafeEqual } from 'crypto';

// sig = HmacSha256(workspace.secret).update(connection.id).update(nonce).update(state).digest('hex')
function isValidAuthCallback(q: { id: string; nonce: string; state?: string; sig: string }) {
    const expected = createHmac('sha256', process.env.UNIFIED_WORKSPACE_SECRET!)
        .update(q.id)
        .update(q.nonce)
        .update(q.state ?? '')
        .digest('hex');
    return expected.length === q.sig.length && timingSafeEqual(Buffer.from(expected), Buffer.from(q.sig));
}
```

Store the connection ID in the database. Don't keep it only in
`localStorage`. Connections can also be looked up later by user with
`GET /unified/connection?external_xref={user_id}`.

## Step 5: Make the first API call

Every data call is `{category}/{connection_id}/{object}` with
`Authorization: Bearer $UNIFIED_API_KEY` (https://docs.unified.to/reference/rest):

```typescript
const { crmContacts } = await unified.crm.listCrmContacts({
    connectionId,
    limit: 20,
});
```

```bash
curl "https://api.unified.to/crm/$CONNECTION_ID/contact?limit=20" \
  -H "Authorization: Bearer $UNIFIED_API_KEY"
```

Apply these rules from the start:

- **Start from a list endpoint.** IDs can't be guessed.
- **Pagination:** use `limit` (max 100) and `offset`. A page shorter than `limit`
  is the last page. Use `updated_gte` for incremental reads, and `sort`/`order`
  where needed (https://docs.unified.to/reference/pagination).
- **Field selection:** `fields=` trims the response. Vendor-specific data is available
  through `fields=raw` (https://docs.unified.to/reference/fields).
- **Capability varies by integration:** a `501` means the vendor doesn't support
  the operation. Check the integration's **Feature Support** tab.
- **Errors:** `401` means the connection needs re-authorizing, so prompt the user. `403` means
  a scope or permission is missing. `429` means back off and retry
  (https://docs.unified.to/reference/rate_limits). Don't blindly retry a `POST`
  that timed out; list first to check whether the record was created.

## Step 6: Verify end to end

Don't report success until all of these pass:

1. The app builds, and its tests and lint pass.
2. Run the app, open the auth UI, pick a Sandbox integration, and enter any mock
   credentials. The callback stores a connection ID.
3. `list_unified_connections` (Core MCP) or `GET /unified/connection` shows the
   new connection.
4. The first API call returns synthetic records in the app.
5. If anything fails, read `list_unified_apicalls` / `list_unified_issues` (Core
   MCP) or the API call logs at app.unified.to before changing code.

## Step 7 (optional): Keep data in sync with webhooks

To get updates pushed instead of polling, register a webhook
(https://docs.unified.to/guides/how_to_create_and_configure_webhooks). Use
`create_unified_webhook` (Core MCP) or:

```
POST https://api.unified.to/unified/webhook?include_all=true
{
  "hook_url": "https://yourapp.com/webhooks/unified",
  "connection_id": "<connection ID>",
  "object_type": "crm_contact",
  "event": "updated"
}
```

`include_all=true` backfills historical data. `updated` events also fire for new
records. The handler must follow these rules:

- **Verify `sig256`.** It is `base64(HMAC-SHA256(workspace secret, JSON.stringify(data) + nonce))`.
  Keep the `data` array's key order exactly as received and compare timing-safely
  (https://docs.unified.to/reference/webhooks).
- **Branch on `type`:** `INITIAL-PARTIAL`, `INITIAL-COMPLETE`, `VIRTUAL`, `NATIVE`.
- **Upsert by `id`.** Duplicates and out-of-order deliveries happen, so handlers
  must be idempotent.
- **Return `200` quickly** and do heavy work asynchronously.
- Use `external_xref` from the payload to find the owning user.

## Critical rules

- The API key and workspace secret stay **server-side**, in env vars or a secrets
  manager. Never put them in client bundles, committed files, or logs.
- Build and test in the **Sandbox**. Always pass `env` explicitly to the Core
  API and the auth URL.
- Unified.to is **real-time and pass-through**: it does not cache the end-user's
  data. If the app needs fast queries, sync into the app's own database (webhooks + upsert).
- Request the **minimum scopes**.
- Verify **every signature** (auth callback `sig`, webhook `sig256`).
- Associate every connection with the app's user (`state` and/or
  `external_xref`) at creation time.
- When unsure about an endpoint, field, or behavior, **search the docs**
  (`search_docs` or https://docs.unified.to/llms.txt). Don't invent parameters.

## Going to production (the developer does this)

From https://docs.unified.to/quick-start#go-to-production:

1. Register a developer app with each vendor to get OAuth or API-key credentials.
   The docs have step-by-step guides for each vendor.
2. Activate the integration in the **Production** environment with those
   credentials.
3. Enable matching scopes in the vendor's developer app
   (https://docs.unified.to/concepts/scopes).
4. Change `env=Sandbox` to `env=Production` in the auth component or URL.

Adding more integrations later needs no code changes. Activate them, and they
appear in the Authorization component automatically.

## Next: category skills

Load the skill that matches the use case:

- CRM: https://docs.unified.to/skills/unified-crm/SKILL.md
- ATS / job boards: https://docs.unified.to/skills/unified-ats-jobboard/SKILL.md
- Assessments: https://docs.unified.to/skills/unified-assessment/SKILL.md
- HRIS / payroll: https://docs.unified.to/skills/unified-hris/SKILL.md
- Accounting / invoicing: https://docs.unified.to/skills/unified-accounting-invoicing/SKILL.md
- Payments: https://docs.unified.to/skills/unified-payments/SKILL.md
- E-commerce: https://docs.unified.to/skills/unified-ecommerce/SKILL.md
- Messaging: https://docs.unified.to/skills/unified-messaging/SKILL.md
- Calendar: https://docs.unified.to/skills/unified-calendar/SKILL.md
- Ticketing: https://docs.unified.to/skills/unified-ticketing/SKILL.md
- Enterprise search / RAG: https://docs.unified.to/skills/unified-enterprise-search-rag/SKILL.md
- "Sign in with …": https://docs.unified.to/skills/unified-auth-signin/SKILL.md
- REST fundamentals: https://docs.unified.to/skills/unified-api/SKILL.md
- SDK details: https://docs.unified.to/skills/unified-sdks/SKILL.md
- Debugging connections and webhooks: https://docs.unified.to/skills/unified-debug-webhooks-connections/SKILL.md

Need help? Email hello@unified.to or see https://unified.to/support.
