# Unified.to – Complete Documentation Unified.to provides a single API to integrate multiple B2B SaaS platforms. This file contains the full documentation for the Unified.to API. Source: https://docs.unified.to OpenAPI spec: https://api.unified.to/swagger.json --- # Getting Started ## Unified.to Documentation URL: https://docs.unified.to/intro # Unified.to Documentation The [Unified.to API](/concepts/what-is-a-unified-api) allows you to add multiple integrations to your application by using a single (i.e. unified) API. Focus on your core product instead of developing and maintaining different integrations. ## Access your customer's data however you want :::grid :::gridhightlight [Unified REST API](/reference/rest) ::: :::gridhightlight [Unified MCP Server](/mcp) ::: :::gridhightlight [Unified Webhooks](/reference/webhooks) ::: :::gridhightlight [Unified SDKs](/reference/sdks) ::: :::gridhightlight [Unified CLI](https://github.com/unified-to/unified-cli) ::: ::: ## How to use these docs Our documentation is organized as follows: - **How-to guides**: These guides provide the answers to “How do I...?” types of questions. Accomplish common task flows like getting OAuth 2 credentials or testing connections. - **API Basics**: Get to know the fundamentals for interacting with our API, such as pagination, webhooks, and rate limits. - **Concepts**: Gain a deeper understanding of topics that are core to the Unified.to platform, such as virtual webhooks, scopes, and unified APIs. - **Reference**: Look up API specs for each of our unified integration categories as well as examples of API requests. If you're new, we recommend starting with our tutorial: [Build a simple Javascript app that calls the Unified.to API](/tutorials/build-a-simple-javascript-app). ## Accessing documentation programmatically The developer documentation at this website is published for human developers. If you need to access the documentation programmatically, via HTTP clients, curl, LLM client, or any non-browser tooling during development, use the following options instead: - Markdown exports: All pages can be accessed with a .md suffix which would provide a markdown version. (for example, this Introduction page is at https://docs.unified.to/intro.md). A GET request for these URLs returns Markdown suitable for version control, search, and offline use. - Documentation index: Use the https://docs.unified.to/llms.txt which lists curated links to Markdown versions of guides and reference pages, including webhooks and API definitions. Use it as a stable entry point when you need to discover or bulk-fetch machine-friendly documentation. - OpenAPI definitions: Endpoint and webhook reference specifications can be found at https://docs.unified.to/reference/sdks.md. ## Additional resources Curious about Unified.to's security posture? Learn more at [Security](https://unified.to/security){:target="\_blank"}. Have a question or just want to chat? Join our [Discord](https://discord.gg/85z7HF7JbD){:target="\_blank"} server. Watch guided video walkthroughs on our [YouTube](https://www.youtube.com/@unified_api){:target="\_blank"} channel, such as [How to get started with Unified.to](https://youtu.be/arTwN_OT8Do) or [How to use Webhooks in Unified.to](https://youtu.be/_zbUplmJd84). ## Get started with Unified.to URL: https://docs.unified.to/quick-start # Get started with Unified.to This quick start guides you through connecting your application to third-party platforms using Unified.to. You'll see how to: - Set up your Unified.to account and credentials - Activate integrations in the sandbox environment - Add the Authorization component to your app - Make your first API call ::callout Want to play around with the Unified API right away? Download [Postman](https://www.postman.com/downloads/) and import our [Postman collection](/reference/sdks) to start making API calls. You'll need your workspace ID and API key first. :: ## Set up your workspace 1. Create your Unified.to account - Sign up at [app.unified.to/login](https://app.unified.to/login) - Log in to your dashboard 2. Get your API credentials - Navigate to **Settings > API Keys** - Copy your **Workspace ID** and **API Key** - Store these securely where you can reference them in your app, such as in an `.env` file - you'll need them for API calls 3. Activate an integration in the sandbox environment - Go to **Integrations** - Select **Sandbox** from the environment dropdown in the top-right corner of the screen - Search for your desired integration - Click the integration card and select **Activate** (don't worry about filling in credentials - they're all mocked in the sandbox environment) ![Search for integrations in the sandbox environment](/images/screenshots/quick-start-integrations.png) ## Add authorization to your app Your users need to authorize your application to access their third-party accounts (like Lever, HubSpot, etc.). Unified.to provides an embedded Authorization component that handles this process securely. When users click on an integration in this component, they'll be redirected to an authorization page, then returned to your app with a unique connection ID that you'll use for making API calls. ![Quick start auth component](/images/screenshots/quick-start-auth-component.png) Add this to your app: ```html
``` Replace `YOUR_WORKSPACE_ID` with the Workspace ID you copied earlier. (Want to use a Javascript framework or make the API call from your server? Discover more options at [app.unified.to/embed](https://app.unified.to/embed?tab=Authorization)) ## Make your first API call After successful authorization, you'll receive a connection ID in the callback URL e.g. `localhost:3000?id={connection_id}`. Save this in your database and use it to make API calls. Our Unified endpoints generally follow the pattern of `{API_CATEGORY}/{CONNECTION_ID}/{DATA_OBJECT}`. For example: ### CRM (HubSpot, Salesforce, etc.) ```javascript // Fetch contacts const response = await fetch(`https://api.unified.to/crm/${connectionId}/contact`, { headers: { Authorization: `Bearer ${API_KEY}`, }, }); ``` ### ATS (Lever, Greenhouse, etc.) ```javascript // Fetch candidates const response = await fetch(`https://api.unified.to/ats/${connectionId}/candidate`, { headers: { Authorization: `Bearer ${API_KEY}`, }, }); ``` ### HRIS (BambooHR, Workday, etc.) ```javascript // Fetch employees const response = await fetch(`https://api.unified.to/hris/${connectionId}/employee`, { headers: { Authorization: `Bearer ${API_KEY}`, }, }); ``` Refer to our **API Reference** for the category and data object you are interested in. ## Next steps When you're ready to move on to production environments, review these guides to set yourself up for success: - [Learn how to work with webhooks](https://docs.unified.to/concepts/webhooks) - [Configure scopes for your authorization flow](https://docs.unified.to/concepts/scopes) - [Associate your end-users with connections](https://docs.unified.to/guides/how_to_associate_a_connection_id_with_your_end_user) - [SDKs for popular languages](https://docs.unified.to/reference/sdks) Need help? - Join our [Discord](https://discord.gg/2nsAPmbx) - Email [hello@unified.to](mailto:hello@unified.to) --- # Concepts ## Integration Authorization URL: https://docs.unified.to/concepts/embedded-components # Authorization Authorization is the process of asking customers to authorize access to their third-party accounts. Unified provides several options to accomplish this, from easy 1-line embedded components to a full-featured API, and even CNAMEd domains. ## Embedded Authorization Component Unified.to provides a suite of pre-built front-end authorization components that support React, Angular, Vue, and Svelte (and plain JavaScript) for you to use in your app. These authorization components display a list of your activated integrations, returned directly from our API. Once the end-user clicks on an integration, they will be redirected to the authorization page for that integration, regardless if it is OAuth2, API Token and any other type of authorization. in their File Storage connection. ### Instructions To use any one of these components in your app: - Make sure you've activated some integrations first. You can do so at [app.unified.to/integrations](https://app.unified.to/integrations). - On app.unified.to, click on Embedded components and select which of the widgets you want to use from the tab. - Configure how you want the component to behave under the Configuration tab. - View and copy the code to add to your app under the Embedded Code tab. - Important: Under the Options tab, be sure to select the correct permissions scopes that the integrations you activated are expecting to receive. By leaving these options blank, Unified.to will request access to all possible scopes. To learn more about scopes, see: [Understanding scopes](/concepts/scopes). ### Front-end Frameworks | | | | ----------- | ------------------------------------------------------------------------------------------------------------------------------- | | **React** | [npmjs.com/package/@unified-api/react-directory](https://www.npmjs.com/package/@unified-api/react-directory){:target="\_blank"} | | **Angular** | [npmjs.com/package/@unified-api/angular-directory](https://npmjs.com/package/@unified-api/angular-directory){:target="\_blank"} | | **VueJS** | [npmjs.com/package/@unified-api/vuejs-directory](https://npmjs.com/package/@unified-api/vuejs-directory){:target="\_blank"} | | **Svelte** | [npmjs.com/package/@unified-api/svelte-directory](https://www.npmjs.com/package/@unified-api/svelte-directory) | ## Manually Authorizing Connections If you do not want to use our pre-built authorization components, nor want to copy our component source code and make changes, then you can use our [API](https://docs.unified.to/unified/integration/Authorize_new_connection) This is the flow to follow in your application with your end-user: 1. You redirect your user to our authorization URL. Make sure to include a `success_redirect`, `failure_redirect`, and `permissions`. Also include either `state` or `external_xref`. 2. Your end-user will authorize the connection 3. If they cancel, then we return them to the `failure_redirect` 4. If they authorize the connection, we then test their API credentials to see if it has the valid permissions 5. If that fails, then we redirect them back to the `failure_redirect` and also include an `error` message with a string denoting the error 6. If it succeeds, then we redirect them to the `success_redirect` and add additional parameters to the URL ## Successful authorization If the authorization is successful (either via our authorization components or via the API), we redirect the user to the specified `success_redirect` URL and add the following URL parameters: - `id`: the newly created connection ID - `state`: the state value you provided - `nonce`: a random nonce value - `type`: the integration type of connection - `sig`: a security signature to ensure that this information has not been tampered with; ### Signature calculation The signature is calculated as follows: ```javascript sig = HmacSha256(workspace.secret).update(connection.id).update(nonce).update(state).digest('hex'); ``` ## Other Embedded Components Unified also has non-authorization components that you can embed into your app. These components are: - **Sign-in**: Authenticate users so they can sign into your app; [info](https://app.unified.to/embed?tab=Sign-in){:target="\_blank"} - **Public Directory**: Display a list of your integration so your users can learn about your third-party API offerings; [info](https://app.unified.to/embed?tab=Directory){:target="\_blank"} - **FileManager**: Display a list of files in a directory so your users can pick a file; [info](https://app.unified.to/embed?tab=FileManager){:target="\_blank"} - **CalendarScheduler**: Display a calendar so your users can schedule events in their Calendar connection; [info](https://app.unified.to/embed?tab=CalendarScheduler){:target="\_blank"} - **MessagingManager**: Display a list of channels/folders and messages; [info](https://app.unified.to/embed?tab=Messaging){:target="\_blank"} ## Sandbox / Synthetic Data environment URL: https://docs.unified.to/concepts/sandbox # Sandbox / Synthetic Data environment The synthetic data (ie. sandbox) environment is a controlled, isolated testing space provided by Unified.to. It allows developers to activate integrations, create connections, and make API calls without interacting with real third-party services or affecting production data. Key points to understand about the synthetic data environment: - It is specific to Unified.to and separate from any sandbox accounts you might have with third-party service providers. - All API calls made in the sandbox environment return synthetic, mock data. - At no point do you communicate with actual third-party providers when using the synthetic data environment. ## Using the synthetic data environment There are two ways to use the synthetic data environment: 1. **From the web app:** - On [app.unified.to](http://app.unified.to/), in the top right corner of the page, click on the **Env** dropdown. - Select **Synthetic Data** from the options. 2. **When calling the Core API:** - When calling the Unified Core API e.g. to query integrations or generate an authorization URL, include `env=Sandbox` in the query parameters. For example: ``` https://api.unified.to/unified/integration/auth/{workspaceId}/{integration}?env=Sandbox ``` **API reference:** [Core API overview](https://docs.unified.to/unified/overview) **Note**: If you use the Core API and do not specify `env`, we will default to using the environment that is currently active on app.unified.to. As a best practice, be explicit about the environment you want to use when working with this API. ## Sandbox vs Production environments | | **Synthetic Data / Sandbox** | **Production** | | ----------------- | ------------------------------------------------ | --------------------------------------------------- | | **Purpose** | For testing and development | For live, real-world usage | | **Data** | Synthetic, mock data generated by Unified.to | Real data from integrated services | | **Integrations** | Can be activated with mock credentials | Requires real credentials from third-party services | | **API Calls** | Return synthetic data | Interact with actual third-party services | | **Risk** | No risk of affecting real data or services | Can affect real data and services | | **Authorization** | Can use any mock credentials and bypasses scopes | Requires valid credentials and correct scopes | **Note**: Production environment refers to any non-sandbox environment. ## Key considerations about the sandbox environment ### Synthetic data generation - Mock data is generated when you activate an integration in the Sandbox environment. - If you deactivate and reactivate an integration, it will be populated with new synthetic data. ### Isolated testing environment - Each activated integration and connection in the Sandbox has its own isolated set of synthetic data. - This isolation allows you to test various scenarios without affecting real data or services. ### Mock credentials - In the sandbox environment, you can use any mock values for OAuth client IDs, secrets, API tokens, and other credentials. - When creating a connection for an integration in the sandbox environment, you can also use any mock values when asked for them. ### Integration-specific environments vs. [Unified.to](http://unified.to/) Synthetic Data / Sandbox An important distinction to be aware of: - Some integrations allow you to choose an "API region / environment" when activating them. This refers to that specific API provider's environment, not Unified.to's environment. - Choosing "Sandbox" when activating an integration this way is **not** the same as using the Unified.to sandbox environment. ### Environment-specific integrations and connections - Activated integrations are tied to the active environment at the time of their activation. - If you activate an integration (e.g., HubSpot) in the sandbox environment, it won't be available in the production environment, and vice versa. - Similarly, a connection created for an integration in the sandbox environment will only return synthetic mock data and won't be accessible in the production environment. ## When to use the sandbox environment The Synthetic Data / Sandbox environment is ideal for: 1. Initial integration testing and exploration 2. Developing and debugging your application without affecting real data 3. Running automated tests that don't require real-world data 4. Demonstrating functionality without needing access to actual third-party services ## Best practices 1. Clearly distinguish between your sandbox and production environments in your code and workflows. 2. Use the sandbox environment for initial testing before moving to production. 3. When seeking support or reporting issues, specify which environment you're working in. ## Additional information - [Tutorial: Build a simple Javascript app that calls the Unified API](https://docs.unified.to/tutorials/build-a-simple-javascript-app) - See how to build an app entirely in the synthetic data / sandbox environment ## sandboxes URL: https://docs.unified.to/concepts/sandboxes # Sandbox Accounts This directory provides guidance for obtaining sandbox accounts for integrations supported by Unified.to. These accounts can be used for testing your app with live data. :::callout Note: These sandbox accounts are different from Unified.to's [synthetic data sandbox environment](/concepts/sandbox). While our sandbox environment generates synthetic data for trying out he Unified API, these sandbox accounts are for testing with real integration endpoints in production. ::: :::sandboxes ::: For integrations requiring partner program enrollment or special access, we recommend: 1. Starting the application process early in your development cycle 2. Having a clear description of your integration use case ready ## Understanding scopes URL: https://docs.unified.to/concepts/scopes # Understanding scopes In the world of API integrations and OAuth, scopes play a crucial role in managing access to resources. This guide explains the concept of scopes, their importance in OAuth, and how they are handled specifically at Unified.to. ## What are scopes? Scopes are a way to limit an application's access to a user's account. Instead of granting complete access to an account, scopes allow for fine-grained permission control. Think of scopes as permission slips. When you use an app that integrates with another service (like signing in with Google), scopes define exactly what that app is allowed to do with your account. It's like telling the bouncer at a club, "This person can enter the main area, but not the VIP lounge." ## Why are scopes important? 1. **Security**: Scopes ensure that applications only have access to the specific data and actions they need, reducing the risk of unauthorized access. 2. **User Control**: Users can make informed decisions about what access they're granting to applications. 3. **Compliance**: Scopes help applications adhere to data protection regulations by implementing the principle of least privilege. ## How scopes work in OAuth In the OAuth flow: 1. An application requests one or more scopes. 2. The user is presented with these scope requests during the authorization process. 3. If the user approves, an access token is issued that's limited to the approved scopes. For example, an application might request the scope `https://www.googleapis.com/auth/drive.readonly` to read files from Google Drive, but not edit or delete them. ## Scopes at Unified.to Unified.to acts as a conduit between your application and various API providers. We use a unified set of scopes that map to provider-specific scopes. This abstraction simplifies the integration process across multiple providers. ### Unified scopes Unified.to uses its own set of scopes, which are then mapped to provider-specific scopes. For example: - `storage_file_read` maps to `https://www.googleapis.com/auth/drive.readonly` for Google Drive - `crm_company_read` maps to `oauth, crm.objects.owners.read, crm.objects.companies.read` for HubSpot You may notice that some Unified scopes map to multiple provider-specific scopes. This is done to ensure that all the necessary permissions are requested for the integration to function correctly. The mappings for all scopes are found under their respective Integration page at app.unified.to under OAuth 2 Credentials e.g. [here](https://app.unified.to/integrations/googledrive?tab=oauth2) are the mappings for Google Drive. ### Platform scopes When setting up your developer account and/or developer app, you will need to define your scopes with the API providers themselves - Unified.to is unable to do this for you. How this is done varies on a case-by-case basis, but it is usually configured while generating your OAuth credentials. We have several how-to guides for generating credentials and setting scopes for our most popular integrations. If you think any are missing, please let us know. ## Requesting scopes When using Unified.to, you can request scopes in one of two ways, depending on how you intend to authorize your users: 1. **(Recommended) Using the Authorization component**: On app.unified.to, you can select the scopes you need under Embedded components > Permission scopes. Screenshot

Example of the scope interface on app.unified.to

2. **Using the Authorization URL**: When generating an auth URL, you can include the scopes as a query parameter. For example: ``` https://api.unified.to/unified/integration/auth/{WORKSPACE_ID}/{INTEGRATION}?scopes=webhook,crm_deal_read,crm_event_read ``` ### Requesting custom or platform-specific scopes In addition to passing Unified scopes in the query parameter, you can also specify scopes that are specific to the integration only. Behind the scenes, Unified scopes are "unfurled" to map to the integration's scopes, and anything else that is passed in will be used as-is. For example: ``` https://api.unified.to/unified/integration/auth/{WORKSPACE_ID}/{INTEGRATION}?scopes=webhook,crm_deal_read,crm_event_read,hubspot_scope_1,hubspot_scope2s ``` :::callout **Note**: If you do not specify any scopes at all, then Unified.to will attempt to request _all_ the scopes available for the integration. We strongly advise you to consider which scopes you actually need and then intentionally specify them using one of the above two methods. The next section will go over best practices when requesting scopes. ::: ### Overriding scopes per integration Each Unified permission (like `ats_job_read`) maps to a default set of the integration's raw scopes. You can replace that default on a per-integration basis using the integration's scope override setting. If you want to use our API to change these scopes, update the WorkspaceIntegration object by adding the value in a map keyed by the Unified permission, where each value is a comma-separated list of the integration's raw scopes: ```json { "overriden_scopes": { "ats_job": "jobs_r" } } ``` In this example, Unified requests the provider's `jobs_r` scope for the `ats_job` permission instead of its default. At authorization time, the override replaces the default scopes for that permission in the OAuth authorize URL. To revert, change the value back to the default scope or remove the override entirely. You can also change the default scopes for the activation workspace integration in app.unified.to inside the integration's page. You can also change the default scopes for the activation workspace integration in app.unified.to inside the integration's page. :::callout **Note**: Scope overrides apply to the OAuth authorization flow. For API-key based integrations, scopes are determined by the key itself, so the override will not change runtime behavior. ::: ### Best practices for scope usage 1. **Request Only What You Need**: Always request the minimum set of scopes required for your application to function. 2. **Check Provider Requirements**: Some providers may require certain scopes to always be enabled. Check the Unified.to documentation for provider-specific requirements. 3. **Include the Webhook Scope**: If you're setting up webhooks, always include the `webhook` scope in your requests. 4. **Verify Scope Mappings**: You can view the mapping of Unified.to scopes to provider-specific scopes in the Unified.to dashboard for each integration. ## Conclusion Understanding and correctly implementing scopes is crucial for secure and efficient API integrations. By leveraging Unified.to's scope system, you can simplify the process of working with multiple providers while maintaining granular control over access permissions. ## Virtual Webhooks URL: https://docs.unified.to/concepts/virtual_webhooks # Understanding virtual webhooks This guide explains what virtual webhooks are, how they differ from native webhooks, and when you would want to use a virtual webhook. ## What are virtual webhooks? Virtual webhooks are [Unified.to](http://unified.to/)'s solution for providing webhook functionality even when an integration doesn't natively support it. They simulate real-time updates by periodically checking for changes in your connected SaaS applications' data e.g. new records, update records, or deleted records. In other words, they use scheduled polling. While native webhooks are great when they're available, many software vendors either don't support webhooks or have limitations with their webhook implementations. Virtual webhooks bridge this gap by providing a consistent webhook experience across all supported integrations. ## How virtual webhooks work Virtual webhooks work by: 1. Monitoring your connections for updates to specific objects (e.g., CRM deals, ATS jobs, etc) at regular intervals (polling) 2. Detecting when data has changed or new data has come in since the last check 3. Sending updates to your webhook URL only when changes are found 4. Managing rate limits automatically The key difference from native webhooks is that virtual webhooks use polling behind the scenes, but this complexity is managed by us so that you, the developer, don’t have to worry about it. You interact with virtual webhooks as you would with native webhooks. ## Virtual vs native webhooks comparison | | **Virtual Webhooks** | **Native Webhooks** | | --------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | **Implementation** | Scheduled polling-based virtualization | Immediate push notifications | | **Setup** | Configured entirely through Unified.to | May require additional configuration on other platforms | | **Cost Structure** | One API call per interval when changes found - the interval can be set by you | One API call per event whenever they occur | | **Event Types** | Mainly supports created and updated events | Supports created, updated, and deleted events if the integration supports them | | **Retry Logic** | Built-in for both reading and dispatching | Handled on a per-integration basis | | **Health Monitoring** | Built-in connection health tracking with retry mechanism | Subject to your server’s availability and the integration’s retry mechanism | ## When to use virtual webhooks Virtual webhooks are ideal for: - Tasks where 1+ minute delays are acceptable and you don’t need the data immediately - When you want greater control over costs by setting the frequency the integration is polled - Integrations that don't offer native webhook support When an integration offers both virtual and native webhooks, consider native webhooks when: - You need immediate updates as soon as they arrive ## How to identify webhook support You can check what type of webhook support an integration offers: 1. Navigate to the integration's page in the [Unified.to](http://unified.to/) dashboard 2. Click on **Feature Support** 3. Look under the **Webhooks** section: - Virtual webhooks are labeled with "virtual" - Native webhooks are labeled with "native" ![How to identify virtual webhooks](/images/screenshots/virtual-webhooks.png) _In this example, the integration supports virtual updated and virtual created events._ ## Creating and configuring virtual webhooks You create and configure virtual webhooks the same as you would with any other webhook, with the exception of also being able to set an interval for the polling. To see how to do that, refer to: [How to create and configure webhooks](https://docs.unified.to/guides/how_to_create_and_configure_webhooks) ## Additional resources - [How to troubleshoot unhealthy webhooks](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_webhooks) - [Introduction to webhooks](https://docs.unified.to/reference/webhooks) ## What is a Unified API? URL: https://docs.unified.to/concepts/what-is-a-unified-api # What is a Unified API? A unified API is a single API interface that aggregates multiple third-party APIs in the same software category — CRM, accounting, HRIS, ATS, payroll, and others — behind one standardized endpoint. With a unified API, applications authenticate once, integrate once, and receive normalized data from any supported integration in that category. Unified APIs vary in their underlying architecture. Real-time pass-through unified APIs route every request live to the source system without storing customer data. Sync-based unified APIs cache data from source systems on a schedule and serve reads from that cache. The architectural choice has implications for data freshness, compliance scope, latency, and AI agent compatibility — covered in detail below. ## Key takeaways - A unified API gives applications one endpoint, one auth flow, and one data model across many vendors in the same category — instead of building per-vendor integrations. - Real-time pass-through unified APIs route requests live to source systems with no customer data stored at rest. Sync-based unified APIs cache data on a schedule and serve reads from that cache. - The architectural choice affects data freshness, compliance scope, latency profile, and AI agent compatibility. These trade-offs are concrete, not stylistic. - Most unified APIs handle the lowest-common-denominator critique through a "raw" passthrough mechanism that preserves vendor-specific custom fields and objects outside the normalized schema. - Pricing models vary substantially: per linked account, per active consumer, per API call, and consumption-based. Each scales differently with customer count and integration breadth. ## What problem does a unified API solve? Different APIs represent the same concepts in different ways, even within a single category. One CRM API refers to an end-user as a "User"; another calls them a "Contact"; a third uses "Lead." Each representation has different properties, field names, authentication schemes, pagination patterns, rate limits, and error formats. For an application that needs to integrate with several vendors in the same category, this fragmentation translates directly into engineering cost. Every integration requires reading new documentation, mapping a new data schema, handling new authentication flows, and building error-handling for new edge cases. Maintenance compounds: each vendor's API evolves independently, and breaking changes must be tracked and adapted to per integration. A unified API addresses this by providing an abstraction layer between an application and the underlying APIs in a category. The application interacts with a single, consistent interface; the unified API translates each request into the appropriate vendor-specific call and normalizes the response into a common schema. ## How does a unified API speed up development? A unified API reduces the number of integrations and the work associated with them. Instead of writing integration code per vendor, applications write integration code once against the unified data model. New vendors in the same category become available without additional integration work — the unified API vendor handles the mapping. For an application supporting a handful of integrations, the time savings are modest. For an application supporting dozens, savings are substantial — typically reducing months of engineering work per integration to days or hours. ## What complexity does a unified API actually remove? A unified API removes per-vendor variance in four specific areas: - **Authentication and authorization** — different OAuth2 flows, API key schemes, scope models, and token refresh behaviours collapse into a single authentication interface. - **Data representation** — different schemas, field names, and data types collapse into a normalized data model per category. - **Endpoint structure** — different URL patterns, HTTP methods, and request formats collapse into consistent endpoints across the category. - **Error handling** — different error codes, response formats, and rate-limit behaviours collapse into consistent error semantics. What a unified API does not remove: the underlying differences in vendor capability. If one vendor in a category supports a feature that others do not, the unified API still has to make a choice about how to surface that feature — typically through a "raw" passthrough mechanism (covered below). ## How does a unified API enhance functionality beyond raw aggregation? Some vendor APIs lack features that simplify integration — webhooks for change notifications, consistent pagination, predictable rate limits, or filtering. A well-designed unified API can add these capabilities as a layer above the underlying APIs: - Polling-based event delivery for vendors without native webhooks ("virtual webhooks"). - Consistent pagination patterns across all integrations in a category. - Centralized rate-limit management that handles per-vendor quotas transparently. - Standardized filtering and field selection. This is the difference between a unified API and a request proxy: a unified API can provide features the underlying APIs do not natively support. ## Real-time pass-through vs. sync-based architectures Unified APIs fall into two architectural categories. The choice has concrete implications for application design. | Property | Real-time pass-through | Sync-and-cache | |---|---|---| | Data freshness | Live; reflects source-system state at request time | Bounded by sync interval; can be hours stale | | Customer data storage | None; only operational metadata stored | Customer records cached for serving reads | | Compliance scope | Not a sub-processor for payload data | Full sub-processor for cached records | | Latency profile | Full source-system round-trip per request | Local cache reads (fast); source delays on writes | | Write-back behaviour | Synchronous; success/failure known immediately | Instant write, but reads may lag sync interval | | AI agent compatibility | Agents reason on current source-system state | Risk of agent acting on stale cached data | | Typical pricing model | Per API call or per request | Per linked account or per consumer | Note on operational metadata: real-time pass-through unified APIs typically store connection identifiers, encrypted OAuth credentials, and API call logs — but not customer payload data (records like contacts, employees, or invoices). This distinction is what determines compliance scope. ### When real-time pass-through is the right choice Real-time pass-through is appropriate when: - The application requires current data on every read (financial reconciliation, AI agent integrations, live dashboards). - The application's compliance posture benefits from minimizing stored customer data. - Write-back operations need immediate confirmation of success or failure at the source. - The application's latency budget can accommodate a full source-API round-trip per request (typically 800ms–1.5s). ### When sync-and-cache is the right choice Sync-and-cache is appropriate when: - The application's read patterns tolerate data being minutes or hours stale. - The application requires sub-100ms read latency that local cache reads provide. - The application performs high-volume historical analytics that would be expensive to execute against live source APIs. - The application's compliance framework already accommodates a unified API vendor as a data sub-processor. ## What should you look for in a unified API? ### How are unified API endpoints structured? A unified API needs unified endpoints for each integration category. An API call to retrieve CRM contacts should be identical regardless of which underlying CRM is providing the data. The endpoint structure should be predictable, RESTful, and consistent across all integrations in the category. ### How does data normalization work across vendors? Data models must be unified, not just endpoints. A CRM contact returned from one vendor should have the same field structure as a CRM contact returned from another. The normalized schema needs sufficient depth to support real use cases — names and emails are not enough for most applications. Two depth dimensions to evaluate: - **Field coverage**: how many fields per object are normalized into the unified schema? - **Object coverage**: how many distinct object types per category are supported (e.g., for CRM: contacts, companies, deals, pipelines, activities, notes)? ### How is authentication handled? The unified API should provide a single authentication flow across all integrations in a category. Common authentication metadata (vendor name, account name, scopes granted) should be available programmatically so the application can display connection state natively. A pre-built authorization UI component is often provided to handle the OAuth2 handshake with end users, reducing the application's auth implementation burden to a single integration. ### How are permission scopes managed? Along with unified authentication, a unified API should provide abstracted permission scopes so the application does not need to research per-vendor OAuth2 scopes for each use case. Scope abstraction maps the application's intent (e.g., "read contacts") to the appropriate per-vendor scope automatically. ### How do unified APIs handle vendors that don't support webhooks? Most SaaS APIs do not support native webhooks. A unified API should provide a consistent event-delivery model regardless of whether the underlying vendor supports webhooks natively. The common implementation pattern is "virtual webhooks": the unified API polls source APIs at configurable intervals, detects changes via timestamps or diffs, and delivers events to the application's endpoint only when changes are found. From the application's perspective, native and virtual webhooks use the same subscription model, the same event format, and the same retry semantics. This differs from sync-based notification systems, where data is first cached on a schedule and a notification fires after sync completion — requiring the application to fetch the changed data in a separate request. With virtual webhooks, change detection and event delivery are unified: detect changes at the source, deliver events directly. Polling intervals should be configurable. Default intervals vary substantially across unified API vendors (some default to 24 hours; others support per-minute polling). ### How are custom fields and custom objects handled? The "lowest common denominator" critique of unified APIs is that normalization across many vendors loses vendor-specific features, custom fields, and custom objects. Most modern unified APIs address this through three mechanisms: - **Custom field support** in the normalized schema, allowing per-customer field definitions. - **Metadata APIs** that surface vendor-specific schema information programmatically. - **Raw passthrough** that allows direct calls to vendor-specific endpoints while still using the unified API's authentication and connection management. Evaluating these mechanisms requires testing against specific use cases. The ability to handle Salesforce custom objects, HubSpot custom properties, or industry-specific HRIS fields varies meaningfully across unified API vendors. ### How does pricing scale with usage? Unified API pricing models vary substantially. The four common patterns: - **Per linked account**: priced by the number of customer-vendor connections (one customer using three integrations = three linked accounts). - **Per active consumer**: priced by the number of active end-customers using any integration. - **Per API call**: priced by request volume, with unlimited connections. - **Consumption-based**: priced by data volume transferred (typically GB). The right pricing model depends on the application's own pricing structure and customer-integration ratio. Per-linked-account pricing scales with customer × integration count, which can be predictable for low-integration applications and prohibitive for multi-integration ones. Per-API-call pricing scales with actual data activity, which favours applications with many connections but low per-connection volume. ## When is a unified API the wrong choice? A unified API is not the right architectural choice in every case. Three common scenarios where alternatives are better: ### Single-vendor or two-vendor integrations If an application only integrates with one or two vendors and has no near-term plans to add more, building per-vendor integrations directly against the source API is often faster and cheaper than adopting a unified API. The amortized engineering savings of a unified API only materialize at three or more integrations in a category. ### Workflow automation across internal applications If the integration use case is internal workflow automation (e.g., "when a Salesforce deal closes, create a Jira ticket and update an internal database"), an iPaaS platform like Workato, Zapier, or MuleSoft is typically a better fit than a unified API. iPaaS platforms are built for workflow orchestration; unified APIs are built for customer-facing data access. ### Deep per-customer customization If each customer requires unique integration logic — different field mappings, different sync schedules, different transformation rules — an embedded iPaaS platform (Paragon, Workato Embedded, Tray Embedded) is often a better fit. Embedded iPaaS gives end-users a configuration interface for their own integrations; unified APIs give developers a programmatic interface for standardized integrations. ### Use cases requiring full vendor-specific feature depth If the application needs deep access to vendor-specific features that are not represented in the unified schema (e.g., Salesforce Apex triggers, HubSpot workflow automation, Workday's full reporting framework), the unified API's normalized layer may not provide sufficient depth. The raw passthrough mechanism partially addresses this, but at the cost of losing the unified abstraction for those operations. ## Security considerations A unified API sits in the data path between an application and its customers' third-party data. Security evaluation should cover: - **Customer data storage**: does the unified API store customer payload data at rest, or pass requests through statelessly? This determines compliance scope and audit surface. - **Credential storage**: where are OAuth2 client credentials and customer access tokens stored? External secret management (e.g., AWS Secrets Manager) is preferable to in-platform storage for high-security applications. - **Encryption**: at-rest encryption for stored credentials and operational metadata; in-transit encryption (TLS 1.2+) for all API traffic. - **Compliance certifications**: SOC 2 Type II, GDPR, HIPAA, PIPEDA, ISO 27001 as applicable to the application's regulated data categories. - **Data residency**: regional infrastructure for applications subject to data residency requirements (US, EU, AU, etc.). ## Reliability considerations The unified API becomes a critical dependency for the application. Evaluation should cover: - **SLA and uptime history**: published uptime targets and historical performance. - **Connection health monitoring**: does the unified API detect broken third-party vendor connections proactively, before the application's customers do? - **Error reporting and observability**: API call logs, error categorization, retry visibility. - **Hosting and scaling**: where the unified API is hosted and how it scales under load. ## Use case compatibility: breadth, depth, and clustering Three dimensions to evaluate against the application's use case: - **Breadth**: number of supported vendors in any one category. Higher breadth supports more customer-vendor combinations. - **Depth**: number of fields and object types in the normalized data model. Higher depth supports more sophisticated use cases (e.g., full HRIS payroll vs. just employee directory). - **Clustering**: support for multiple categories with linked data models. Applications that span CRM + Support + Marketing benefit from clustering; single-category applications do not. ## Unified.to Unified.to provides a real-time, stateless pass-through unified API across 440+ integrations and 27 categories — including CRM, accounting, HRIS, ATS, payroll, ticketing, e-commerce, messaging, and others. Every request is routed live to the source system. No customer payload data is stored at rest. Unified.to additionally provides a hosted MCP (Model Context Protocol) server with 20,000+ callable MCP tools, allowing AI agents to read and write across customer SaaS integrations through a single connection-scoped interface. For pricing details, see [unified.to/pricing](https://unified.to/pricing). For full API documentation, see [docs.unified.to](https://docs.unified.to). --- # Guides ## Advertising Report Metrics by Integration URL: https://docs.unified.to/guides/advertising_report_metrics_by_integration # Advertising Report Metrics by Integration ------ _June 12, 2026_ **Endpoint:** `GET /ads/{connection_id}/report` — [List all reports](https://docs.unified.to/ads/report/List_all_reports) This is a reference for the metrics returned by the Advertising **Report** endpoint: which metrics each supported integration populates, and how each platform's native field maps onto Unified.to's single normalized reporting model. ## How reports return metrics A call to `GET /ads/{connection_id}/report` returns an array of [Report objects](https://docs.unified.to/ads/report/model). Each report describes the performance of an advertising entity over a window, and carries its metrics in a single normalized array: | Field | Type | Description | | --------------------------- | ------ | ----------------------------------------------------------------- | | `id` | string | Report identifier | | `created_at` / `updated_at` | date | ISO-8601 timestamps | | `start_at` / `end_at` | date | The reporting window the metrics cover | | `currency` | string | Currency for all monetary metrics in the report | | `organization_id` | string | The advertising organization the report belongs to | | `metrics` | array | The normalized performance metrics (the subject of this document) | | `raw` | any | The untransformed payload from the underlying platform | The value of the `metrics` array is that it is **the same shape regardless of which platform** **`connection_id`** **points to**. Whether the connection is Meta Ads or LinkedIn, a `CLICKS` metric means the same thing and is read the same way. Behind the scenes, Unified.to handles the platform-specific renaming, the nested-field extraction, and the unit conversions — and where a platform doesn't return a value directly, it computes it. If you ever need the original platform field, it remains available under `raw`. ## The unified metric model Each ad platform reports the same fundamental ideas — a click, an impression, a dollar spent — but names and shapes them differently. A click is `clicks` in Meta, `METRIC_CLICKS` in Google DV360, and `Clicks` in Microsoft Ads. Cost arrives as raw currency in some platforms and as micro-units (millionths of a currency unit) in Google Ads, which must be divided by 1,000,000 before it means anything. Unified.to collapses all of that into one vocabulary of **92 normalized metrics** (written in `UPPER_SNAKE_CASE`). The tables below show, for each normalized metric, the native field it is sourced from on each of the five integrations the Report endpoint supports: **Meta Ads, Google Ads, Google DV360, LinkedIn, and Microsoft Ads**. A `—` means that integration does not expose the metric, so it will not appear in the `metrics` array for that connection. ## Coverage at a glance The breadth of the `metrics` array depends entirely on which integration the connection is for. Not all platforms expose data at the same depth: | Integration | Metrics returned | Character of the data | | ----------------- | ---------------- | -------------------------------------------------------------------------------------------------------- | | **LinkedIn** | 57 / 92 | Deepest coverage, driven by a large set of social, lead-gen, and "viral" (organic amplification) metrics | | **Google DV360** | 45 / 92 | Strong on viewability, video engagement, and cost/financial breakdowns | | **Google Ads** | 30 / 92 | Broad core coverage plus search-specific and viewability metrics | | **Meta Ads** | 19 / 92 | Focused on core performance and detailed video-watch metrics | | **Microsoft Ads** | 14 / 92 | Core performance metrics only | Coverage is highly uneven across the metric set. Of the 92 normalized metrics, **60 are returned by only a single integration**, while just **8 are returned by all five**. The long tail is dominated by LinkedIn's social and viral metrics and DV360's financial and viewability metrics, which have no equivalent elsewhere. Write your code defensively: check for a metric's presence rather than assuming every report contains it. ## The universal core These eight metrics are the common denominator — every supported integration returns them, making them the safe foundation for any cross-connection report or comparison: `CLICKS` · `IMPRESSIONS` · `CONVERSIONS` · `COST` · `CTR` · `CPC` · `CONVERSION_VALUE` · `CPA` Even within this universal set, the source data differs. `COST` is `spend` in Meta but arrives in micro-units in Google Ads and as `costInLocalCurrency` in LinkedIn (all normalized to the report's `currency`). `CONVERSIONS` is a clean field in most platforms but is pulled from Meta's nested `actions[purchase]`. And `CTR`, `CPC`, and `CPA` are all **derived** for LinkedIn — a reminder that "returned" sometimes means "computed for you," not "delivered by the platform." Two near-universal ratios fall just short of full coverage: `ROAS` is derived everywhere it exists but is absent from DV360, and `CPM` is missing only from Google Ads. ## Core performance metrics The headline numbers most reports are built on — volume, cost, and the efficiency ratios derived from them. | Unified Metric | Meta Ads | Google Ads | Google DV360 | LinkedIn | Microsoft Ads | | ------------------ | ------------------------------ | -------------------------- | -------------------------------------------------- | ------------------------------ | ----------------- | | `CLICKS` | clicks | clicks | METRIC_CLICKS | clicks | Clicks | | `IMPRESSIONS` | impressions | impressions | METRIC_IMPRESSIONS | impressions | Impressions | | `CONVERSIONS` | actions[purchase] | conversions | METRIC_TOTAL_CONVERSIONS | externalWebsiteConversions | Conversions | | `COST` | spend | cost_micros (/1e6) | METRIC_REVENUE_ADVERTISER | costInLocalCurrency | Spend | | `CTR` | ctr | ctr | METRIC_CTR | derived | Ctr | | `CPC` | cpc | average_cpc (/1e6) | METRIC_REVENUE_ECPC_ADVERTISER | derived | AverageCpc | | `CONVERSION_VALUE` | action_values[purchase] | conversions_value | METRIC_TRUEVIEW_TOTAL_CONVERSION_VALUES_ADVERTISER | conversionValueInLocalCurrency | Revenue | | `CPA` | cost_per_action_type[purchase] | cost_per_conversion (/1e6) | METRIC_REVENUE_ECPA_ADVERTISER | derived | CostPerConversion | | `ROAS` | derived | derived | — | derived | derived | | `CPM` | cpm | — | METRIC_REVENUE_ECPM_ADVERTISER | derived | AverageCpm | | `ECPM` | — | — | METRIC_REVENUE_ECPM_ADVERTISER | — | — | ## Conversion detail Beyond the headline conversion count, platforms differ on how they attribute and segment conversions. Google Ads and Microsoft Ads add "all conversions" totals and their revenue counterparts; DV360 and LinkedIn distinguish post-click from view-through (post-view) conversions; and Google Ads alone exposes cross-device conversions and raw `interactions`. | Unified Metric | Meta Ads | Google Ads | Google DV360 | LinkedIn | Microsoft Ads | | -------------------------- | -------- | ------------------------ | ----------------------------- | ----------------------------------- | ---------------------- | | `POST_CLICK_CONVERSIONS` | — | — | METRIC_POST_CLICK_CONVERSIONS | externalWebsitePostClickConversions | — | | `VIEW_THROUGH_CONVERSIONS` | — | view_through_conversions | METRIC_POST_VIEW_CONVERSIONS | externalWebsitePostViewConversions | ViewThroughConversions | | `ALL_CONVERSIONS` | — | all_conversions | — | — | AllConversions | | `ALL_CONVERSION_VALUE` | — | all_conversions_value | — | — | AllRevenue | | `CROSS_DEVICE_CONVERSIONS` | — | cross_device_conversions | — | — | — | | `INTERACTIONS` | — | interactions | — | — | — | ## Video engagement Video is the most fragmented category, because each platform instruments playback in its own way. Quartile completion (25/50/75/100%) is the one widely shared concept, supported across Meta, Google Ads, DV360, and LinkedIn — though Google Ads reports quartiles as _rates_ while the others report counts. From there the platforms diverge: Meta uniquely tracks `VIDEO_AVG_TIME_WATCHED` and `VIDEO_THRUPLAY`; DV360 captures rich-media interaction signals like pauses, mutes, skips, and companion-banner activity; and Google Ads surfaces search-driven views. | Unified Metric | Meta Ads | Google Ads | Google DV360 | LinkedIn | Microsoft Ads | | ------------------------- | ------------------------------ | -------------------------------------- | ------------------------------------------------ | ----------------------------- | ------------- | | `VIDEO_VIEWS` | — | video_views | METRIC_TRUEVIEW_VIEWS | videoViews | VideoViews | | `VIDEO_PLAYS` | video_play_actions | — | METRIC_RICH_MEDIA_VIDEO_PLAYS | videoStarts | — | | `VIDEO_COMPLETIONS` | — | — | METRIC_RICH_MEDIA_VIDEO_COMPLETIONS | videoCompletions | — | | `VIDEO_QUARTILE_25` | video_p25_watched_actions | video_quartile_p25_rate | METRIC_RICH_MEDIA_VIDEO_FIRST_QUARTILE_COMPLETES | videoFirstQuartileCompletions | — | | `VIDEO_QUARTILE_50` | video_p50_watched_actions | video_quartile_p50_rate | METRIC_RICH_MEDIA_VIDEO_MIDPOINTS | videoMidpointCompletions | — | | `VIDEO_QUARTILE_75` | video_p75_watched_actions | video_quartile_p75_rate | METRIC_RICH_MEDIA_VIDEO_THIRD_QUARTILE_COMPLETES | videoThirdQuartileCompletions | — | | `VIDEO_QUARTILE_100` | video_p100_watched_actions | video_quartile_p100_rate | METRIC_RICH_MEDIA_VIDEO_COMPLETIONS | — | — | | `VIDEO_AVG_TIME_WATCHED` | video_avg_time_watched_actions | — | — | — | — | | `VIDEO_THRUPLAY` | video_thruplay_watched_actions | — | — | — | — | | `AVERAGE_CPV` | — | average_cpv (/1e6) | — | — | — | | `EARNED_VIEWS` | — | — | METRIC_TRUEVIEW_EARNED_VIEWS | — | — | | `UNIQUE_VIEWERS` | — | — | METRIC_TRUEVIEW_UNIQUE_VIEWERS | — | — | | `VIDEO_VIEWS_FROM_SEARCH` | — | video_views_from_google_search_results | — | — | — | | `VIDEO_FULLSCREENS` | — | — | METRIC_RICH_MEDIA_VIDEO_FULL_SCREENS | fullScreenPlays | — | | `VIDEO_PAUSES` | — | — | METRIC_RICH_MEDIA_VIDEO_PAUSES | — | — | | `VIDEO_MUTES` | — | — | METRIC_RICH_MEDIA_VIDEO_MUTES | — | — | | `VIDEO_SKIPS` | — | — | METRIC_RICH_MEDIA_VIDEO_SKIPS | — | — | | `COMPANION_CLICKS` | — | — | METRIC_RICH_MEDIA_VIDEO_COMPANION_CLICKS | — | — | | `COMPANION_VIEWS` | — | — | METRIC_RICH_MEDIA_VIDEO_COMPANION_VIEWS | — | — | ## Social engagement Likes, shares, comments, and follows are native to the social-first platforms. LinkedIn reports them directly, and DV360 exposes the YouTube "earned" equivalents (earned likes, shares, and subscribers). Curiously, Google Ads maps `SHARES` and `SAVES` onto Gmail-specific forward and save actions — an artifact of how Gmail Ads engagement was instrumented. | Unified Metric | Meta Ads | Google Ads | Google DV360 | LinkedIn | Microsoft Ads | | ------------------- | -------- | -------------- | ----------------------------------------- | ---------------- | ------------- | | `ENGAGEMENT` | — | — | METRIC_ENGAGEMENT_RATE | derived (sum) | — | | `ENGAGEMENTS` | — | engagements | METRIC_ENGAGEMENTS | — | — | | `TOTAL_ENGAGEMENTS` | — | — | — | totalEngagements | — | | `OTHER_ENGAGEMENTS` | — | — | — | otherEngagements | — | | `LIKES` | — | — | METRIC_TRUEVIEW_EARNED_LIKES | likes | — | | `SHARES` | — | gmail_forwards | METRIC_TRUEVIEW_EARNED_SHARES | shares | — | | `COMMENTS` | — | — | — | comments | — | | `FOLLOWS` | — | — | METRIC_TRUEVIEW_EARNED_SUBSCRIBERS | follows | — | | `SAVES` | — | gmail_saves | METRIC_TRUEVIEW_EARNED_PLAYLIST_ADDITIONS | — | — | | `COMMENT_LIKES` | — | — | — | commentLikes | — | | `OPENS` | — | — | — | opens | — | ## Click and impression breakdowns Most of these granular click types are LinkedIn-specific, reflecting its sponsored-content and document-ad formats (card clicks, company-page clicks, text-URL clicks, and so on). Meta contributes `LANDING_PAGE_CLICKS` (its `inline_link_clicks`) and `UNIQUE_CLICKS`, while LinkedIn adds an approximate unique-impression count. | Unified Metric | Meta Ads | Google Ads | Google DV360 | LinkedIn | Microsoft Ads | | ------------------------ | ------------------ | ---------------------- | ------------ | ---------------------------- | ------------- | | `LANDING_PAGE_CLICKS` | inline_link_clicks | — | — | landingPageClicks | — | | `AD_UNIT_CLICKS` | — | — | — | adUnitClicks | — | | `CARD_CLICKS` | — | — | — | cardClicks | — | | `CARD_IMPRESSIONS` | — | — | — | cardImpressions | — | | `COMPANY_PAGE_CLICKS` | — | — | — | companyPageClicks | — | | `ACTION_CLICKS` | — | — | — | actionClicks | — | | `TEXT_URL_CLICKS` | — | — | — | textUrlClicks | — | | `GMAIL_SECONDARY_CLICKS` | — | gmail_secondary_clicks | — | — | — | | `UNIQUE_CLICKS` | unique_clicks | — | — | — | — | | `UNIQUE_IMPRESSIONS` | — | — | — | approximateUniqueImpressions | — | ## Lead generation Native lead-form metrics are exclusive to LinkedIn in this mapping, tied to its one-click Lead Gen Forms. | Unified Metric | Meta Ads | Google Ads | Google DV360 | LinkedIn | Microsoft Ads | | ----------------- | -------- | ---------- | ------------ | --------------------- | ------------- | | `LEADS` | — | — | — | oneClickLeads | — | | `LEAD_FORM_OPENS` | — | — | — | oneClickLeadFormOpens | — | ## Viewability and measurement Viewability — whether an impression was actually seen — is a Google ecosystem strength. Google Ads and DV360 share the Active View family (viewable, measurable, and percent-viewable impressions), DV360 adds billable and eligible impression counts plus average viewable time, and Google Ads contributes top-of-page impression share metrics from search. | Unified Metric | Meta Ads | Google Ads | Google DV360 | LinkedIn | Microsoft Ads | | ------------------------------- | -------- | ---------------------------------- | ------------------------------------------- | -------- | ------------- | | `VIEWABLE_IMPRESSIONS` | — | active_view_impressions | METRIC_ACTIVE_VIEW_VIEWABLE_IMPRESSIONS | — | — | | `MEASURABLE_IMPRESSIONS` | — | active_view_measurable_impressions | METRIC_ACTIVE_VIEW_MEASURABLE_IMPRESSIONS | — | — | | `VIEWABILITY_RATE` | — | active_view_viewability | METRIC_ACTIVE_VIEW_PCT_VIEWABLE_IMPRESSIONS | — | — | | `BILLABLE_IMPRESSIONS` | — | — | METRIC_BILLABLE_IMPRESSIONS | — | — | | `ELIGIBLE_IMPRESSIONS` | — | — | METRIC_ACTIVE_VIEW_ELIGIBLE_IMPRESSIONS | — | — | | `ACTIVE_VIEW_AVG_TIME` | — | — | METRIC_ACTIVE_VIEW_AVERAGE_VIEWABLE_TIME | — | — | | `ABSOLUTE_TOP_IMPRESSION_SHARE` | — | absolute_top_impression_percentage | — | — | — | | `TOP_IMPRESSION_SHARE` | — | top_impression_percentage | — | — | — | ## Cost and financial breakdowns DV360 is the only integration that decomposes cost into its component fees — media cost, data fees, platform fees — and reports advertiser-level revenue and profit. This reflects its role as a programmatic buying platform where margin transparency matters. | Unified Metric | Meta Ads | Google Ads | Google DV360 | LinkedIn | Microsoft Ads | | ------------------ | -------- | ---------- | ---------------------------------- | -------- | ------------- | | `REVENUE` | — | — | METRIC_REVENUE_ADVERTISER | — | — | | `MEDIA_COST` | — | — | METRIC_MEDIA_COST_ADVERTISER | — | — | | `TOTAL_MEDIA_COST` | — | — | METRIC_TOTAL_MEDIA_COST_ADVERTISER | — | — | | `DATA_FEES` | — | — | METRIC_DATA_COST_ADVERTISER | — | — | | `PLATFORM_FEES` | — | — | METRIC_PLATFORM_FEE_ADVERTISER | — | — | | `PROFIT` | — | — | METRIC_PROFIT_ADVERTISER | — | — | ## Viral / organic amplification (LinkedIn) LinkedIn's "viral" metrics measure the organic spread of sponsored content — engagement that happens when someone's network sees an ad because a connection interacted with it. This entire 19-metric family is exclusive to LinkedIn and mirrors its standard metrics (impressions, clicks, conversions, video quartiles, leads) but for organically-amplified reach. No other integration models this concept. | Unified Metric | Meta Ads | Google Ads | Google DV360 | LinkedIn | Microsoft Ads | | -------------------------------- | -------- | ---------- | ------------ | ---------------------------------------- | ------------- | | `VIRAL_IMPRESSIONS` | — | — | — | viralImpressions | — | | `VIRAL_CLICKS` | — | — | — | viralClicks | — | | `VIRAL_LIKES` | — | — | — | viralLikes | — | | `VIRAL_COMMENTS` | — | — | — | viralComments | — | | `VIRAL_SHARES` | — | — | — | viralShares | — | | `VIRAL_FOLLOWS` | — | — | — | viralFollows | — | | `VIRAL_VIDEO_PLAYS` | — | — | — | viralVideoStarts | — | | `VIRAL_VIDEO_VIEWS` | — | — | — | viralVideoViews | — | | `VIRAL_VIDEO_COMPLETIONS` | — | — | — | viralVideoCompletions | — | | `VIRAL_VIDEO_QUARTILE_25` | — | — | — | viralVideoFirstQuartileCompletions | — | | `VIRAL_VIDEO_QUARTILE_50` | — | — | — | viralVideoMidpointCompletions | — | | `VIRAL_VIDEO_QUARTILE_75` | — | — | — | viralVideoThirdQuartileCompletions | — | | `VIRAL_LEADS` | — | — | — | viralOneClickLeads | — | | `VIRAL_LEAD_FORM_OPENS` | — | — | — | viralOneClickLeadFormOpens | — | | `VIRAL_LANDING_PAGE_CLICKS` | — | — | — | viralLandingPageClicks | — | | `VIRAL_CONVERSIONS` | — | — | — | viralExternalWebsiteConversions | — | | `VIRAL_POST_CLICK_CONVERSIONS` | — | — | — | viralExternalWebsitePostClickConversions | — | | `VIRAL_VIEW_THROUGH_CONVERSIONS` | — | — | — | viralExternalWebsitePostViewConversions | — | | `VIRAL_ENGAGEMENTS` | — | — | — | viralTotalEngagements | — | ## Integration-by-integration takeaways **LinkedIn** is the deepest integration by a wide margin. Beyond solid core coverage, it owns two large metric families that exist nowhere else: native lead-form metrics and the complete "viral" organic-amplification set. If a report needs B2B lead data or organic-reach attribution, LinkedIn is the only source. **Google DV360** is the choice for media-buying transparency. It uniquely breaks cost into media, data, and platform fees and reports profit, and it offers the richest video-interaction signals (pauses, mutes, skips, companion banners) alongside full Active View viewability. **Google Ads** provides broad, dependable core coverage plus search-native metrics — impression share, cross-device conversions, and "all conversions" rollups. Watch for its two quirks: micro-unit cost fields that need dividing by a million, and quartile metrics expressed as rates rather than counts. **Meta Ads** concentrates on core performance and granular video-watch behavior. Its conversion and value figures come from nested action breakdowns rather than flat fields, so the `purchase` action type must be extracted explicitly. **Microsoft Ads** is the leanest integration, covering the universal core plus video views, view-through conversions, and "all conversions" totals — sufficient for standard performance reporting but without engagement, viewability, or social depth. ## Working with the response - **Build cross-connection reports on the universal eight.** `CLICKS`, `IMPRESSIONS`, `CONVERSIONS`, `COST`, `CTR`, `CPC`, `CONVERSION_VALUE`, and `CPA` are the only metrics guaranteed to appear in the `metrics` array for every supported integration. - **Check for presence, don't assume it.** With 60 of 92 metrics returned by only one integration, code that reads the `metrics` array should handle a metric being absent rather than expecting a fixed set. - **Read currency from the report, not the metric.** All monetary metrics are normalized into the report's `currency` field; Google Ads micro-units (`/1e6`) and per-platform conventions are already resolved for you. - **Some metrics are computed.** Ratios like `ROAS`, and several of LinkedIn's rates, are derived by Unified.to rather than supplied by the platform — they arrive in the `metrics` array all the same. - **Drop to** **`raw`** **when you need the original.** Anything platform-specific that the normalized model doesn't capture remains available on the report's `raw` field. ## ATS to Vector DB: How to Power Talent Intelligence with Real-Time Data URL: https://docs.unified.to/guides/ats_to_vector_db_how_to_power_talent_intelligence_with_real_time_data # ATS to Vector DB: How to Power Talent Intelligence with Real-Time Data ------ _October 14, 2025_ _Updated June 2026_ With Unified.to, you can build a talent intelligence application that works with your customers' preferred ATS — Lever, Greenhouse, and 80+ others — through one integration. With a single API, you fetch candidate records, normalize and embed resumes, and upsert those embeddings into a vector database like Pinecone for semantic search and recruiter-agent workflows. The result is a retrieval layer for talent intelligence, built on ATS data fetched live from the source. This guide shows you how to go from ATS to vector DB, step by step, using Unified.to, its GenAI API, and Pinecone. ## What this builds 1. Fetch normalized candidate data from an ATS. 2. Chunk and embed resume content. 3. Store embeddings in a vector database. 4. Retrieve the most relevant candidates at query time. 5. Use the retrieved context to power recruiter search, ranking, or AI agents. Unified.to handles ingestion and live updates across ATS providers; the embeddings and vector storage stay in your infrastructure. Because Unified.to is pass-through, no candidate data rests on its servers — the records flow to your retrieval layer and stop there. --- ## Prerequisites - Node.js (v18+) - A Unified.to account with an ATS integration enabled (e.g., Lever, Greenhouse) - A Unified.to API key - Your customer's ATS connection ID - A Unified.to GenAI connection ID (for embeddings) - A Pinecone API key and index --- ## Step 1: Set up your project ```bash mkdir ats-vector-demo cd ats-vector-demo npm init -y npm install @unified-api/typescript-sdk dotenv @pinecone-database/pinecone ``` Add your credentials to `.env`: ```plain text UNIFIED_API_KEY=your_unified_api_key CONNECTION_ATS=your_customer_ats_connection_id CONNECTION_GENAI=your_genai_connection_id PINECONE_API_KEY=your_pinecone_api_key PINECONE_INDEX=your_pinecone_index ``` --- ## Step 2: Initialize the SDKs ```typescript import 'dotenv/config'; import { UnifiedTo } from '@unified-api/typescript-sdk'; import { Pinecone } from '@pinecone-database/pinecone'; const { UNIFIED_API_KEY, CONNECTION_ATS, CONNECTION_GENAI, PINECONE_API_KEY, PINECONE_INDEX, } = process.env; const sdk = new UnifiedTo({ security: { jwt: UNIFIED_API_KEY! }, }); const pinecone = new Pinecone({ apiKey: PINECONE_API_KEY! }); const index = pinecone.Index(PINECONE_INDEX!); ``` --- ## Step 3: Get your customer's connection ID Before you can fetch candidates, your customer authorizes your app to access their ATS (e.g., Lever, Greenhouse) through Unified.to's embedded authorization flow. Once authorized, you receive a **connection ID** for that customer's integration. Store it securely and use it in all API calls for that customer. --- ## Step 4: Fetch and normalize candidate records Fetch candidates from the ATS and flatten each into a single text block for embedding. Normalizing the resume text before embedding keeps embeddings consistent across every ATS. ```typescript import type { AtsCandidate } from '@unified-api/typescript-sdk/models/components'; export async function fetchCandidates(connectionId: string): Promise { return await sdk.ats.listAtsCandidates({ connectionId, limit: 50, }); } export function normalizeResume(candidate: AtsCandidate): string { const experiences = (candidate.experiences ?? []) .map((exp: any) => `${exp.title ?? ''} at ${exp.company_name ?? ''}`) .join('; '); const education = (candidate.education ?? []) .map((edu: any) => `${edu.degree ?? ''} in ${edu.field_of_study ?? ''} from ${edu.institution ?? ''}`) .join('; '); return [ `Name: ${candidate.name ?? ''}`, `Email: ${candidate.emails?.[0]?.email ?? ''}`, `Title: ${candidate.title ?? ''}`, `Skills: ${(candidate.skills ?? []).join(', ')}`, `Experience: ${experiences}`, `Education: ${education}`, ].join('\n'); } ``` --- ## Step 5: Embed resumes with the GenAI API Use Unified.to's GenAI embedding endpoint. Note `content` is an array, `encoding_format` takes `FLOAT`, and `type` distinguishes documents you index (`SEARCH_DOC`) from the recruiter query (`SEARCH_QUERY`). The `embeddings` field returns a JSON string, so parse it before use. ```typescript export async function embed(text: string, kind: 'SEARCH_DOC' | 'SEARCH_QUERY'): Promise { const result = await sdk.genai.createGenaiEmbedding({ connectionId: CONNECTION_GENAI!, genaiEmbedding: { modelId: 'text-embedding-3-small', content: [text], // If the SDK rejects this key, the upstream schema spells it `enconding_format`; // the mechanically camelCased form is then `encondingFormat`. One live call confirms which. encodingFormat: 'FLOAT', type: kind, dimension: 1536, }, }); // `embeddings` is a read-only JSON string per the GenAI model. return JSON.parse(result.embeddings ?? '[]'); } ``` ### Keeping the vector index current To keep your retrieval layer fresh, subscribe to ATS webhooks for candidate create and update events. When a resume changes, re-fetch the candidate, re-embed, and upsert. Unified.to manages native and virtual webhooks, so you receive these events even when the ATS has no native webhook support. --- ## Step 6: Upsert embeddings to Pinecone ```typescript export async function upsertCandidate(candidate: AtsCandidate, values: number[]) { await index.upsert([ { id: candidate.id!, values, metadata: { name: candidate.name ?? '', email: candidate.emails?.[0]?.email ?? '', candidate_id: candidate.id ?? '', }, }, ]); } ``` --- ## Step 7: Retrieval Given a recruiter query, embed it with `SEARCH_QUERY` and search Pinecone for the closest candidates. ```typescript export async function searchCandidates(query: string) { const queryVector = await embed(query, 'SEARCH_QUERY'); const results = await index.query({ vector: queryVector, topK: 5, includeMetadata: true, }); return results.matches; } ``` --- ## Step 8: Putting it together ```typescript async function main() { const candidates = await fetchCandidates(CONNECTION_ATS!); for (const candidate of candidates) { const resumeText = normalizeResume(candidate); const values = await embed(resumeText, 'SEARCH_DOC'); await upsertCandidate(candidate, values); } const matches = await searchCandidates('Senior Python developer with fintech experience'); console.log('Top matches:', matches); } main(); ``` --- ## What you built - A single API call fetches candidate records from any ATS (Lever, Greenhouse, and 80+ more). - Resumes are normalized and embedded through Unified.to's GenAI API, then upserted into Pinecone. - Recruiters search in natural language and retrieve the most relevant candidates — over data fetched live from the source, with nothing cached on Unified.to's side. For the product-level walkthrough, see [How to Build a Candidate Assessment Product with Unified.to](https://unified.to/blog/how_to_build_a_candidate_assessment_product_with_unified). To wire up sourcing, see How to Build Candidate Sourcing with a Unified API. Ready to build? [Sign up for a free 30-day trial](https://app.unified.to/) or [book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified). ## Building AI applications with Unified and Langbase URL: https://docs.unified.to/guides/building_ai_applications_with_unified_and_langbase # Building AI applications with Unified and Langbase ------ _February 27, 2025_ ![unified_langbase_2.png](https://s3.us-east-2.amazonaws.com/unified-article-images/building_ai_applications_with_unified_and_langbase-0.png) Developers building AI applications for their customers face a common challenge: they need to juggle many different APIs and authorization flows, configure and manage multiple data pipelines, and deal with complex data models. What if you could focus on building amazing AI experiences without getting bogged down by all that complexity? That's where the power of Unified.to's APIs combined with [Langbase's](https://langbase.com/) serverless AI developer platform comes in. In this article, we'll look at how you can leverage these platforms to build sophisticated AI applications using your customers' data while writing significantly less integration and AI boilerplate code. ## Why use Unified.to with Langbase? When you pair Unified.to's normalized data access with Langbase's AI capabilities, you get: - **One API, many integrations:** Instead of building separate integrations for each platform, use [Unified.to](https://unified.to/)'s unified APIs to access normalized data from multiple data sources through a single, unified API. Langbase does the same for AI—with multi-agent orchestration and advanced long-term memory, seamlessly integrating AI capabilities. It supports 250+ LLMs through one API, ensuring a unified developer experience with easy model switching and optimization. It's an API-first platform, with simple Pipe and Memory Agent APIs for effortless integration. - **Unified authorization:** Seamlessly handle authorization with Unified.to's pre-built components, creating a secure link between your app and your customers' data with just a few lines of code. - **Production-ready AI:** Langbase Pipes and Memory Agents (serverless RAG agents with long-term memory) eliminate infrastructure management, making scaling and deployment seamless for all developers—not just AI/ML experts. Its composable AI infrastructure lets developers chain multiple models into AI pipelines, optimizing cost, performance, and personalization at every step. - **Real-time updates:** Build dynamic applications using Unified.to's webhook system. Fine-tine AI models on the fly when your customers have new data, allowing your AI agent to provide more relevant responses. ## Real-world applications you can build Let's look at some practical applications you can create by combining these platforms. **Intelligent support bots** Build an AI-powered support bot that can answer questions using your customer's knowledge base, handle real-time inquiries, and maintain context across conversations. By combining Unified.to's KMS (Knowledge Management System) and messaging APIs with [Langbase's Memory Agents](https://langbase.com/docs/memory) , you can create a bot that gets smarter as your knowledge base grows. The best part? Since you're using unified APIs, you can easily adapt this pattern for different platforms: - Use Notion, Confluence or other KMS platforms for your knowledge base - Deploy your bot on Slack instead of Discord Want to build your own? Check out our step-by-step [guide on building a Discord support bot.](https://docs.unified.to/guides/how_to_build_a_discord_support_bot_with_unified_and_langbase#how-to-build-a-discord-support-bot-with-unifiedto-and-langbase) **AI-powered recruitment assistant** Create an intelligent recruiting assistant that streamlines your hiring process by automatically screening candidates, conducting initial interviews, updating candidates' application statuses, and providing detailed summaries to your hiring team. Using Unified.to's [ATS integrations](https://unified.to/ats) and Langbase [Pipe agents](https://langbase.com/docs/pipe/quickstart), your assistant can handle the time-consuming parts of recruitment while maintaining a personal touch. The unified approach means you can support multiple ATS platforms (Lever, Greenhouse, Workday, etc.) without rewriting your core logic. **Enhanced email agents** With Unified.to and Langbase, you can automate email responses and streamline communication. By combining Unified.to's email integrations with Langbase's composable multi-agent architecture, you can build an AI-powered email workflow that enhances efficiency and personalization. **Intelligent sales assistant** Supercharge your sales team with an AI assistant that understands your customer relationships, helps qualify leads, and generates personalized follow-ups based on past interactions. Using Unified.to's CRM integrations with [Langbase's Memory Agents](https://langbase.com/docs/memory) (that dynamically attach private data to any LLM at scale, with industry-leading accuracy in advanced agentic routing and intelligent reranking), you can create an assistant that becomes an invaluable part of your sales process. Create leads and deals in your CRM automatically and tap into Unified.to's Enrichment API to add extra contextual data for the Memory Agent. ## Getting started Ready to start building? Here's how to get going: 1. Sign up for accounts on [Unified.to](https://unified.to/) and[ Langbase](https://langbase.com/) 2. Join our [Discord community](https://discord.gg/85z7HF7JbD) to connect with other developers 3. Keep an eye on our blog for upcoming guides in this series ## Looking ahead The combination of Unified.to and Langbase opens up exciting possibilities for AI application development. As both platforms continue to evolve, you can expect: - More supported platforms and integrations - Enhanced AI capabilities through Langbase's Memory Agents - New unified APIs for different use cases - Expanded tutorials and example applications Stay tuned for our upcoming tutorials that will dive deep into each use case, providing concrete examples and code you can use in your own applications. ## Concur & Concur (Company) — Connection Guide URL: https://docs.unified.to/guides/concur_and_concur_company_connection_guide # Concur & Concur (Company) — Connection Guide ------ _April 10, 2026_ ## Overview There are two SAP Concur integrations are available. You will need to choose which one to use based off of the authentication type and required data objects. ### Integrations - **Concur** - End-user OAuth (authorization code flow) - Categories: Accounting, Storage, Auth - **Concur (Company)** - Company-level authentication - Uses credentials instead of user OAuth - Categories: Accounting, HRIS, Storage, Auth --- ## Concur vs Concur (Company) ### Concur - **Authentication:** OAuth 2.0 (user-based) - **Credentials:** - Client ID - Client Secret - Redirect URI - **Supported Objects:** - Accounting — Expense - Accounting — Invoice - Accounting — Contact - Storage — File - **Use Case:** - User connects their own account and grants access --- ### Concur (Company) - **Authentication:** Company token flow (server-to-server) - **token_names:** - Client ID - Client Secret - Company UUID - Request Token - **Supported Objects:** - Accounting — Expense - Accounting — Invoice - Accounting — Contact - HRIS — Employee - Storage — File - **Use Case:** - Company-wide access - Backend integrations - Employee data access --- ## When to Use Which - **Use Concur** - When a user signs in and authorizes access - **Use Concur (Company)** - When you need company-wide access - When working with employee data - When SAP requires Company UUID + Request Token --- ## OAuth Scopes | Object | Read | Write | | ------- | --------------------------------------------------- | --------------------------------------------------- | | Expense | EXPRPT | EXPRPT | | Invoice | INVPMT | INVPMT | | File | IMAGE | IMAGE | | User | `identity.user.ids.read`, `identity.user.core.read` | `identity.user.ids.read`, `identity.user.core.read` | | Vendor | INVVEN | INVVEN | --- ## Concur (Company) — Credential Details ### Required Fields - **Client ID:** From SAP Concur OAuth Application Management - **Client Secret:** From the same OAuth application - **Company UUID:** Unique identifier for your company in SAP Concur - **Request Token:** Temporary token generated via SAP Concur Company Request Token tool (Guide: [https://developer.concur.com/api-reference/authentication/company-refresh-tool.html](https://developer.concur.com/api-reference/authentication/company-refresh-tool.html)) - Not long-lived - Must be regenerated when expired --- ## Region Reference Select the correct API region based on your tenant: - Production (US2 default) - EU2 - APJ1 - US Government - Sandbox / Implementation If unsure, confirm with your SAP Concur administrator. --- ## Network Requirements To ensure successful API communication, you may need to whitelist Unified IP addresses on your SAP Concur account. You can find the list of IP addresses here: [https://app.unified.to/apikeys](https://app.unified.to/apikeys) Failure to whitelist these IPs may result in connection or data access issues. ## Connect Amazon Seller Central to Unified URL: https://docs.unified.to/guides/connect_amazon_seller_central to_unified # Connect Amazon Seller Central to Unified ------ _May 13, 2026_ **What this integration covers** | Area | Unified objects | Typical use | | -------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | **E‑Commerce** | commerce_item, commerce_collection, commerce_inventory, commerce_saleschannel | Catalog, product types, FBA inventory summaries, marketplace participation | | **Accounting** | commerce_account, accounting_salesorder, accounting_contact | Orders and related account/contact style data from SP-API | | **Metadata** | metadata_metadata | Custom / structured metadata mapping where supported | Exact read/write support follows your workspace permissions and the integration's object support matrix in the Unified app. --- **Prerequisites (Amazon side)** 1. **Active Seller Central** account for the marketplaces you care about. 2. **SP-API developer registration** — create and configure a **Selling Partner API** application in the Amazon developer / SP-API flow. Please follow this guide: [https://developer-docs.amazon.com/sp-api/docs/registering-your-application](https://developer-docs.amazon.com/sp-api/docs/registering-your-application) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connect_amazon_seller_central to_unified-0.png) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connect_amazon_seller_central to_unified-1.png) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connect_amazon_seller_central to_unified-2.png) Put the redirect_url as shown here: [https://app.unified.to/integrations/amazonsellercentral?tab=oauth2](https://app.unified.to/integrations/amazonsellercentral?tab=oauth2) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connect_amazon_seller_central to_unified-3.png) 3. **LWA (Login with Amazon) security profile** linked to that application so you obtain: - **LWA Client ID** - **LWA Client Secret** 1. **Marketplace ID(s)** for each marketplace (e.g. US). Reference: Marketplace IDs. 2. **Correct region** for the SP-API endpoint you will use (see below). **NOTE:** Amazon's SP-API documentation also describes **IAM users/roles and request signing** for many operations. Follow Amazon's current SP-API onboarding for your app (including any **IAM / signing** requirements Amazon still enforces for certain endpoints). If calls fail with auth errors, verify the app's SP-API roles and Amazon's latest auth requirements. --- **Choose the SP-API region (must match your selling region)** When connecting, pick the environment that matches where you sell (this selects **authorize URL** and **SP-API base URL**): | Region | Host | | ----------------- | -------------------------------- | | **North America** | **Seller Central** host : .com | | **Europe** | **Seller Central** host : .co.uk | | **Far East** | **Seller Central** host : .co.jp | --- **Connect in Unified (step-by-step)** 1. **Open Integrations** (or your product's connection UI) and activate **Amazon Seller Central**. 2. **Enter OAuth app credentials** from Amazon (LWA **Client ID** and **Client Secret**) in the fields your Unified workspace uses for this integration (same pattern as other OAuth2 apps). 3. **Set Marketplace ID** in the field Unified uses as the developer / connection key for this integration (**labeled as Marketplace ID** in vendor config). 4. **Select the correct regional URL** (North America vs Europe vs Far East) so authorize and API hosts align with your seller account. 5. **Start OAuth** — you will be sent to **Seller Central** to approve the app (/apps/authorize/consent). --- **Sandbox** Amazon documents an SP-API sandbox: [https://app.unified.to/integrations/amazonsellercentral?tab=partnership](https://app.unified.to/integrations/amazonsellercentral?tab=partnership) Use it if you are testing before production seller data. ## Connecting BambooHR via OAuth 2 URL: https://docs.unified.to/guides/connecting_bamboohr_via_oauth_2 # Connecting BambooHR via OAuth 2 ------ _July 28, 2026_ # BambooHR OAuth Setup Guide ## Overview First, register an application in the BambooHR Developer Portal to obtain OAuth credentials. Next, configure those credentials in Unified. After the setup is complete, your end users can authorize their own BambooHR accounts through your embedded Unified connection flow. --- # Part A — Set Up Your App in the BambooHR Developer Portal > **One-time setup** ## 1. Open the BambooHR Developer Portal Go to: [https://developers.bamboohr.com/home/applications](https://developers.bamboohr.com/home/applications) Sign in to your BambooHR Developer Portal account. ## 2. Create a New Application ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connecting_bamboohr_via_oauth_2-0.png) 1. Click **Add application** in the top-right corner. 2. Enter an **Application Name**, for example: `unified` 3. Click **Create Application**. ## 3. Collect Your Application Details After creating the application, note the following values: - **Application Key** - **Application ID** You will need the **Application Key** when configuring the integration in Unified. ## 4. Copy Your OAuth Credentials Under **App Credentials**, copy the following: - **Client ID** - **Client Secret** Use the eye icon to reveal the Client Secret. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connecting_bamboohr_via_oauth_2-1.png) You can use **Regenerate** to create a new Client Secret when required. > **Important:** Treat the Client Secret like a password. If you regenerate it, you must also update the Client Secret in Unified. ## 5. Add the Redirect URI Under **Redirect URIs**, add the OAuth callback URL provided by Unified. ## 6. Select the Required Scopes Go to: **Application Scopes → Select Scopes** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connecting_bamboohr_via_oauth_2-2.png) For each available category, such as Employee, Company, Hiring, Time Off, and Reports, choose the appropriate permission level: - **No Access** - **Read** - **Read / Write** Configure the following scopes as required by your implementation: ```plain text email openid company:info company_file company_file.write employee employee.write employee:assets employee:assets.write employee:compensation employee:compensation.write employee:contact employee:contact.write employee:custom_fields employee:custom_fields.write employee:custom_fields_encrypted employee:custom_fields_encrypted.write employee:demographic employee:demographic.write employee:dependent employee:dependent.write employee:dependent:ssn employee:dependent:ssn.write employee:education employee:education.write employee:emergency_contacts employee:emergency_contacts.write employee:file employee:file.write employee:identification employee:identification.write employee:job employee:job.write employee:management employee:management.write employee:name employee:name.write employee:payroll employee:payroll.write employee:photo employee:photo.write employee:providers employee:providers.write employee:providers:payroll employee:providers:payroll.write employee:vaccination employee:vaccination.write employee_verifications employee_verifications.write goal goal.write onboarding onboarding.write performance:assessments performance:assessments.write performance:feedback performance:feedback.write performance:one_on_ones performance:one_on_ones.write sensitive_employee:address sensitive_employee:address.write sensitive_employee:creditcards sensitive_employee:creditcards.write sensitive_employee:protected_info sensitive_employee:protected_info.write tasks tasks.write hiring:applications hiring:applications.write hiring:job_openings hiring:job_openings.write offline_access user user.write report report.write time_off time_off.write time_off:requests time_off:requests.write ``` --- # Part B — Configure BambooHR in Unified ## 7. Configure the BambooHR Integration In the Unified dashboard, go to: **Integrations → BambooHR → Authorization** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connecting_bamboohr_via_oauth_2-3.png) Configure the following fields: - Set the authorization method to **OAuth 2** - Paste the **OAuth 2 Client ID** from Step 4 - Paste the **OAuth 2 Client Secret** from Step 4 - Enter the **Developer API Key** - Use the **Application Key** collected in Step 3 Click **Update** to save the configuration. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connecting_bamboohr_via_oauth_2-4.png) ## 8. Configure the Embedded Component In the Unified dashboard, go to: **Embedded components** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connecting_bamboohr_via_oauth_2-5.png) Configure the following settings: - **Success URL** - **Failure URL** - **Data region**, such as: - US - EU - AU - **Permission Scopes / Categories** Copy the appropriate embed code into your application. Unified provides embed options for: - React - Angular - Vue - Svelte - JavaScript - API-based implementations --- # Part C — End-User Authorization Flow ## 9. Enter the BambooHR Domain In the embedded authorization flow, the end user sees the **Authorize Access** screen. The user enters their **BambooHR Domain** and clicks **Authorize**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connecting_bamboohr_via_oauth_2-6.png) ## 10. Approve Access BambooHR displays a consent screen similar to: > **Share access with unified?** The user reviews the requested scopes and clicks **Allow Access**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connecting_bamboohr_via_oauth_2-7.png) ## 11. Confirm the Connection After authorization is completed, the connection appears in Unified under: **Settings → Connections** The connection should display a **Healthy** status. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/connecting_bamboohr_via_oauth_2-8.png) ## Connecting Google Workspace Integrations with a Service Account URL: https://docs.unified.to/guides/connecting_google_workspace_integrations_with_a_service_account # Connecting Google Workspace Integrations with a Service Account ------ _May 21, 2026_ Unified API supports two ways to authenticate Google integrations: the standard interactive **OAuth2** flow (a user clicks "Allow"), or a **service account** for server-to-server access with no human in the loop. This guide covers the service account path: how to create one, how to grant it access to your data, and what to enter in Unified API. ## **When to use a service account** Use a service account when you want a backend system to access Google data without a user logging in each time — for example, syncing every mailbox in your company, reading a shared Drive, or managing your Workspace directory. For most Workspace data (Gmail, Calendar, Drive files owned by users, etc.) the service account must **impersonate** a user via **domain-wide delegation (DWD)**. For a few APIs (Analytics, Merchant Center, Campaign Manager) you instead just **share** the resource with the service account's email — no delegation needed. --- ## **Step 1 — Create a Google Cloud project** 1. Go to the [Google Cloud Console](https://console.cloud.google.com/). 2. Create a new project (or select an existing one) using the project picker at the top. 3. Note the **Project ID** — you'll see it referenced in the key file later. ## **Step 2 — Enable the APIs you need** 1. Navigate to **APIs & Services → Library**. 2. Search for and **Enable** the API for each integration you'll use: | **Integration** | **API to enable** | | ------------------- | ------------------------------------- | | Google Drive | Google Drive API | | Gmail | Gmail API | | Google Calendar | Google Calendar API | | Google Sheets | Google Sheets API | | Google Docs | Google Docs API | | Google Slides | Google Slides API | | Google Forms | Google Forms API | | Google Tasks | Google Tasks API | | Google Contacts | People API | | Google Meet | Google Meet API + Google Calendar API | | Workspace Directory | Admin SDK API | If an API isn't enabled, calls will fail with a `403 ... API has not been used in project` error. ## **Step 3 — Create the service account** 1. Go to **APIs & Services → Credentials → Create credentials → Service account**. 2. Give it a name (e.g. `unified-api-sync`) and click **Create and continue**. 3. You can skip the optional "grant access" steps for now. Click **Done**. 4. You'll land on the service account list. Note its **email** — it looks like:`unified-api-sync@your-project-id.iam.gserviceaccount.com` ## **Step 4 — Generate a key** 1. Click the service account, then open the **Keys** tab. 2. **Add key → Create new key → JSON → Create.** 3. A `.json` file downloads. **Store it securely** — Google does not let you re-download it. The file looks like this (the two fields you need are highlighted): ```json { "type": "service_account", "project_id": "your-project-id", "private_key_id": "abc123...", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEv...\n-----END PRIVATE KEY-----\n", ← Service Account Private Key "client_email": "unified-api-sync@your-project-id.iam.gserviceaccount.com", ← Service Account Email "client_id": "11223344...", "token_uri": "https://oauth2.googleapis.com/token", ... } ``` --- ## **Step 5 — Grant the service account access to your data** This is the step that differs by API. Pick the path that matches your integration. ### **Path A — Domain-wide delegation (Workspace user data)** Required for **Gmail, Calendar, Drive (user files), Sheets, Docs, Slides, Forms, Tasks, Contacts, Meet, and Directory**. This lets the service account act _as_ a specific user in your Workspace. 1. On the service account's detail page, note its **Client ID** (the numeric `client_id` from the JSON, also called "Unique ID"). 2. As a Workspace **super admin**, go to the [Admin Console → Security → Access and data control → API controls → Domain-wide delegation](https://admin.google.com/ac/owl/domainwidedelegation). 3. Click **Add new** and enter: - **Client ID**: the service account's numeric Client ID. - **OAuth scopes**: a comma-separated list of the scopes for the APIs you'll use (see the scope table below). 4. **Authorize.** Changes can take a few minutes to propagate. > Only the scopes you authorize here will work. If you add the Drive integration later, you must come back and add the Drive scope. ### **Path B — Resource sharing (no delegation)** For APIs where data is owned by an account/property rather than a user, you skip DWD and just grant the service account's **email** access inside the product: - **Google Analytics** — add the service account email as a user on the GA4 property. - **Merchant Center** — add it as a user in Merchant Center settings. - **Campaign Manager / Display & Video 360** — add it as a user in the platform. (These integrations aren't part of the Workspace set covered here but follow the same key-creation steps.) --- ## **Step 6 — Scopes per integration** When configuring domain-wide delegation (Path A), authorize the matching scope(s). These are exactly the scopes Unified API requests when minting tokens: | **Integration** | **Scope(s) to authorize** | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Google Drive | `https://www.googleapis.com/auth/drive` | | Gmail | `https://mail.google.com/` | | Google Calendar | `https://www.googleapis.com/auth/calendar` | | Google Sheets | `https://www.googleapis.com/auth/spreadsheets` | | Google Docs | `https://www.googleapis.com/auth/documents` | | Google Slides | `https://www.googleapis.com/auth/presentations` | | Google Forms | `https://www.googleapis.com/auth/forms.body`, `https://www.googleapis.com/auth/forms.responses.readonly` | | Google Tasks | `https://www.googleapis.com/auth/tasks` | | Google Contacts | `https://www.googleapis.com/auth/contacts` | | Google Meet | `https://www.googleapis.com/auth/calendar`, `https://www.googleapis.com/auth/meetings.space.readonly` | | Workspace Directory | `https://www.googleapis.com/auth/admin.directory.user`, `https://www.googleapis.com/auth/admin.directory.group`, `https://www.googleapis.com/auth/admin.directory.group.member` | --- ## **Step 7 — Configure the connection in Unified API** When creating the connection, choose the **Service Account** authentication option and fill in three fields: | **Field** | **Value** | | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Service Account Email** | The `client_email` from the JSON key file. | | **Service Account Private Key** | The `private_key` from the JSON key file — paste it whole, including the `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` lines. | | **Subject** | _(Optional)_ The email of the Workspace user to impersonate via domain-wide delegation. **Required for most Workspace data** (e.g. Gmail needs a mailbox owner). Leave blank only if the service account itself owns the data or you've shared resources directly with it. | That's it. Unified API signs a short-lived JWT with your private key, exchanges it with Google for an access token (caching it until it expires), and uses it for all API calls — no further interaction needed. --- ## **Troubleshooting** | **Error** | **Likely cause** | | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `unauthorized_client` | The scope isn't authorized in domain-wide delegation, or the Client ID is wrong. Re-check Step 5A. | | `403 ... has not been used in project` | The API isn't enabled for the project (Step 2). | | `400 invalid_grant` / `Invalid JWT Signature` | The private key was pasted incorrectly (truncated, or newlines lost). Re-copy the full `private_key` value. | | Empty results / `404` for a user's data | Missing or wrong **Subject** — you're querying as the service account itself instead of impersonating the user. | | `403 Not Authorized to access this resource` (Directory) | The impersonated **Subject** must be a Workspace **admin** with rights to the directory data. | ### **Security notes** - The private key grants standing access to your data — store it in a secret manager, never in source control. - Authorize only the scopes you actually use; broad scopes increase blast radius if the key leaks. - Rotate keys periodically (create a new key, update the connection, delete the old key). ## Correct WelcomeKit Scopes for Jungle Integration URL: https://docs.unified.to/guides/correct_welcomekit_scopes_for_jungle_integration # Correct WelcomeKit Scopes for Jungle Integration ------ _April 14, 2026_ This guide outlines the required WelcomeKit scopes per Unified object for the Jungle integration. ## Scope Mapping (by Unified Object) | Unified Object | Required Scope(s) | Partnership (su_*) Needed? | | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | | **ATS · Job (**`ats_job`**)** | `su_jobs_r` | Yes | | **ATS · Candidate (**`ats_candidate`**)** | **Read:** `candidates_r` AND `my_candidates_r`
**Write:** `candidates_rw` AND `my_candidates_rw` | No | | **ATS · Document (**`ats_document`**)** | **Read:** `documents_r` AND (`candidates_r` & `my_candidates_r`)
**Write:** `documents_rw` AND (`candidates_rw` & `my_candidates_rw`) | No | | **ATS · Activity (**`ats_activity`**)** | **Read:** `emails_r` AND (`candidates_r` & `my_candidates_r`)
**Write:** `comments_w` AND (`candidates_rw` & `my_candidates_rw`) | No | | **HRIS · Company (**`hris_company`**)** | **Read:** `su_organizations_r` | Yes | | HRIS · Location (hris_location) | Read: `offices_r` & `su_offices_r`
Write: `offices_rw` | Yes | | HRIS · Group (hris_group) | Read: `departments_r` & `departments_rw` & `su_departments_r`
Write: `departments_rw` | Yes | --- ## Important Notes ### Partnership-only scopes - `su_jobs_r` → Required for listing jobs - `su_organizations_r` → Required for listing organizations - `su_offices_r` → Required for listing locations - `su_departments_r` → Required for listing groups These require WelcomeKit partnership approval. ### **Non-partner access (scope overrides)** If you do **not** have WelcomeKit partnership approval, you won't be able to use the partnership-only scopes. Instead, request the standard non-partner scopes by overriding the defaults for this integration. Each Unified permission (like `ats_job_read`) maps to a default set of the provider's raw scopes, and you can replace that default per integration via the integration's scope override setting. Update the `WorkspaceIntegration` object with an `overriden_scopes` map keyed by the Unified permission, where each value is a comma-separated list of the provider's raw scopes: `{ "overriden_scopes": { "ats_job": "jobs_r" } }` With this override, Unified requests `jobs_r` (org-scoped jobs) instead of `su_jobs_r`. At runtime, the connector lists jobs via `GET /jobs?organization_reference=...` using the organization cached on the connection during auth setup, and falls back to `GET /users/current` for organization discovery — so non-partner keys never depend on the `su_*` scopes. For full details, see [Overriding scopes per integration](https://docs.unified.to/concepts/scopes#overriding-scopes-per-integration). --- ### Candidate-dependent scopes The following always require candidate access: - Documents → require candidate read/write scopes - Emails → require candidate read scopes - Comments → require candidate write scopes --- ## How to Get Partnership Access (Jungle / WelcomeKit) To access partnership-only scopes (`su_*`), you will need to be approved by WelcomeKit. ### Steps: 1. **Reach out to WelcomeKit support or your account manager (**[https://help.welcometothejungle.com/en](https://help.welcometothejungle.com/en)) - Request access to partnership scopes - Clearly mention your use case (e.g. job syncing, org-level data access) 2. **Provide integration details** - Your application name - What data you intend to access - Whether access is read-only or includes write operations 3. **Explain your end-user flow** - How users connect their WelcomeKit account - Why elevated (partnership) access is required 4. **Await approval** - Once approved, the scopes will be enabled for your app - You can then include `su_*` scopes in your OAuth flow ## Creating Ads using the Unified Ads API URL: https://docs.unified.to/guides/creating_ads_using_the_unified_ads_api # Creating Ads using the Unified Ads API ------ _March 14, 2026_ # Unified Ads API Overview The Unified API Ads category models advertising structures across Meta, Google, Amazon, Twitter, LinkedIn, and other providers. Understanding how objects relate helps you build the correc flows. ### Core Hierarchy Most integrations follow a hierarchy from organization → campaign → ad group → ad: ```plain text ads_organization (ad account) └── ads_campaign └── ads_group (ad set, line item, etc.) └── ads_ad ``` | Object | Description | Parent(s) | Typical use | | -------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------- | | **ads_organization** | The ad account (e.g., Meta ad account, Google Ads customer). Billing and access are scoped here. | — | List accounts, select which account to use | | **ads_campaign** | A campaign groups ad sets under a goal, budget, and schedule. | `organization_id` | Set campaign-level budget, dates, objective | | **ads_group** | An ad set or line item. Holds targeting, bid, and budget. Often called "ad set" (Meta) or "line item" (DV360). | `campaign_id`, `organization_id` | Define who sees ads (targeting), bid strategy, promoted object | | **ads_ad** | The actual ad creative + placement. What users see. | `group_id`, `campaign_id`, `organization_id` | Creative content, destination URL, promoted entity (e.g., tweet, product) | ### Creatives and Insertion Orders | Object | Description | Parent(s) | Notes | | ---------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------ | | **ads_creative** | The creative asset (image, video, copy). Some providers treat it as a separate resource; others embed it in the ad. | `group_id`, `campaign_id`, `organization_id` | Meta: can be shared across ads. Amazon/Twitter: often 1:1 with ad. | | **ads_insertionorder** | DV360-specific. Sits between campaign and line item. | `campaign_id`, `organization_id` | Similar to a purchase order for a campaign. | ### Discovery | Object | Description | Purpose | | --------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------- | | **ads_target** | Targeting options (countries, regions, interests, etc.). | Look up valid IDs to use in `targeting` when creating campaigns/groups. | | **ads_promote** | Promoted entities (pages, apps, products, tweets, ad group types). | Look up or pass IDs for `promoted` when creating groups/ads/creatives. | ### Reporting | Object | Description | Purpose | | -------------- | -------------------- | ---------------------------------------------------------------------- | | **ads_report** | Performance metrics. | Fetch impressions, clicks, spend, etc. by org, campaign, group, or ad. | ### Provider Mapping Naming varies by provider: | Unified API | Meta Ads | Google Ads | Amazon Ads | Twitter Ads | LinkedIn | | ---------------- | ----------- | ---------- | ---------- | -------------- | ----------------- | | ads_organization | Ad Account | Customer | Profile | Account | Sponsored Account | | ads_campaign | Campaign | Campaign | Campaign | Campaign | Campaign Group | | ads_group | Ad Set | Ad Group | Ad Group | Line Item | Campaign | | ads_ad | Ad | Ad | Product Ad | Promoted Tweet | Creative | | ads_creative | Ad Creative | Ad | Product Ad | Promoted Tweet | Creative | _Some integrations collapse levels (e.g., LinkedIn's "Campaign" maps to our ads_group)._ ### Creating Ads: Required Parent IDs When creating objects, you typically need parent IDs: - **Create campaign:** `organization_id` - **Create ad group:** `organization_id`, `campaign_id` - **Create ad:** `organization_id`, `campaign_id`, `group_id` - **Create creative:** `organization_id`, `campaign_id`, `group_id` (varies by provider) The `promoted` field is required when creating ad groups or ads that need a promoted entity (e.g., Meta ad set with a page, Twitter promoted tweet with a tweet ID). Use `ads_promote` to discover options, or pass IDs manually when the provider has no list API. --- # Promoted Explained The new `promoted` field specifies promoted entities (pages, apps, products, tweets, ad group types) when creating and managing ads across the Unified API. Ads, Campaigns, and AdGroups have a `promoted` field to specify an ID of an entity to promote. There is also a `ads_promote` endpoint that provides a clean way to query the promoted entities. ```json { "organization_id": "act_123", "campaign_id": "456", "name": "My Ad Group", "promoted": [ { "id": "789", "type": "PAGE_ID" } ] } ``` --- ## The AdsPromoted Object Each promoted entity has a simple structure: | Field | Type | Required | Description | | ------ | ------ | -------- | ----------------------------------------------- | | `id` | string | Yes | The entity ID (e.g., page ID, ASIN, tweet ID) | | `name` | string | No | Display name (optional, used in list responses) | | `type` | string | Yes | One of the supported promoted types (see below) | ### Supported Promoted Types | Type | Integration(s) | Description | | --------------- | -------------------- | ----------------------------------------------------------------- | | `PAGE_ID` | Meta Ads | Facebook Page to promote | | `APP_ID` | Meta Ads | App to promote | | `PIXEL_ID` | Meta Ads | Conversion pixel | | `CATALOG_ID` | Meta Ads | Product catalog | | `STORE_URL` | Meta Ads | Store URL (manual input) | | `PRODUCT_ID` | Amazon Ads | Product ASIN (manual input) | | `TWEET_ID` | Twitter Ads | Tweet to promote | | `AD_GROUP_TYPE` | LinkedIn, Google Ads | Campaign/ad group type (e.g., SPONSORED_UPDATES, SEARCH_STANDARD) | --- ## GET /ads/{connection_id}/promoted Endpoint Use the `ads_promote` list endpoint to discover available promoted entities that the integration supports. **Endpoint:** `GET /ads/{connection_id}/promote` **Query parameters:** | Parameter | Type | Description | | --------- | ------ | ----------------------------------------------------- | | `type` | string | Filter by promoted type (e.g., `PAGE_ID`, `TWEET_ID`) | | `org_id` | string | Organization/account I | | `query` | string | Optional search filter | | `limit` | number | Max results (default 100) | | `offset` | number | Pagination offset | ### Example: List Facebook Pages (Meta Ads) ```bash GET /ads/{connection_id}/promote?type=PAGE_ID ``` Response: ```json [ { "id": "123456789", "name": "My Business Page", "type": "PAGE_ID" }, { "id": "987654321", "name": "Another Page", "type": "PAGE_ID" } ] ``` ### Example: List User Tweets (Twitter Ads) ```bash GET /ads/{connection_id}/promote?type=TWEET_ID ``` ### Example: List Ad Group Types (Google Ads, LinkedIn) ```bash GET /ads/{connection_id}/promote?type=AD_GROUP_TYPE ``` Some integrations will return static options (e.g., `SEARCH_STANDARD`, `DISPLAY_STANDARD` for Google; `SPONSORED_UPDATES`, `TEXT_ADS` for LinkedIn). ### When ads_promoted Returns Empty Some promoted types have no discovery API (e.g., Amazon `PRODUCT_ID`, Meta `STORE_URL`). In those cases, `ads_promote` returns an empty array. You can still create ads by passing `promoted` manually with IDs you already know (e.g., ASINs for Amazon, store URLs for Meta). --- ## Quick Reference: Create Examples ### Meta Ads – Create Ad Group with Page ```json POST /ads/{connection_id}/group { "organization_id": "act_123", "campaign_id": "456", "name": "My Ad Group", "promoted": [{ "id": "789", "type": "PAGE_ID" }] } ``` ### Amazon Ads – Create Product Ad ```json POST /ads/{connection_id}/ad { "organization_id": "profile_123", "campaign_id": "456", "group_id": "789", "name": "Product Ad", "promoted": [{ "id": "B08N5WRWNW", "type": "PRODUCT_ID" }] } ``` ### Twitter Ads – Create Promoted Tweet ```json POST /ads/{connection_id}/ad { "organization_id": "account_123", "group_id": "line_item_456", "name": "Promoted Tweet", "promoted": [{ "id": "1234567890", "type": "TWEET_ID" }] } ``` ### LinkedIn – Create Ad Group (Campaign) ```json POST /ads/{connection_id}/group { "organization_id": "123", "campaign_id": "456", "name": "Sponsored Updates Campaign", "promoted": [{ "id": "SPONSORED_UPDATES", "type": "AD_GROUP_TYPE" }] } ``` ### Google Ads – Create Ad Group ```json POST /ads/{connection_id}/group { "organization_id": "123", "campaign_id": "456", "name": "Search Ad Group", "promoted": [{ "id": "SEARCH_STANDARD", "type": "AD_GROUP_TYPE" }] } ``` --- ## Using Targeting Targeting lets you define who sees your ads (geography, demographics, interests, etc.). Use the `ads_target` endpoint to look up valid targeting IDs, then pass them in the `targeting` field when creating campaigns, ad groups, or ads. ### The ads_target Endpoint Look up targeting options (countries, regions, cities, interests, etc.) before creating ad objects. **Endpoint:** `GET /ads/{connection_id}/target` **Query parameters:** | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------- | | `type` | string | Yes | Targeting type (see supported types below) | | `query` | string | Yes* | Search term (*required for most types) | | `org_id` | string | No | Organization/account ID (required for some types) | | `limit` | number | No | Max results (default 100) | | `offset` | number | No | Pagination offset | **Supported** **`type`** **values:** `countries`, `regions`, `cities`, `zips`, `us_dmas`, `locales`, `interests`, `behaviors`, `topics`, `user_lists`, `age_ranges`, `genders` _Support varies by integration. Meta Ads supports most types; others (e.g., Amazon, Twitter) may support only_ _`countries`_ _and_ _`locales`__._ ### Example: Look Up Countries ```bash GET /ads/{connection_id}/target?type=countries&query=united ``` Response: ```json [ { "id": "US", "name": "United States", "value": "US", "type": "countries" }, { "id": "GB", "name": "United Kingdom", "value": "GB", "type": "countries" } ] ``` ### Example: Look Up Regions (e.g., US States) ```bash GET /ads/{connection_id}/target?type=regions&query=california ``` Response: ```json [ { "id": "3843", "name": "California", "value": "3843", "type": "regions" } ] ``` ### Example: Look Up Interests (Meta Ads) ```bash GET /ads/{connection_id}/target?type=interests&query=technology ``` ### Using Targeting IDs When Creating Ads Pass the `id` (or `value`) from `ads_target` responses into the `targeting` object. The structure matches the targeting type: - **Countries:** `targeting.geographic.countries` — array of ISO country codes (e.g., `["US", "CA"]`) - **Regions:** `targeting.geographic.regions` — array of `{ id, name? }` (use `id` from ads_target) - **Cities:** `targeting.geographic.cities` — array of `{ id, name?, radius?, radius_unit? }` - **Demographics:** `targeting.demographic` — age and gender (no lookup needed; use values directly) --- ## Targeting Examples ### Geo-Targeting: Countries and Regions First, look up IDs: ```bash # Get country codes GET /ads/{connection_id}/target?type=countries&query=united # Get regions (e.g., states) — for regions, query often includes country context GET /ads/{connection_id}/target?type=regions&query=california ``` Then create an ad group with geographic targeting: ```json POST /ads/{connection_id}/group { "organization_id": "act_123", "campaign_id": "456", "name": "US & California Ad Group", "promoted": [{ "id": "789", "type": "PAGE_ID" }], "targeting": { "geographic": { "countries": ["US"], "regions": [ { "id": "3843", "name": "California" } ] } } } ``` ### Geo-Targeting: Cities with Radius ```json { "targeting": { "geographic": { "countries": ["US"], "cities": [ { "id": "2420379", "name": "San Francisco", "radius": 25, "radius_unit": "MILES" } ] } } } ``` ### Demographic Targeting Demographic targeting uses simple values; no `ads_target` lookup is needed: ```json { "targeting": { "demographic": { "age_min": 25, "age_max": 54, "male": true, "female": true } } } ``` - `age_min` / `age_max`: 18–65 typically - `male` / `female`: `true` to include, `false` or omit to exclude ### Combined Geo + Demographic Targeting ```json POST /ads/{connection_id}/group { "organization_id": "act_123", "campaign_id": "456", "name": "US Adults 25-54", "promoted": [{ "id": "789", "type": "PAGE_ID" }], "targeting": { "geographic": { "countries": ["US"] }, "demographic": { "age_min": 25, "age_max": 54, "male": true, "female": true } } } ``` ### Audience Targeting (Interests, Custom Audiences) For interests and custom audiences, look up IDs first: ```bash GET /ads/{connection_id}/target?type=interests&query=technology GET /ads/{connection_id}/target?type=user_lists&query=my&org_id=act_123 ``` Then use the `id` values in `targeting.audience`: ```json { "targeting": { "audience": { "interests": [ { "id": "600313926646746", "name": "Technology" } ], "custom_audiences": [ { "id": "12345678", "name": "My Custom Audience" } ] } } } ``` --- ## Creating Walmart Items via Unified URL: https://docs.unified.to/guides/creating_walmart_items_via_unified # Creating Walmart Items via Unified ------ _July 31, 2025_ Unified allows you to create structured product listings on **Walmart Marketplace** via our Unified data model `commerce_item`. This guide explains the **critical fields** you must understand and provide correctly: --- ### 🧩 Key Concepts | Field | Description | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type` | This specifies the **Walmart subcategory** your item belongs to. It determines what metadata is required. Must be one of the supported 76 categories (see below). | | `global_code` | This is your product's **GTIN** (Global Trade Item Number), a required identifier for Walmart. It must be a **valid 12–14 digit string**. | | `metadata` | A dynamic array of key-value attributes that depends on the selected `type`. This is how you provide structured product details like color, material, warranty, etc. | --- ### 🧠 How Metadata Works Each `type` (subcategory) has a corresponding schema of fields allowed by Walmart. For example: - `animal_accessories` allows fields like `animalType`, `petSize`, `isFoldable`, `colorCategory`, etc. - `computers` allows fields like `processorType`, `screenSize`, `ramMemory`, `operatingSystem`, etc. **List the metadata fields from Unified** using this API ```typescript GET /metadata_metadata?type=commerce_item [ { "id": "shortDescription", "name": "shortDescription", "slug": "shortDescription", "format": "TEXT", "original_format": "string", "object_type": "commerce_item" }, { "id": "minimumRecommendedAge", "name": "minimumRecommendedAge", "slug": "minimumRecommendedAge", "format": "MEASUREMENT", "original_format": "{\"measure\":\"number\",\"unit\":[\"months\",\"years\"]}", "object_type": "commerce_item" }, { "id": "smallPartsWarnings", "name": "Small Parts Warning Code", "slug": "smallPartsWarnings", "format": "MULTIPLE_SELECT", "original_format": "string[]", "options": [ "0 - No warning applicable", "5 - Choking hazard is a marble", "4 - Choking hazard balloon", "6 - Choking hazard contains a marble", "2 - Choking hazard contains small ball", "3 - Choking hazard contains small parts", "1 - Choking hazard is a small ball" ], "object_type": "commerce_item" }, ] ``` You can filter these results by inspecting the supported pagination options. --- ### Supported `type` Values for Walmart Items (commerce_item) - `Skateboard Risers` - `Baby Play Yards` - `Power Hedge Trimmers` - `Eyeglass Cases` - `Microphone Splitters` - `Electric Buffet Servers` - `Radio Control Vehicle Servos` - `Sticky Notes` - `Matcha Tea Bowls & Whisks` - `Doughs` - `Taffy Candy` - `Hair Dryers` - `Base Layers` - `Emergency Lighting` - `Bidet Toilet Seats` - `Purse Making Supplies` - `Grill Grid Lifters` - `Hunting Scents` - `Automotive Headlight Restorer` - `Ear Thermometer Replacement Lens Filters` - `Glassware & Drinkware` - `Precious Metals` - `Lab Chromatography Paper` - `Plant Terrariums` - `Microphone Cables` - `Glucose Supplements` - `Snow Skis` - `Perineal Cleansers` - `Soda Maker Kits` - `Camera Accessory Bundles` - `Tetherball Equipment` - `Paint Reducers & Retarders` - `Pork Rinds` - `Portable Basketball Systems` - `Plate Joiners` - `Other Weight Loss Supplies` - `Powder Candy` - `Outdoor Kitchen Serving & Storage Carts` - `Unitards & Leotards` - `Lab Sample Bags` - `Fishing Rod & Reel Combos` - `Adhesive Tapes` - `Track & Field Markers` - `Fruit & Vegetable Corers` - `Automotive Fuel Tank Caps` - `Drawing Boards` - `Hand Dryers` - `Pregnancy Tests` - `Cat Litter Mats` - `Facial Wipes` - `Exercise Weights` - `Sewing Pins` - `Other Makeup Brushes & Tools` - `Other Shaving Supplies` - `Sugar Packet Holders` - `Honey Dippers` - `Artificial Topiaries` - `Cupcake Making Kits` - `Mobility Walker Bags, Baskets & Carriers` - `Faucet Water Filters` - `Urinary Tract Infection Test Strips` - `Baptism Gowns & Outfits` - `Function Generators` - `FM Transmitters` - `Home Brewing Refractometers` - `Saddle Bags` - `Track & Field Competitor Numbers` - `Fuel Distributors` - `Nicotine Patches` - `Griddles & Grill Pans` - `Planter & Stand Sets` - `Other Pet Health Supplies` - `Scooter Decks` - `Knitting & Crochet Stitch Counters` - `Camera Tool Kits` - `Eyelash Combs` - `Blouses & Tops` - `Knitting & Crochet Stitch Markers` - `Hand Tool Punches` - `Climbing Rappel Devices` - `Kitchen Towels` - `Homogenizer Accessories` - `Attendance Time Cards` - `Digital Camera Parts` - `Planners & Appointment Book Refills` - `Herbal Supplements` - `Hand-Sewing Needles` - `Weight Bars` - `Photography Fixers` - `Massage Table Carrying Cases` - `Grill Covers` - `Other Skating Supplies` - `Maple Syrups` - `Pizza Stones & Pans` - `Manual Letter Openers` - `Water Guns` - `Manual Can Openers` - `Electronic Touchpads` - `String Trimmer Replacement Parts` - `Baby Bodysuits & One-Pieces` - `Walking Cane & Crutch Tips` - `Cutting Mats` - `Throwing Knives` - `Skins for Tablets` - `Serving Utensils & Sets` - `Photographic Art` - `Soap Making Kits` - `Athletic Shirts & Tops` - `Ticket Rolls` - `Fireplace Bellows` - `Travel Plug Adapters` - `Vacuum Sealers` - `Small Animal Habitat Decor` - `Vacuum Tubes` - `Stroller Connectors` - `Ceiling Fans` - `Other Dancing Supplies` - `Model Train Locomotives` - `Electronic Cigarette Starter Kits` - `Bicycle Baskets` - `Golf Visors` - `Bicycle Storage Hooks` - `Handheld Video Games` - `Cocktail Mixers` - `Follow-Focus Levers` - `Embossing Tools` - `Incentive Charts` - `Pressure Gauges` - `Art Paints & Pigment Powders` - `Replacement Water Filters` - `Moisture Meters` - `Other Pet Grooming Supplies` - `Shopping Cart & High Chair Covers` - `Nursing Pillows` - `Disposable Cutlery Sets` - `Heat Guns` - `Construction Flashing` - `Automotive Aerodynamics Kits` - `Fire Retardants` - `Gun Snakes` - `Snow Removal Rakes & Hand Pushers` - `Costume Wigs` - `Outdoor Decorative Stones` - `Lip Stains` - `Workwear Uniform Badges` - `Gut Health Tests` - `Carbon Monoxide Alarms` - `Cable Covers` - `Lathe Cylindrical Rollers` - `Hamburger Patty Makers` - `Printing Press Accessories` - `Automotive Light Covers & Guards` - `Overbed Tables` - `Artwork Cases & Portfolios` - `Disposable Storage Bags` - `Football Helmets` - `Automatic Transmission Filters` - `Live Trees` - `Umpire Leg Guards` - `Line Conditioners` - `Cup & Saucer Sets` - `Rain Barrels` - `S` - `,` - `s Tapes` - `Indoor Fireplaces` - `Headphone Cases` - `Battery Testers` - `Floor Hockey Carts` - `T-Shirts` - `Dunun Drums` - `Speed Reducers` - `Percussion Mallets` - `Pallet Covers` - `Telescope Cases` - `Automotive Grilles` - `Fencing Sport Epee Parts` - `Emergency Food Kits` - `Sports Corner Flags` - `Bird Nests & Nesting Material` - `Boxing Rings` - `Paper Sorters` - `Lollipop & Treat Sticks` - `Cabin Air Filters` - `Pool Covers` - `Video Game Dance Pads` - `Football Sleds & Chutes` - `Clay & Dough Extruders & Presses` - `Baseball Gloves & Mitts` - `Yeasts & Leaveners` - `Skins for Aerial Drones` - `Replacement Faucet Water Filters` - `Other Electronic Components & Accessories` - `Lab Coats` - `Laser Detectors` - `Baseball Sliding Shorts` - `Hunting Game Finders` - `Rain & Snow Gauges` - `Shapewear Bodysuits` - `Toiletry Kits & Bags` - `Photo Enlarger Heads` - `Athletic Outfit Sets` - `Combustion Analyzers` - `Personal Organizer Refills & Accessories` - `Harmonic Exciters` - `Billiard Table Felt` - `Video Game Console Parts` - `Ham Radio Transceivers` - `Baby Juices` - `Hot Dog Machines` - `Pipe Inspection Cameras` - `Misting Systems` - `Sleeping Bag Liners` - `Kids Gardening Tools` - `Camera Film Winders` - `Other Billiard Supplies` - `Ergonomic Backrests` - `Balloon Pumps` - `Lump Charcoals` - `Pilates Barrels` - `Surfing Leashes` - `Dart Shafts` - `Makeup Blotting Paper` - `Eye Masks` - `Welding Helmets` - `Electronic Coils` - `Energy Drinks` - `Push-Up Stands` - `Photography Stop Baths` - `Handbag Hook Hangers` - `Network Security Devices` - `Stitch Holders` - `Bicycle Pegs` - `Outdoor Daybeds` - `Party Favors` - `Canning Kits` - `Molding & Casting Materials & Kits` - `Musical Instrument Bodies` - `Other Games` - `Electrolyte Solutions` - `Fruit & Wine Crushers & Destemmers` - `Power Inverters` - `Jewelry Jump Rings & Split Rings` - `Table Tennis Paddles` - `SIM Card Backup Devices` - `Tie Pins` - `Billiard Tables` - `Archery Broadheads` - `Vehicle Windshields` - `Jock Straps` - `Pet Live Animals` - `DC-to-DC Converters` - `Transport Drums` - `Vinyl Figures` - `Entertainment Centers` - `Fireplace Tools` - `Serving Forks` - `Nut Splitters` - `Blasters & Foam Play` - `Garden Border Edging` - `Gun Holsters & Holders` - `Spice Racks & Organizers` - `Stamps & Stamp Sets` - `Udu Drums` - `Reception Chairs & Seating` - `Pepper & Peppercorns` - `Golf Drivers` - `CC Cream` - `Yoga Socks` - `Vacuum Cleaner Attachments` - `Planter Liners` - `Pie & Pastry Fillings` - `Bed Skirts` - `Dart Carrying Cases & Wallets` - `Pizza Ovens` - `Crepe Spreaders` - `Hex Tools` - `Stretch Film & Shrink Wrap` - `Massage Headrests` - `Electronics Carrying Cases` - `Dust Collectors` - `Decorative Stones` - `Yoga Straps` - `Espresso Machines` - `Lab Face Masks & Shields` - `Guitar Effects Pedals` - `Hockey Hip Pads` - `Pressure Cookers & Canners` - `Diabetic Foot Care` - `Nail Top Coat` - `Wax Molding Materials` - `Dock Guards` - `Decoupage Tools & Sets` - `Rugby Balls` - `Bra Back Adapters` - `Other Backyard Wildlife Supplies` - `Food Peelers` - `Modular Synthesizers` - `Mirror Balls` - `Cricket Wickets` - `Snatch Rigging Blocks` - `Rowing Shells` - `Staple Gun Staples` - `Golf Rangefinders` - `Waterbed Liners` - `Beer Glasses` - `KVM Switches` - `Panel Carriers` - `Sanding Frames` - `First Aid Tape` - `Hand Pruners & Loppers` - `Reusable Training Pants` - `Outdoor Spas` - `Air Hockey Pucks` - `Cat Trees` - `Surgical Gowns` - `Baseball Protective Screens` - `Artificial Plants` - `Basting Brushes` - `Archery Arrows` - `Calibration Weights` - `Laptop Computers` - `Chalkboard Chalk` - `Nasal Aspirators` - `Instant Coffee` - `Vanity Tables & Table & Bench Sets` - `Printer Drums` - `Automotive Body Paint` - `Paraffin Treatment Hand & Foot Mitts` - `Stovetop Espresso Pots` - `Robotic Lawn Mowers` - `Phone Grips` - `Hammock Chairs` - `Home Brewing Kegs & Keg Supplies` - `Dishwasher Rack Adjusters` - `Sculpting Tools & Kits` - `Mattocks & Pickaxes` - `Watch & Clock Hands` - `Umpire Bags` - `Other Lighting Accessories` - `Lab Shaker Accessories` - `Print Servers` - `Home Brewing Beer Bottle Trees` - `Football Hand Warmers` - `Spectrum Analyzers` - `Hair Perms & Texturizers` - `Lab Glassware Washers` - `Trailer Weight Distributing Systems` - `Digital Cameras` - `Drum & Pail Mixers` - `Porch Swings` - `Gun Choke Tubes` - `Pet Milk Replacers` - `Fabric Paints` - `Other Kiteboarding Accessories` - `Paper Cups` - `Bottle Pourers` - `Golf Divot Tools` - `Aquarium Cleaning Tools` - `Fishing Vest & Packs` - `Die-Cut Cartridges` - `Kiteboard Control Bars` - `Welding Tips` - `Food Tongs` - `Corn & Callus Remover Cushions` - `Cocktail Shakers` - `Inspection Mirrors` - `Fruit Snacks` - `Kayak Storage Racks` - `Cycling Vests` - `Field Hockey Goggles` - `Breathalyzer Alcohol Monitors` - `Dry Erase Markers` - `Racquet Sport Stringing Machines` - `Gun Swivels` - `Power Tool Chargers` - `Home Brewing Sanitization Kits` - `Ice Cream, Sorbet & Frozen Yogurt` - `Underwater Cameras` - `Hair Color Stain Shields` - `Roof Gutter Accessories` - `Bicycle Brake Rotors` - `Repinique Drums` - `Camera Film Backs & Holders` - `Automotive Cigarette Lighters & Plugs` - `Meal Kits` - `Lathe Cole Jaws` - `PBX Phone Systems` - `Face Serums` - `Exercise Treadmills` - `Lawn Sweepers` - `Sunglasses Replacement Lenses` - `Pipe Fitting Reducers` - `Microphone Pop Filters` - `MiniDisc Players` - `Audio/Video Cables` - `Hand Sanitizers` - `Nail Pullers` - `Traditional Salwar Kameez` - `Other Air Tool Accessories` - `Cymbal Pads` - `Automotive Winch Mounting Systems` - `Laptop Bags` - `Contour Makeup Set` - `Puzzle Cubes` - `Plant Covers` - `Empty Paint Cans` - `Tableware Knives` - `Storage Sheds` - `Window & Door Frames` - `Paper Plates` - `Calculator Accessories` - `Shaving Sets` - `Microphone Preamplifiers` - `Baby Feeding Chairs` - `Rain Ponchos` - `Aquarium Thermometers` - `Mini Helmets` - `RV Awnings` - `Lawn & Garden Sprayers` - `Car CD Changers` - `Selfie Sticks` - `Calibration Standard Rods` - `Lab Chromatography TLC Developing Tanks` - `Poultry Treats` - `Mechanical Pencil Refills` - `Dip Stands` - `Drinkware Accessories` - `Artist Manikins` - `Leathercraft Kits` - `Metalworking Spring Plungers` - `Marquee Signs` - `Craft Dyes` - `Wakesurf Boards` - `Pool Safety Supplies` - `Pedal Boats` - `Snack Crackers` - `Jewelry Cleaning Machines` - `Party Supply Sets` - `Other Decking & Fencing Hardware` - `Motorcycle Glasses` - `Art Smocks` - `Archery Nocks` - `Diving Regulators` - `Camera Flash Snoots` - `Martial Arts Weapon Stands` - `Automotive Wiper Nozzles` - `Tea Cozies` - `Stadium Seats & Cushions` - `Bear Protection` - `Flagpole Brackets & Mounts` - `Patio Umbrella Base Weights` - `Stretch Film & Shrink Wrap Dispensers` - `Punching Bag Hangers` - `Track Lighting Sets` - `Wheel Chocks & Stops` - `Itching & Rash Treatments` - `Automotive Rims` - `Tire Pressure Gauges` - `Hair Crimping Irons` - `Baker` - `s Helmets` - `Curtains & Valances` - `Aquarium Starter Kits` - `Body Muds` - `Body Piercing Retainers` - `Hockey Chest Protectors` - `Diving Boards` - `Small Engines` - `Pet Sofa & Chair Protectors` - `Tent Accessories` - `Pedal Covers` - `Appliance Covers` - `Vegetable Dips` - `Hockey Shin Pads` - `Acne Creams` - `Mobility Aid Steps` - `Quilting Frames` - `Water Heaters` - `Sound Pressure Level Meters` - `Glider Rocking Chairs` - `Punching Bags` - `Nail Ridge Filler` - `Yoga Gloves` - `Jewelry Stringing Materials` - `Dishwasher Rack Rollers` - `Other Hair Removal Supplies` - `Agility Ladders` - `Watch Winders` - `Blender Pitchers` - `Hand Tampers` - `Dehumidifier Accessories` - `Punch Bowl Sets` - `Mini Projectors` - `Drinkware Sets` - `Evaporated Milks` - `Spoon Rests` - `Juggling Toys` - `Mixed Spices & Seasonings` - `Bicycle Child Seats` - `Water Coolant Systems` - `Jai Alai Sets` - `Surveillance Camera Lenses` - `Chain Hoists` - `Snack Pudding & Gelatin` - `Hair Curling Wands` - `Spa Cover Lifts` - `Track Lighting Heads` - `Photo Enlargers` - `Photo Enlarger Lenses` - `Plant Cages` - `DJ Mixers` - `Condiment Servers` - `Electric Kettles` - `Glass Raw Materials` - `Radio Antennas` - `Bicycle Front & Rear Racks` - `Body Oils` - `ATV Tires` - `RAM Memory` - `Pet Toothpaste` - `Dressing Aid Sticks` - `Coconut Knives` - `Sports Pole Holders & Bases` - `Dock Steps` - `Bath Toy Storage` - `Martial Arts Belts` - `Currency Bands & Straps` - `Outdoor Playsets` - `Video Switchers` - `Brake System Replacement Parts & Hardware` - `Tennis Shorts` - `Facial Treatments` - `Athletic Compression Socks` - `Brake Booster Filters` - `Other Knitting & Crochet Supplies` - `Martini Glasses` - `Personal Organizers` - `pH Meters` - `Parchment Paper` - `Other Gift Wrapping Supplies` - `Leathercraft Accessories` - `Rod Hockey` - `House Numbers & Letters` - `Toaster Pastries` - `Anti-Chafing Creams` - `Sunshine Recorders` - `Makeup Sets` - `Baby Jumping Exercisers` - `Replacement Appliance Knobs` - `Hair Removal Waxing Strips` - `Toaster Ovens` - `Electric Crepe Makers` - `Kiteboard Control Lines` - `Camera Cleaning Equipment` - `Body Piercing Jewelry` - `Weatherproofing Pipe Coverings` - `Plant Misters` - `Rosin Bags` - `Plastic Cups` - `Baby Bouncer & Rocker Chairs` - `Washing Machines` - `Ground Coffee` - `Gun Mounts` - `Remote Controls` - `Medicine Dosing Containers` - `Chainsaw Chains` - `Climbing Slings` - `Basketball Storage` - `Writing Notebooks & Sketch Books` - `Thermometer Probe Covers` - `Wall Calendars` - `Power Tool Batteries` - `Corded Phones` - `Desk Lamps` - `Network-Attached Storage Servers` - `Slatwall Panels` - `Golf Push & Pull Carts` - `Media Storage Cabinets` - `Stovetop Waffle Irons` - `Pet Tie-Outs` - `Toilet Paper Holders` - `Boating Heads` - `Movie Cameras` - `Vanity Lights` - `Plaques & Signs` - `Decorative Bottles` - `Art Paint Sponges` - `Kayak Carts` - `Radar Detectors` - `Outdoor Bar Stools` - `Piggy Banks & Money Jars` - `Fog Machines` - `Food Storage Jars & Containers` - `Pet ID Tags` - `Salad Serving Utensils` - `Bath Rugs` - `Water Distillers` - `Sewing Bias Tape` - `Racquet Vibration Dampeners` - `Live Shrubs` - `Cable Tie Guns` - `Floating Shelves` - `Empty Camping Stove Fuel Bottles` - `Toy Billiards` - `Water Diverters` - `Powdered Baking Cocoa` - `Card Shufflers` - `Tool Sets` - `Paper Holders & Dispensers` - `Bed-in-a-Bag` - `Rail Planters` - `Glass Markers & Stemware Charms` - `Poison Ingestion Treatments` - `Sewing Baskets` - `Decorative Boxes` - `Play Tents` - `Oke Daiko Drums` - `One-Piece Swimsuits` - `Bicycle Stands` - `Photography Flash Heads` - `Yogurt Makers` - `Field Hockey Goalie Hand Protectors` - `Window Shades` - `Camera Battery Grips` - `Hunting Game Hoists & Gambrels` - `Sunglass Visor Clips` - `Christmas Trees` - `Edge & Corner Guards` - `Golf Carts` - `Screen Houses` - `Tent Poles` - `Fish Cleaning Tables` - `Bicycle Saddle Covers` - `Plant Seeds` - `Geographic Globes` - `Jewelry Making Chain` - `Figurines & Knick-Knacks` - `Binder Insert Strips` - `Hair Clipper Blades` - `Nutrition Drinks` - `Musical Bow Cases` - `Glue Guns` - `Breast Tape` - `Automotive Lift Supports` - `Acoustic Guitars` - `Therapeutic Light Boxes` - `Stovetop Espresso Pot Replacement Gaskets & Filters` - `Beverage Tubs` - `Lifeguard Swimsuits` - `Other Backpacking & Camping Supplies` - `Napkin Rings` - `Staple Removers` - `Gravy Boats, Stands & Sets` - `Bath Pillows` - `Easter Eggs` - `Cut Flowers` - `Traditional Abayas` - `Air Guns` - `Electric Fence Insulators` - `SIM Cards` - `Oven Mitts` - `Gun Barrels` - `Automotive Skid Plates` - `Sport Starter Pistols` - `Personal Misters` - `Telescope Eyepiece & Filter Sets` - `Cell Phone Radiation Guards` - `Snowmobile Riser Blocks` - `Football Target Nets` - `Memory Card Adapters` - `Mobility Walkers` - `Ice Buckets` - `Skins for Vape Pens` - `Electrical Ballasts` - `Bicycle Frames` - `Cheese Wax` - `Whole Bean Coffee` - `Tennis Rebounders` - `Plaster Hawks` - `Dance Shoes` - `Pizza Crusts` - `Other Fireplaces Accessories` - `Epoxy Coatings` - `Automotive Trailers` - `Gift Wrap Paper` - `Material Handling Securing Straps` - `Disposable Cameras` - `Small Animal Exercise Wheels` - `Hall Trees` - `Cash Boxes` - `Sprinkler Valves` - `Renewable Energy Controllers` - `Impact Drivers` - `Home Brewing Grain & Hop Bags` - `ATV Cargo Storage` - `Bicycle Derailleurs` - `Darkroom Squeegees` - `Lens Supports` - `Roller Stands` - `Breath Fresheners` - `Leaf & Plant Cleaner` - `Cricket Balls` - `Automotive Bumpers` - `Other Baby Safety Supplies` - `Confetti Poppers` - `Swimsuit Sets` - `Manual Pencil Sharpeners` - `Toilet Bowls` - `Art & Blueprint Tubes` - `Air Fryer Liners` - `Other Bicycle Parts` - `Gymnastics Hand Grips` - `Garlic Presses` - `GPS Software` - `Banana Hangers` - `Animal Grooming Scissors` - `Dining Tables` - `DAT Recorders` - `Decorative Bowls` - `Dustpans & Dustpan & Brush Sets` - `Racquet Sport Racquets` - `Lap Guitars` - `Construction Siding` - `Baking Cups` - `Hair Extensions` - `Starting Fluid` - `Chemical Solvents` - `Pet Training Pad Trays` - `Fishing Hook Sharpeners` - `Other Construction Heating & Cooling Supplies` - `Air Press Coffee Makers` - `Playground Merry-Go-Rounds` - `Clothes Iron Rests` - `Coffee Drippers` - `Microscope Mechanical Stages` - `Makeup Palettes` - `Fish Poachers & Bakers` - `Power Screwdrivers` - `Other Bowling Supplies` - `Hardware Bolts` - `Gymnastics Rings` - `Other Occupational Health` - `Automotive Glow Plugs` - `Gun Stocks` - `Ice Fishing Spearing Equipment` - `Light Timers` - `Plant Pots & Planters` - `Camera Rain Covers` - `Table Place Cards` - `Door Closers` - `Commercial Refrigerators` - `Home Brewing Beer Bottle Caps` - `Badge & ID Holders` - `Interdental Brushes` - `Facial Self-Tanners` - `Archery Finger Tabs` - `Hand Drills` - `Cooking Sauces & Marinades` - `Conductor` --- ## Walmart Category Metadata Field Mapping Use this table to understand what metadata fields are allowed for each `type` when creating items via Unified. Please note that these metadata fields are optional, while some categories do need specific metadata fields [file](https://s3.us-east-2.amazonaws.com/unified-article-images/creating_walmart_items_via_unified-file-0-walmart_schema_fields.md) --- ### Sample Payload ```json { "name": "Wireless Earphones with Advanced Features", "slug": "09876543210987", "global_code": "06146190200012", "type": "Exterior Automotive Accessories", "description": "Totally wireless earphones are built to revolutionize your workouts. The adjustable, secure-fit earhooks are customizable for extended comfort and stability. A reinforced design for sweat and water resistance lets you take it to the next level. Each earbud has full volume and track controls and up to 9 hours of listening time to fuel your training with powerful, balanced sound.", "media": [ { "url": "https://i5.walmartimages.com/seo/onn-Wireless-Bluetooth-on-Ear-Headphones-Black-New_07de2d93-505b-4902-8366-388a3f30e3b4.9375c5690645733ec080eef3f4895f7b.jpeg" } ], "vendor_name": "Walmart", "variants": [ { "sku": "09876543210987", "weight": 0.57, "prices": [ { "price": 249.99 } ] } ], "metadata": { "productName": "Wireless Earphones with Advanced Features", "brand": "Walmart", "keyFeatures": [ "Designed to keep up with your workouts", "Reinforced design for sweat and water resistance with adjustable, secure-fit earhooks for added comfort and stability.", "Stay connected from a distance with Bluetooth technology, these wireless earphones deliver extended range and fewer dropouts.", "Powers through long hours of training" ], "countPerPack": 1, "multipackQuantity": 1, "isProp65WarningRequired": "No", "condition": "New", "has_written_warranty": "Yes - Warranty Text", "netContent": { "productNetContentMeasure": 1, "productNetContentUnit": "Each" }, "productSecondaryImageURL": [ "https://i5.walmartimages.com/seo/onn-Wireless-Bluetooth-on-Ear-Headphones-Black-New_07de2d93-505b-4902-8366-388a3f30e3b4.9375c5690645733ec080eef3f4895f7b.jpeg" ], "assembledProductLength": { "measure": 4.33, "unit": "in" }, "assembledProductHeight": { "measure": 4.33, "unit": "in" }, "assembledProductWeight": { "measure": 0.57, "unit": "lb" }, "assembledProductWidth": { "measure": 3.15, "unit": "in" }, "prop65WarningText": "No", "manufacturer": "Walmart", "manufacturerPartNumber": "WM123456", "count": 1, "warrantyText": "1 Year", "color": "Blue", "aaiaBrandID": "WMTB", "vehicle_fitment_type": "Specific" }, "options": {} } ``` **NOTE**: If item creation **fails** or takes **too long** to process, the **`feed ID`** and any **`error details`** will be returned in the error response. ⚠️ **Important Note on Reading** **`commerce_item`** **Data via Walmart's GET and LIST APIs** When using Walmart's **GET** or **LIST** endpoints to retrieve `commerce_item` records (i.e., your catalog items), **you will only receive a subset of the full item**. This limitation is imposed by Walmart's public Marketplace APIs and is not configurable. ### What's Missing? - Fields defined in the **`Visible`** section of your original item feed (e.g., `animalLifestage`, `assembledProductWeight`, `features`, `fabricContent`, etc.) will **not be returned** in full. - Walmart truncates or omits many **category-specific structured attributes**. ### Why This Happens: Walmart's GET/LIST endpoints are designed for high-level overviews or simple listing needs, not for reconstructing detailed item payloads. They surface: - Basic identifiers (e.g., SKU, productName) - Core logistics info (e.g., price, inventory, brand) - A _limited set_ of visible attributes ## End-Users, Integrations, and Connections URL: https://docs.unified.to/guides/end_users_integrations_and_connections # End-Users, Integrations, and Connections ------ _April 18, 2024_ This guide explains how connections work at [Unified.to](https://unified.to/) and shows you different ways to create connections for your application. You'll learn about the relationship between your end-users, connections, and integrations, as well as how to get started quickly with test connections. ## Overview At Unified.to, there are three key concepts to understand: 1. **Integrations** - These are the bridges between your application and third-party platforms (a.k.a SaaS apps). These are pre-built for you! 2. **Connections** - These are the authorized links between your end-users and specific integrations 3. **End-users** - These are your application's users whose data you will access from their accounts i.e. HubSpots, Salesforce, Shopify, and so on. ![Users_connections_and_integrations_%2810%29.png](https://s3.us-east-2.amazonaws.com/unified-article-images/end_users_integrations_and_connections-0.png) ## Understanding integrations Your application communicates with a SaaS application through an integration. ### Activating an integration in the sandbox environment 1. Navigate to **Integrations** at [app.unified.to/integrations](https://app.unified.to/integrations) 2. Search for the integration you want to use 3. Click on the integration card to view its details 1. For sandbox environment integrations, you don't need to enter any real credentials here. 4. Click **Activate** All data is synthetic in the sandbox environment, meaning you don't need to sign up for any third-party accounts while you're testing the Unified.to platform. [Learn more about the sandbox environment →](https://docs.unified.to/concepts/sandbox) ### Activating an integration in production environments To activate an integration in production environments, you'll need developer credentials from the platform you're integrating with. ### Developer credentials When activating an integration, you'll be asked to provide credentials like API keys or OAuth 2.0 client IDs and secrets. These credentials should come from **your developer account** with the platform, not your end-users' accounts. Here's how it works: 1. You register your application with the platform (e.g., create a HubSpot developer account and register your app) 2. You receive developer credentials that identify your application 3. You enter these credentials when activating the integration on Unified.to 4. Your end-users will later authorize your application to access their accounts, with or without using their own credentials (depends on the type of auth flow) For example, if you're building an interview scheduling platform that needs to access your clients' Zoho CRM data: - You use **your** Zoho developer account credentials to activate the integration - Your **end-users** will authorize access to their Zoho CRM accounts when creating connections by consenting to give you access to their data ## Understanding connections Your end-users are connected to integrations through connections_._ A connection is an authorized link between one of your end-users and a specific integration. For example, if your application needs to access a user's HubSpot account, you'll need: 1. The HubSpot integration to be activated in your workspace 2. A connection between that specific user and HubSpot 3. The correct scope configuration for what data you need to access Each user needs their own connection to each integration you want to use. This ensures proper authorization and data isolation between users. ### About scopes Scopes determine what data your application can access in your end-users' accounts. You need to: 1. Configure the correct scopes in your developer account with the platform 2. Select the corresponding Unified.to scopes when setting up your integration For example, if you need to read CRM contacts, you would request the `crm_contact_read` scope. Unified.to has its own set of scopes (called Unified scopes) which have been mapped to the corresponding scopes for every single integration that needs them. When requesting scopes, you only need to pass in Unified scopes in addition to enabling those scopes in your developer account. [Learn more about configuring scopes →](https://docs.unified.to/concepts/scopes) ## How to create connections There are three main ways to create connections in Unified.to, each suited for different scenarios: ### 1. Development and testing: Create test connections in the sandbox environment This is the fastest way to get started and test integrations: 1. Switch to the **Sandbox** environment in the Unified.to app 2. Go to the **Connections** page 3. Click **Create test connection** 4. Select any activated integration you want to test Test connections are created instantly with mock credentials, perfect for development and testing. ### 2. Production ready: Use the Authorization embedded component This is the recommended approach for production-ready applications: 1. Navigate to the [**Embedded Authorization**](https://app.unified.to/embed) page 2. Configure your callback settings, permission scopes, and display options 3. Copy the provided code snippet for your preferred tool (React, Angular, Vue, vanilla JavaScript, or an API call) 4. Add the code to your application 5. The Authorization component will be rendered on your app. When users click an integration's logo, they'll be guided through the authorization process Example code for React: ```javascript import UnifiedDirectory from '@unified-api/react-directory'; function App() { return ( ); } ``` ### 3. Custom implementation: Build your own authorization flow For greater control over the user experience: 1. Fetch available integrations using the Unified API 2. Create authorization URLs for selected integrations 3. Handle authorization callbacks 4. Manage connection IDs This approach requires slightly more implementation work but offers more flexibility. You can find a [complete tutorial for this approach in our documentation](https://docs.unified.to/tutorials/customize-auth-flow). ## Best practices - **Always test in Sandbox first**: Use test connections to validate your integration logic - **Store connection IDs securely**: Never store connection IDs in client-side storage in production - **Handle authorization failures gracefully**: Provide clear feedback when connection attempts fail. Users will be redirected to the failure URL you specified during configuration. [Learn more about handling failure callbacks here](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_connections#handle-errors-during-connection-creation). - **Monitor connection health**: Set up [webhook notifications](https://app.unified.to/settings/workspace) for connection status changes - **Configure scopes correctly**: Request only the permissions your application needs - **Use separate environments**: Keep your development and production environments separate ## Troubleshooting Common issues and solutions: ### Connection creation fails - Verify your developer credentials are correct - Check that required scopes are configured both in your platform developer account and Unified.to - Ensure redirect URLs are properly set up in your platform developer settings ### Authorization flow issues - Confirm your integration is activated - Verify environment settings (Sandbox vs Production) - Check that scopes match between your platform developer account and Unified.to For more guidance, see: [How to troubleshoot unhealthy connections](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_connections) ## Next steps - Try creating your first test connection in the sandbox environment - Explore our [Quickstart guide](https://docs.unified.to/quick-start) for a hands-on example - Read our detailed guide on [understanding and configuring scopes](https://docs.unified.to/concepts/scopes) ## Enriching Unified Objects with Custom Metadata URL: https://docs.unified.to/guides/enriching_unified_objects_with_custom_metadata # Enriching Unified Objects with Custom Metadata ------ _July 23, 2025_ ## Overview The metadata_metadata object is a powerful addition to the Unified data model. It enables the definition of **metadata schemas (custom fields)** that can be attached to various unified objects—such as `commerce_item`, `ats_job`, `hris_employee`, and more. This document explains its structure, how to use it effectively, and best practices for managing dynamic metadata across models. --- ## Purpose `metadata_metadata` serves as a **schema registry** for custom fields, allowing users to enrich objects with reusable, structured metadata. These definitions dictate the format and structure of metadata values that live inside the `metadata` field of supported Unified objects. --- ## Object Structure ```typescript interface IMetadataMetadata { id?: string; created_at?: (string | Date | number); updated_at?: (string | Date | number); name: string; // Required slug?: string; format?: string; // One of the supported formats original_format?: string; options?: string[]; object_type: string; // e.g. 'commerce_item', 'ats_job' objects?: { [objectType: string]: string[] }; // Map of object_type to object IDs } ``` --- ## Key Concepts ### `object_type` This specifies the **unified model** the metadata is associated with. Examples include: - `"commerce_item"` - `"ats_job"` - `"hris_employee"` - Other Unified Objects > When listing metadata_metadata entries, you can filter by **type**, which corresponds to the object_type. --- ### `format` Defines the data type for the metadata field. Supported formats include: - `'TEXT'` - `'NUMBER'` - `'DATE'` - `'BOOLEAN'` - `'FILE'` - `'TEXTAREA'` - `'SINGLE_SELECT'` - `'MULTIPLE_SELECT'` - `'MEASUREMENT'` - `'PRICE'` - `'YES_NO'` - `'CURRENCY'` - `'URL'` --- ### `objects` A mapping of object types to the specific object IDs where this metadata is currently applied. **Example:** ```json { "objects": { "commerce_item": ["item1", "item2"], "ats_job": ["job1"] } } ``` This makes it easy to track which unified records are using each metadata definition. --- ## Usage in Unified Objects Each unified object includes a `metadata` array, where each entry corresponds to a metadata definition from the `metadata_metadata` registry. ### Example: `IAtsJob` ```javascript interface IAtsMetadata { id?: string; slug?: string; value?: unknown; namespace?: string; format?: string; extra_data?: unknown; } interface IAtsJob { ... metadata?: IAtsMetadata[]; ... } ``` --- ## Workflow Example ### Step 1: Create a `metadata_metadata` Definition To begin, define the metadata schema by providing **only** the following required fields: - `name`: Human-friendly label - `slug`: Unique programmatic identifier (Picked up from name if not given) - `format`: Expected data format (e.g., `"TEXT"`, `"NUMBER"`, etc.) ```json POST /metadata_metadata { "name": "Binding Mount", "slug": "binding_mount", "format": "TEXT" } ``` --- ### Step 2: Review the Created Metadata Once the metadata is created, the full record might look like this (after automatic enrichment): ```json GET /metadata_metadata/1 { "id": "1", "name": "Binding Mount", "slug": "binding_mount", "format": "TEXT", "original_format": "single_line_text_field" } ``` --- ### Step 3: Attach Metadata to a Unified Object ```json PATCH /commerce_item/7 { "metadata": [ { "id": "1", "slug": "binding_mount", "value": "Optimistic" } ] } ``` Make sure the id / slug match the schema created in Step 1. ### Step 4: Review the Metadata Again Once the metadata is used in a Unified object, the full record might be updated to look like this: ```json GET /metadata_metadata/1 { "id": "1", "name": "Binding Mount", "slug": "binding_mount", "format": "TEXT", "original_format": "single_line_text_field", "object_type": "commerce_item", "objects": { "commerce_item": ["7"] } } ``` This shows the metadata definition has been associated with the unified object type `commerce_item` and is linked to object with ID `7`. --- ## CRUDL Support Full **Create, Read, Update, Delete, and List** operations are supported on `metadata_metadata`. This enables: - Schema-driven form builders - Reusable definitions across multiple objects - Consistent data validation and audit trails --- ## Use Cases | Use Case | Example | | --------------------------------- | -------------------------------------- | | Custom field on ATS Job | `"interview_style": "technical panel"` | | Enrich commerce item with specs | `"binding_mount": "Optimistic"` | | Add legal identifiers to employee | `"national_id": "IN123456"` | ## Extended observability — pushing API logs to Grafana / Loki URL: https://docs.unified.to/guides/extended_observability_pushing_api_logs_to_grafana_loki # Extended observability — pushing API logs to Grafana / Loki ------ _April 27, 2023_ We're excited to add another option for external observability: you can send **API call logs** from [Unified.to](https://unified.to/) directly to a **Grafana Loki**–compatible endpoint. directly to a **Grafana Loki**–compatible endpoint. This is the same class of logs we already support for [Datadog](https://docs.unified.to/guides/extended_observability_pushing_api_logs_to_your_datadog_instance); you choose where they go in your [workspace settings](https://app.unified.to/settings/workspace). [**Grafana Cloud**](https://grafana.com/products/cloud/) and [**Grafana Loki**](https://grafana.com/oss/loki/) are commonly used for log aggregation and querying. Unified delivers logs using Loki's standard HTTP push API. Setting it up is easy! Here's how: - **Prepare your endpoint and credentials** - In [**Grafana Cloud**](https://grafana.com/docs/grafana-cloud/), open your stack and note the **logs / Loki** ingest base URL (no path) — often `https://logs-prod-XX.grafana.net` (your region may differ). If you use **self-hosted Loki**, use that base URL instead. - Create or copy a **logs token** (or the username + token pair your provider documents for Basic auth). **Set** **`grafana_site`** **explicitly** for EU/regional Grafana Cloud stacks or self-hosted Loki; if you leave it blank, our API falls back to a default US Grafana Cloud logs host, which may not match your region. - **Enter values in workspace settings** - Open [Workspace settings](https://app.unified.to/settings/workspace) (or use the Workspace API) and choose **Grafana** under external logging. Configure: - **Loki base URL** (`grafana_site`): Base URL only (for example [https://logs-prod-XX.grafana.net](https://logs-prod-xx.grafana.net/)) - **Authentication: Basic** (Grafana Cloud: user/instance id + token), **Bearer** (if your gateway expects a bearer token), or **Legacy** (single field — see below). - **Token / API key** (`grafana_apikey`): Your secret or legacy `user:token` string, depending on mode. - **Confirm logs are flowing** - Make a sample call to the Unified API (for example, list an object on a connection). In **Grafana Explore** (or any Loki client), run LogQL such as `{job="unified-api"}`. Log lines include JSON with the route **`path`**, `integration_type`, `status`, and other API-call fields. ## Why does Grafana / Loki matter for [Unified.to](https://unified.to/) users? By sending your logs directly to your own Loki stack or Grafana Cloud, you get immediate visibility into what's happening in your environment. This allows you to quickly debug issues, get notified about errors, and more. Currently, we support both inbound API calls and webhook calls. However, we're constantly improving our product, and we're looking forward to supporting more log types in the future. So, please [send us feedback](https://unified.to/contact) so we can better understand what you need. ## About Unified.to If you're not familiar with Unified API, it's a single API that you can use to read and write data to third-party integrations using our unified data model. This makes it easy to integrate with multiple systems and eliminates the need for separate API calls for each integration. We hope this new feature makes your life easier and your work more efficient! [Get started with Unified.to](https://app.unified.to/) ## Extended Observability - Pushing API Logs to your Datadog Instance URL: https://docs.unified.to/guides/extended_observability_pushing_api_logs_to_your_datadog_instance # Extended Observability - Pushing API Logs to your Datadog Instance ------ _April 27, 2023_ We're excited to announce the latest addition to increase observability for our customers — the ability to send API call logs within [Unified.to](https://unified.to/) directly to your Datadog instance. [Datadog](https://www.datadoghq.com/) is a cloud-based monitoring and analytics platform that provides real-time insights into the performance of IT infrastructure, applications, and logs. Setting it up is easy! Here's how: 1. Get your Datadog API key: To start sending logs to Datadog, you need to obtain your API key. Here's a [detailed article](https://docs.datadoghq.com/account_management/api-app-keys/) that walks you through the process. 2. Once you have your API key, simply paste it into your Unified.to [workspace settings](https://app.unified.to/settings/workspace). 3. Finally, make a sample API call to Unified API to confirm that logs are being sent to your Datadog instance. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/extended_observability_pushing_api_logs_to_your_datadog_instance-0.png) ## Why does Datadog matter for Unified.to users? By sending your logs directly to Datadog, you get immediate visibility into what's happening in your environment. This allows you to quickly debug issues, get notified about errors, and more. Currently, we support both inbound API calls and webhook calls. However, we're constantly improving our product, and we're looking forward to supporting more log types in the future. So, please [send us feedback](https://unified.to/contact) so we can better understand what you need. ## About Unified.to If you're not familiar with Unified API, it's a single API that you can use to read and write data to third-party integrations using our unified data model. This makes it easy to integrate with multiple systems and eliminates the need for separate API calls for each integration. We hope this new feature makes your life easier and your work more efficient! [Get started with Unified.to](https://app.unified.to/) ## Fireflies Integration Guide URL: https://docs.unified.to/guides/fireflies_integration_guide # Fireflies Integration Guide ------ _November 5, 2025_ # Fireflies Integration Guide Learn how to connect an end-customer's [**Fireflies.ai**](https://fireflies.ai/) account to your application using [**Unified.to**](https://unified.to/). This guide covers how to enable the integration, embed the authorization component, and verify the connection end-to-end. ## Overview The Fireflies integration allows your app's end users to authorize [Unified.to](https://unified.to/) to access their Fireflies accounts for meeting recordings and user data. Once connected, your application can access the Fireflies API securely through [Unified.to](https://unified.to/) — without your users ever leaving your app. ## Prerequisites Before getting started, ensure you have: - A [**Unified.to**](https://unified.to/) **developer account** and active API credentials - Your end-customer has a [**Fireflies.ai**](https://fireflies.ai/) **paid plan** (API access required) - Access to your app's frontend or backend to embed the Unified.to authorization component so that you can get your end-customer to authorize access to Fireflies - Optional: API Explorer or MCP Server for testing connections Please note that the Fireflies API is only available to users on a **paid Fireflies plan**. Free-tier accounts cannot authorize API access. ## Step-by-Step Setup ### 1. Enable the Fireflies Integration in [Unified.to](https://unified.to/) 1. Log in to your [Unified.to](https://unified.to/) developer dashboard. 2. Navigate to **Integrations**. 3. Locate **Fireflies** in the catalog. 4. Click **Activate Integration**. Once enabled, Fireflies becomes available for your end-customer to authorize within your own app. --- ### 2. Embed the Authorization Component Use the **Embedded Authorization (Authorize Optimization)** component from [Unified.to](https://unified.to/) to create a seamless in-app experience. 1. Add the component to your application UI where users connect integrations. 2. Preview the embedded experience - this is what your customers see when connecting Fireflies. 3. The component displays a button (e.g., 'Authorize Fireflies') and short instructions. --- ### 3. Obtain the Fireflies API Key Your end-customers/users will need their **Fireflies API key** to complete authorization. **Steps for your end user:** 1. Log in to their [**Fireflies.ai**](https://fireflies.ai/) account. 2. Go to **Settings → Integrations**. 3. Locate **API Key** and click **Copy**. 4. Return to your app's authorization screen and paste it into the field provided. --- ### 4. Authorize Access 1. In the authorization window, paste the Fireflies API key. 2. Click **Authorize**. 3. [Unified.to](https://unified.to/) will automatically: - Validate the API key - Connect to Fireflies - Run a **test connection** If successful, [Unified.to](https://unified.to/) creates a new **Connection Object** linked to that customer. --- ### 5. Verify the Connection Once the test passes, a new connection record appears in your [Unified.to](https://unified.to/) dashboard. | Field | Description | | ----------------- | ------------------------------------ | | **Connection ID** | Unique identifier for API requests | | **Integration** | [Fireflies.ai](https://fireflies.ai/) | | **Status** | Connected | | **Created At** | Timestamp of creation | Use the **Connection ID** to make authenticated API calls to Fireflies through [Unified.to](https://unified.to/). --- ### 6. Test the Connection (Optional) You can test the integration in two environments: a) Using [Unified.to](https://unified.to/) API Explorer - Open **API Explorer** in your dashboard. - Use your new Connection ID to query endpoints such as: - `/hris/{id}/employee` – list Fireflies users - `/calendar/{id}/recording` – retrieve meeting data b) Using MCP Server (for AI Agents) If your use case involves AI agents, test the connection within your **MCP Server** using the same Connection ID. --- ## Example Use Cases - Retrieve meeting recordings for transcription or analysis - Sync Fireflies user data into your CRM or analytics system - Feed Fireflies meeting data into AI summarization or tagging workflows --- ## Troubleshooting | Issue | Description | Solution | | -------------------------- | -------------------------------- | ------------------------------------------------------- | | **Authorization failed** | Invalid or expired API key | Re-copy the key from Fireflies and retry authorization. | | **Test connection failed** | API access not enabled | Ensure user is on a paid Fireflies plan. | | **No data returned** | Key valid but no accessible data | Check that the Fireflies workspace contains recordings. | *Fireflies must have existing data (e.g., recorded meetings) to return results when testing API endpoints. --- ## Next Steps - Explore the [Unified.to API Reference](https://www.notion.so/api-reference) to interact with `/hris/{id}/employee` and `/calendar/{id}/recording`. - Monitor performance and connection status under **Integration Health** in your dashboard. - Repeat these steps for other integrations or customer environments. --- ## Related Articles - [Embedding the Authorization Component](https://www.notion.so/guides/embedded-authorization) - [Testing Connections via API Explorer](https://www.notion.so/guides/testing-connections) - [Managing Customer Connections](https://www.notion.so/guides/manage-connections) --- _Last updated: November 2025_ ## Getting started with Workable URL: https://docs.unified.to/guides/getting_started_with_workable # Getting started with Workable ------ _January 11, 2024_ Workable is one of 190+ integrations that you no longer have to build from scratch thanks to our unified API developer platform. In this guide, you will learn about Unified.to's [Workable integration](https://unified.to/integrations/workable), including how to activate Workable, implementation steps, example use cases, and how your users can authorize the integration in your app. At the end of this guide, you will have everything you need to activate a Workable integration and begin creating user connections in production. ## Prerequisites To activate a Workable integration, you will need a Unified.to account. [Sign up for a free tester account](https://app.unified.to/login) and follow the onboarding steps. Workable and all our pre-built integrations are available on every [Unified.to plan](https://unified.to/pricing). This article assumes you are familiar with [Workable](https://www.workable.com/) and product integration concepts. You do not have to be an expert in integration development or our platform. ## Activating Workable Once you've created your Unified.to account and completed onboarding, you can activate Workable and any other integrations you'd like to add to your app. Activation refers to the process of enabling and configuring an integration. Sign in to Unified.to, and select **Integrations > Active Integrations**. Use the search bar or click the **ATS** tab to find Workable and other integrations within the same category. You can also locate Workable in the **HR** tab if you are interested in HR data. ![Search_Workable_in_Unified.to.png](https://s3.us-east-2.amazonaws.com/unified-article-images/getting_started_with_workable-0.png) **Authorization** Select **Workable** to begin the activation process. On the authorization page, you can specify the integration categories (ATS or HR) and the authorization method (API key or OAuth 2). If you'd like your Workable integration to support OAuth 2, you will need to enter your OAuth 2 Client ID. ![Workable_authorization.png](https://s3.us-east-2.amazonaws.com/unified-article-images/getting_started_with_workable-1.png) **OAuth 2** If you want your Workable integration to support OAuth 2, select the **OAuth 2** tab to view where to set your OAuth2 redirectUri to and the unified scopes to enable in your application. You may need all or some depending on your use case. ![Workable_OAuth_2_Credentials.png](https://s3.us-east-2.amazonaws.com/unified-article-images/getting_started_with_workable-2.png) If you don't yet have your Workable OAUth 2 credentials, fill out [this form](https://www.workable.com/partnership-program/apply?utm_source=unified.to&utm_medium=unified.to%20blog&utm_campaign=unifedto_partner) to start a Workable partnership and generate your credentials. Return to the **Authorization** tab, paste your credentials, and select **Activate**. If you need support with your Workable partnership, please email hello@unified.to or reach out on [Discord](https://discord.gg/85z7HF7JbD). **Feature support** Select the **Feature Support** tab to view a detailed breakdown of the objects and fields our Workable integration currently supports. Green represents fields we support, grey represents fields Workable does not support, and the fields in red are the features we don't support yet. ![Workable_feature_support_preview.png](https://s3.us-east-2.amazonaws.com/unified-article-images/getting_started_with_workable-3.png) We're always open to feedback. If you have questions or ideas, reach out on [Discord](https://discord.gg/85z7HF7JbD). To see full feature support for Workable, [sign in or sign up for Unified.to for free](https://app.unified.to/login). ## Your workspace When you create a Unified.to account, we automatically create a workspace for you. This will be true for any team members in your company that individually sign up for Unified.to. You can create multiple workspaces for different environments (QA, staging, production, etc.) with unique access and configuration rules. You will need to know your workspace ID when using our embedded authorization widget. You can find your workspace ID under **Settings > API Information**. ## Setting up authorization To add Workable and other integrations to your app, navigate to **Settings > Embed** to set up user authorization. Configure your authorization settings and copy the line of code to insert the embedded authorization widget into your app along with your workspace ID. If you do not wish to use our authorization widget, you can call our API directly to get a list of integrations to display in your app and build your own front-end. ![Embedded_authorization.png](https://s3.us-east-2.amazonaws.com/unified-article-images/getting_started_with_workable-4.png) You can also interact with the authorization preview to simulate your end user's Workable authorization experience. You'll see the same authorization page that your end user will experience when they enable an integration in your app. You can run further authorization tests by adding a [test user connection](https://unified.to/blog/start_here_how_to_add_a_test_connection) or by using our [sandbox environment](https://unified.to/blog/how_to_test_unified_apis_using_sandbox_connections). ## Creating user connections Once a customer authorizes a Workable integration from your app, they are redirected to the success URL that you defined earlier when setting up authorization. That URL will have an id={connection_id} appended to the URL. Store that connection_id in your database and associate it with your customer's account. You can also send in a state query parameter with your customer's ID. **Important:** Make sure you associate your app's user ID/account ID with the newly created Unified.to connection ID. We recommend using the state parameter in our embedded authorization widget or authorization endpoint. That state will be sent back to your success_url along with the connection_id. Store our connection_id in your database and associate it with your user. ## Perform actions with your user connections Here's the exciting part! Once your user authorizes your Workable integration, you can start accessing our API with your user connections. User connections represent a specific authentication of an integration, which means you can now: - Access your customers' third-party data in Workable - Build new features and support more users - Leverage Workable data to support use cases like [candidate sourcing](https://unified.to/blog/how_to_build_a_candidate_sourcing_or_job_board_app_with_unified) and [candidate assessments](https://unified.to/blog/how_to_build_a_candidate_assessment_product_with_unified) If you'd like to simulate performing actions with user connections, you can create user connections in our Sandbox environment. Pass 'X-Mock: 1' in a header in your API request to receive mock data from our API. [See documentation](https://docs.unified.to/overview/restapi/mock). **Note:** We have SDKs available for Node/Typescript, Python, PHP, Java, Go, C#, and Ruby. [Learn more](https://docs.unified.to/overview/sdks). Have a question for us? Complete [this form](https://unified.to/contact) or chat with us directly on [Discord](https://discord.gg/85z7HF7JbD). ## Handling Delegated vs. Application Scopes in Microsoft Integrations URL: https://docs.unified.to/guides/handling_delegated_vs_application_scopes_in_microsoft_integrations # Handling Delegated vs. Application Scopes in Microsoft Integrations ------ _December 9, 2025_ Microsoft Graph offers two fundamentally different permission models — **Delegated** and **Application** scopes. Understanding how these scopes behave is important when connecting Microsoft integrations through Unified. Some Microsoft APIs only support delegated permissions, while others require strictly application-level permissions. Mixing them in a single OAuth flow will cause failures. In Unified, you can create separate Microsoft connections so you can cleanly target the scopes required for the endpoints you plan to use. This guide explains the difference between the permission types, why certain Unified endpoints require one or the other, and how to configure your Unified connections correctly. ## **1. Delegated vs. Application Permissions** ### **Delegated Permissions** Delegated permissions are used when a **signed-in Microsoft user** is present in the OAuth flow. Use delegated permissions when: - The API needs to act **on behalf of a user** - You want to access data the user normally has permission to view - The Microsoft Graph endpoint explicitly supports delegated scopes ### **Application Permissions** Application permissions are used when **no user is logged in**, and the app accesses Microsoft Graph directly as itself. Use application permissions when: - A Microsoft endpoint **does not support delegated scopes** - The API involves system-level or tenant-wide data - The OAuth token must come from the **client_credentials** flow Examples of Unified endpoints that only support application scopes: - **`uc_call`** (MS Teams call records API) - Microsoft Graph requires `CallRecords.Read.All` as an **application permission**, not delegated. --- ## **2. Why You Cannot Mix Delegated and Application Scopes** Microsoft Graph enforces strict separation between permission types: - Delegated scopes must be requested during a **user login** OAuth flow - Application scopes must be requested using **client credentials** - A single OAuth authorization cannot request both delegated and application permissions If both are selected together: - Microsoft returns `AADSTS650053` or similar errors - The OAuth token cannot be issued - Your Unified connection will fail to authenticate Because of this, selecting both scope types for the same Unified connection will not work. ## HiBob & Unified: connection and time off guide URL: https://docs.unified.to/guides/hibob_and_unified_connection_and_time_off_guide # HiBob & Unified: connection and time off guide ------ _April 22, 2026_ This guide explains how to connect **HiBob** to **Unified** for HRIS use cases, which credentials to use, and how **time off**works in our implementation, including: - `hris_employee` time off fields - the `hris_timeoff` object **Official HiBob API reference:** [https://apidocs.hibob.com/docs](https://apidocs.hibob.com/docs) --- ## Creating credentials in HiBob 1. In HiBob, go to **Settings → Integrations → Automation → Service Users** 2. Create or select a **service user** intended for API access 3. Copy the **Service User ID** 4. Generate or copy the **Service User Token** 5. Paste: - the **ID** into the first credential field in Unified - the **Token** into the second credential field in Unified **Note:** Store the token securely and rotate it if exposed. --- ## hris_timeoff – Required Permissions Before accessing time off data, ensure the **service user has the correct permissions**. ### Visibility Rules Time off policies can be: - Public - Public with custom name - Private - Private requests are only visible if permission is granted - Responses include a `visibility` field: - `Public` - `Private` - `Custom name` ### Required Permissions ### Get Requests - **People's Data → Time off → See who's out today → See who's out** ### Private Requests - **People's Data → Time off → See who's out today → See who's out because of a private policy or policies with a custom name** - Some endpoints require: - `includePrivate: true` ### Pending Requests - **People's Data → Time off → Requests → Create, edit all fields, and cancel people's requests that haven't been approved yet** - Some endpoints require: - `includePending: true` ### Get Request by ID - **People's Data → Time off → Requests → Create, edit, and cancel people's requests that haven't been approved yet** ### Request Attachments - **People's Data → Time off → Requests → Upload, edit and view attachments in people's requests** ### Policies & Policy Types - **Features → Time off → Settings → Manage company's time off settings** ### Create Request Requires BOTH: - **Features → Time off → Settings → Manage company's time off settings** - **People's Data → Time off → Requests → Create, edit, and cancel people's requests that haven't been approved yet** ### Time Off Balance - **People's Data → Time off → Balance → See selected people's time off and sick leave balances** - **People's Data → Time off → Balance → Adjust selected people's time off balances and bank their overtime** --- ## hris_employee – Required Permissions To access time off balances and policies: ### Time Off Balance - **People's Data → Time off → Balance → See selected people's time off and sick leave balances** - **People's Data → Time off → Balance → Adjust selected people's time off balances and bank their overtime** ### Policies & Policy Types - **Features → Time off → Settings → Manage company's time off settings** ## How long are logs retained URL: https://docs.unified.to/guides/how_long_are_logs_retained # How long are logs retained ------ _April 2, 2024_ **API call logs are retained for 60 days.** Our system removes log entries for API calls that are older than 60 days. To retain a record of all of your API calls, we suggest that you make a backup copy of your API call log on a regular basis. You can do this by using our [Unified.to Admin API](https://docs.unified.to/unified/apicall/Returns_API_Calls). For example, if you use our Node SDK, you could perform a backup using the `listUnifiedApicalls()` method: ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); const results = await sdk.unified.listUnifiedApicalls({ }); ``` ## How to access employees and users URL: https://docs.unified.to/guides/how_to_access_employees_and_users # How to access employees and users ------ _June 25, 2024_ How do you get information on a customer account's users and employees? The essential idea is that regardless of whether the integration involves an e-commerce, CRM, or accounting application, user information for a corporate account is consistently accessible through our unified HR API. This is achieved using the HR [Get Employee](https://docs.unified.to/hris/employee/Retrieve_an_employee) and [List Employees](https://docs.unified.to/hris/employee/List_all_employees) API endpoints. ```javascript GET /hris/{connection_id}/employee GET /hris/{connection_id}/employee/{id} ``` There are approximately [120 ](https://docs.unified.to/hris/integrations)[integrations](https://docs.unified.to/hris/integrations)[ ](https://docs.unified.to/hris/integrations)that support the HR Employee endpoints. ### Employee/User IDs in all unified data models Here is a list of different objects across all our unified API categories that link back to an HR Employee via their IDs. Note: We use the terms 'employee' and 'user' interchangeably. | Category | Data Model | Field | Description | | ----------- | ---------------------------------------------------------- | --------------------------- | --------------------------------------------------------------- | | ATS | [Activity](https://docs.unified.to/ats/activity/model) | user_ids | The recruiters associated with this activity | | ATS | [Candidate](https://docs.unified.to/ats/candidate/model) | user_id | The recruiter or hiring manager associated with this candidate | | ATS | [Document](https://docs.unified.to/ats/document/model) | user_id | The user who created this document | | ATS | [Interview](https://docs.unified.to/ats/interview/model) | user_ids | The employees that were in this interview | | ATS | [Job](https://docs.unified.to/ats/job/model) | recruiter_ids | | | ATS | [Job](https://docs.unified.to/ats/job/model) | hiring_manager_ids | | | ATS | [Scorecard](https://docs.unified.to/ats/scorecard/model) | interviewer_id | | | ATS | [Company](https://docs.unified.to/ats/company/model) | recruiters_ids | | | Call Center | [Call](https://docs.unified.to/uc/call/model) | user_id | The agent | | CRM | [Company](https://docs.unified.to/crm/company/model) | user_id | The salesperson that is responsible for this company/account | | CRM | [Contact](https://docs.unified.to/crm/contact/model) | user_id | The salesperson that is responsible for this contact | | CRM | [Deal](https://docs.unified.to/crm/deal/model) | user_id | The salesperson that is responsible for this deal/opportunity | | CRM | [Event](https://docs.unified.to/crm/event/model) | user_id | | | CRM | Lead[ ](https://docs.unified.to/crm/lead/model) | user_id | The salesperson that is responsible for this lead | | CRM | [Lead](https://docs.unified.to/crm/lead/model) | creator_user_id | The user that created this lead | | HR | [Employee](https://docs.unified.to/hris/employee/model) | manager_id | This user's reporting manager | | HR | [Group](https://docs.unified.to/hris/group/model) | user_ids | The group's members | | HR | [Group](https://docs.unified.to/hris/group/model) | manager_ids | The group's managers | | HR | [Payslip](https://docs.unified.to/hris/payslip/model) | user_id | The employee that this pay slip pertains to | | HR | [Timeoff](https://docs.unified.to/hris/timeoff/model) | user_id | The employee that this time off request pertains to | | HR | [Timeoff](https://docs.unified.to/hris/timeoff/model) | approver_user_id | The employee that approved this time off request | | KMS | [Space](https://docs.unified.to/kms/space/model) | user_id | The creator of this space | | KMS | [Page](https://docs.unified.to/kms/page/model) | user_id | The creator of this oage | | Messaging | [Message](https://docs.unified.to/messaging/message/model) | author_member.user_id | | | Messaging | [Message](https://docs.unified.to/messaging/message/model) | destination_members.user_id | | | Messaging | [Message](https://docs.unified.to/messaging/message/model) | hidden_members.user_id | | | Messaging | [Message](https://docs.unified.to/messaging/message/model) | mentioned_members.user_id | | | Storage | [File](https://docs.unified.to/storage/file/model) | user_id | The owner of this file/folder | | Tasks | [Project](https://docs.unified.to/task/project/model) | user_ids | The users that belong to this project | | Tasks | [Task](https://docs.unified.to/task/task/model) | assigned_user_id | | | Tasks | [Task](https://docs.unified.to/task/task/model) | creator_user_id | | | Tasks | [Task](https://docs.unified.to/task/task/model) | follower_user_ids | | | Ticketing | [Note](https://docs.unified.to/ticketing/note/model) | user_id | The user who created this note | | Ticketing | [Ticket](https://docs.unified.to/ticketing/ticket/model) | user_id | The user who created this ticket | Here are several practical use cases that leverage our unified HR API for non-HR use-cases: **Streamlined Onboarding:** Automate your customers' onboarding process by syncing employee data across HR, task management, and training platforms. Ensure that new hires are promptly added to all necessary systems and have access to the right resources from day one. **Assigning Leads and Deals:** Assign newly created leads and/or deals to a specific sales-rep using their user ID **Notify Hiring Managers:** When adding a new candidate and their job application, notify the hiring manager by retrieving their information and emailing them By integrating employee data across multiple SaaS platforms, organizations can improve operational efficiency, enhance data accuracy, and provide a better employee experience. ## Resources - - [Overview of our unified HR API documentation ](https://docs.unified.to/hris/overview) - [Full list of supported HR integrations ](https://unified.to/hris) ## How to add API support for the Create Activity in Crelate URL: https://docs.unified.to/guides/how_to_add_api_support_for_the_create_activity_in_crelate # How to add API support for the Create Activity in Crelate ------ _September 25, 2025_ Here's how to configure Activity Types properly. First off, you need to set "Available On" for Activity Types: 1. Navigate to Settings → Activities 2. Select the specific Activity Type you want to configure 3. Find the Available On section - this determines which record types the Activity will appear on 4. Critical: By default, Activity Types are set to no records and must be configured or they'll be unavailable for use ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_add_api_support_for_the_create_activity_in_crelate-0.png) The settings shown in the image above will enable the maximum number of API features. ## How to Associate a Connection ID with Your End-User URL: https://docs.unified.to/guides/how_to_associate_a_connection_id_with_your_end_user # How to Associate a Connection ID with Your End-User ------ _April 4, 2024_ This guide explains how to associate connections you create through Unified.to with end-users in your own application. When your end-users authorize access to third-party applications through Unified.to, you need a way to keep track of which connections belong to which users in your application. This guide covers two ways to accomplish this, with recommendations on when to use each approach. If you prefer to learn through video, check out our guide on YouTube: ## Before you begin This guide assumes you have: - A basic understanding of [end-users, integrations, and connections](https://unified.to/blog/end_users_integrations_and_connections) - Familiarity with OAuth 2.0 authorization flows ## Method 1: Use the state parameter (recommended) The state parameter provides a secure way to maintain context throughout the OAuth flow by passing data between your authorization request and the callback. The state parameter _does not_ change during authorization. Whatever value you put into the state parameter in the authorization URL is the same value that will be extracted from the success URL. 1. Create a state object with your user's ID, passing in additional security measures like a hashing signature. For example: ```javascript const stateObject = { user_id: "xyz789", nonce: generateRandomString(), // Add randomness for security timestamp: Date.now(), // Optional: Add timestamp for expiry checking sig: generateSignature() // Optional: Add signature for verification }; ``` 2. Encode the state object as a base64-string: ```javascript const encodedState = Buffer.from(JSON.stringify(stateObject)).toString('base64'); ``` 3. Add the encoded state to your authorization URL ```javascript const authUrl = `https://api.unified.to/unified/integration/auth/${workspace_id}/${integration}?state=${encodedState}`; ``` 4. Handle the callback after successful authorization. The connection ID is included in the URL as `id`. For example: ```javascript // In your callback handler function handleCallback(req, res) { // Get connection ID from the callback URL const connectionId = req.query.id; // Decode and verify the state const decodedState = JSON.parse(Buffer.from(req.query.state, 'base64').toString()); // Verify the state is valid (check signature, nonce, timestamp if used) if (verifyState(decodedState)) { // Associate the connection ID with the user ID in your database await saveUserConnection(decodedState.user_id, connectionId); } } ``` ## Method 2: Use the external ID parameter This method leverages Unified.to's built-in external ID (`uid`) field to store your user associations. The success URL contains another parameter, `uid`, also known as the **External ID** parameter, which can be used to map to your end-user's ID. 1. Add the external ID parameter to your authorization URL ```javascript const authUrl = `https://api.unified.to/unified/integration/auth/${workspace_id}/${integration}?uid=${user_id}`; ``` 2. Once a connection is created, that external ID is stored in the connection object. You can now query connections using the external ID: ```javascript const options = { method: 'GET', url: 'https://api.unified.to/unified/connection', headers: { 'authorization': `Bearer ${unified_api_key}` }, params: { external_xref: user_id } }; ``` ## Best practices for implementation 1. **Always validate the state parameter** - Check signatures if used - Verify nonces haven't been reused 2. **Securely store connection associations** - Use a database to map user IDs to connection IDs - Consider encrypting sensitive data 3. **Handle error cases** - Missing parameters - Failed authorization attempts ## How to build a Candidate Assessment product with Unified.to URL: https://docs.unified.to/guides/how_to_build_a_candidate_assessment_product_with_unified # How to build a Candidate Assessment product with Unified.to ------ _October 23, 2023_ There are two different ways that you can build a candidate assessment solution. Some ATS providers will allow for an assessment solution to register with them and then call your server with an assessment request. Unfortunately, not many ATS providers support this method, so [Unified.to](https://unified.to/) only supports this method that is widely supported. In the high-paced realm of recruitment, where talent acquisition meets the evolving landscape of HR technology, the need for efficient and robust Candidate Assessment tools has never been more crucial. As companies strive to identify top talent, incorporating candidate assessment software that facilitates critical activities like background checks, skills tests, candidate analytics analysis and more not only expedites the screening process but also ensures a comprehensive and data-driven approach to talent acquisition. This guide explore how to develop robust and scalable Candidate Assessment software by leveraging third-party integrations through Unified.to's Unified API developer platform to enhance the value of your candidate assessment solution. For teams building AI-driven screening or semantic resume matching, this same architecture can also power retrieval-augmented generation (RAG) workflows on top of ATS data. ## Who You have built a `Candidate Assessment` solution for recruiters and hiring managers, enabling them to evaluate candidates who are applying for positions within their companies. ## Why (your goal) - You need to access your customers' `ATS` (application tracking systems) to automate the data flow of new applications so that your solution can test and assess their candidates. - Retrieve resumes and job descriptions to power RAG-based candidate ranking and semantic matching. ## What You will need to integrate seamlessly with the leading ATS solutions utilized by your customers, such as [Greenhouse](https://unified.to/integrations/greenhouse), [Lever](https://unified.to/integrations/lever), [SmartRecruiters](https://unified.to/integrations/smartrecruiters), [SAP SuccessFactors](https://unified.to/integrations/successfactors), and [more](https://unified.to/ats). This will provide your users with a streamlined user experience, ensuring efficient data flow and synchronization between your Candidate Assessment solution and their preferred Applicant Tracking Systems (ATS). ## How to add ATS integrations to your product Before we start, be sure to first read: [Getting Started with Unified](https://unified.to/blog/start_here_getting_started_with_unified) 1. **Get an initial list of applications** 1. When the customer authorizes an ATS connection, [read the initial set of active applications](https://docs.unified.to/ats/application/List_all_applications) ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; import { AtsApplication, AtsApplicationStatus } from '@unified-api/typescript-sdk/dist/sdk/models/shared'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); export async function getApplications(connectionId: string, trigger_status: AtsApplicationStatus, jobId: string, updatedGte?: Date) { const applications: AtsApplication[] = []; const limit = 100; let offset = 0; while (true) { const result = await sdk.ats.listAtsApplications({ updatedGte, jobId, offset, limit, connectionId, }); const apps = result.atsApplications || []; applications.push(...apps.filter((application) => application.status === trigger_status)); if (apps.length === limit) { offset += limit; } else { break; } } return applications; } ``` 2. **Set up to get updated applications** 1. If you want to use webhooks to get new/updated applications in the future, [create a webhook](https://docs.unified.to/unified/webhook/Create_webhook_subscription) with that `connection_id` ```typescript import { createHmac } from 'crypto'; import { UnifiedTo } from '@unified-api/typescript-sdk'; import { AtsApplication, AtsApplicationStatus, Event, ObjectType, WebhookType } from '@unified-api/typescript-sdk/dist/sdk/models/shared'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); interface IncomingWebhook { id: string; created_at: Date; updated_at: Date; workspace_id: string; connection_id: string; hook_url: string; object_type: TObjectType; interval: number; checked_at: Date; integration_type: string; environment: string; event: Event; runs: string[]; fields: string; webhook_type: WebhookType; is_healthy: boolean; page_max_limit: number; } interface IWebhookData { data: T[]; // The data array will contact an array of specific objects according to the webhook's connection. (eg. CRM Contacts) webhook: IncomingWebhook; // The webhook object nonce: string; // random string sig: string; // HMAC-SHA1(workspace.secret, data + nonce) type: 'INITIAL-PARTIAL' | 'INITIAL-COMPLETE' | 'VIRTUAL' | 'NATIVE'; } export async function createApplicationsWebhook(connectionId: string, myWebhookUrl: string) { const result = await sdk.unified.createUnifiedWebhook({ webhook: { hookUrl: myWebhookUrl, objectType: ObjectType.AtsApplication, event: Event.Updated, connectionId, }, }); return result.webhook; } export async function handleUnifiedWebhook(incoming: IWebhookData, trigger_status: AtsApplicationStatus) { if (incoming.webhook.object_type !== 'ats_application') { return; // not for us } const sig = createHmac('sha1', process.env.WORKSPACE_SECRET) .update(JSON.stringify(incoming.data)) .update(String(incoming.nonce)) .digest('base64'); if (sig !== incoming.sig) { return; // Houston, we have a problem... with security } return incoming.data?.filter((application: AtsApplication) => application.status === trigger_status); } ``` 2. Alternatively, create a polling schedule to [get new/updated applications](https://docs.unified.to/ats/application/List_all_applications), which would be similar to the code in 1a) 3. **Filter applications on a specific status** 1. The [application](https://docs.unified.to/ats/application/model) has a standardized `status` across all ATS integrations, so decide which one makes sense for you to trigger the assessment 2. Application.Status options are `NEW` `REVIEWING` `SCREENING` `SUBMITTED` `FIRST_INTERVIEW` `SECOND_INTERVIEW` `THIRD_INTERVIEW` `BACKGROUND_CHECK` `OFFERED` `ACCEPTED` `HIRED` `REJECTED` `WITHDRAWN`. Of these, `REVIEWING`, `SCREENING`, `FIRST_INTERVIEW`, `SECOND_INTERVIEW`, or `BACKGROUND_CHECK` should be considered. 4. **Email the candidate with the assessment based on the job** 1. [Read the candidate](https://docs.unified.to/ats/candidate/Retrieve_a_candidate) from the `Application.candidate_id` field ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); export async function readCandidate(connectionId: string, candidateId: string) { return await sdk.ats.getAtsCandidate({ id: candidateId, connectionId, }); } ``` 2. Optional: [read the job](https://docs.unified.to/ats/job/Retrieve_a_job) from the `Application.job_id` field ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); export async function readJob(connectionId: string, jobId: string) { return await sdk.ats.getAtsJob({ id: jobId, connectionId, }); } ``` 3. Determine which test to send to the candidate based on the [`Job.id`](https://job.id/), `Job.descripton` or `Job.name` fields 5. **Once the candidate has completed the assessment/test, notify the hiring manager and/or recruiter** 1. [Read the employee](https://docs.unified.to/hris/employee/Retrieve_an_employee) information based on the `Job.hiring_managers_ids` and/or `Job.recruiter_ids` fields and use the `Employee.emails` field to email them the notification ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); export async function readEmployee(connectionId: string, employeeId: string) { return await sdk.hris.getHrisEmployee({ id: employeeId, connectionId, }); } ``` 2. Optional: [push back a PDF document](https://docs.unified.to/ats/document/Create_a_document) into the customer's ATS that is associated with that application ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; import { AtsDocumentType } from '@unified-api/typescript-sdk/dist/sdk/models/shared'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); export async function createDocument(connectionId: string, applicationId: string, documentUrl: string, filename: string, type: AtsDocumentType) { return await sdk.ats.createAtsDocument({ atsDocument: { applicationId, type, documentUrl, filename, }, connectionId, }); } ``` 3. Optional: [create an activity](https://docs.unified.to/ats/activity/Create_an_activity) associated with the application or candidate 4. Optional: [update a candidate](https://docs.unified.to/ats/candidate/Update_a_candidate) with certain `tags` ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); export async function updateCandidateTags(connectionId: string, candidateId: string, tags: string[]) { return await sdk.ats.updateAtsCandidate({ atsCandidate: { tags, }, id: candidateId, connectionId, }); } ``` ### Advanced: Adding RAG-Based Resume Matching Beyond triggering assessments, you can extend this workflow into a RAG architecture: 1. Retrieve the candidate's resume (`AtsCandidate.resume_url`) and job description. 2. Chunk and embed both documents. 3. Store embeddings in your vector database with identifiers like `connection_id`, `candidate_id`, and `job_id`. 4. Retrieve the most relevant resume segments against the job description before generating screening insights or ranking scores. Unified handles ingestion and normalization across ATS providers; embeddings and retrieval remain in your infrastructure. ## Keep learning: - [How to add a test connection ](https://unified.to/blog/start_here_how_to_add_a_test_connection) - [How your customers add integrations from your application](https://unified.to/blog/start_here_how_your_customer_can_add_an_integration_in_your_application) - [How to generate OAuth 2 credentials ](https://unified.to/blog/start_here_how_to_generate_oauth2_credentials) ## A unified API to integrate them all Unified.to is a complete solution to streamline your integration development process and power your Candidate Assessment product with critical third-party candidate data. You're reading this article on the [**Unified.to**](https://unified.to/) blog. We're a Unified API developer platform for SaaS customer-facing integrations. We're excited to continue to innovate at Unified.to and solve hard, critical integration-related problems for our customers. If you're curious about our integrations-as-a-service solution, consider [**signing up for a free account**](https://app.unified.to/login) or [**meet with an integrations expert**](https://calendly.com/michelle-unified/discovery-via-blog)**.** ## How to build a candidate sourcing or job board app with Unified.to URL: https://docs.unified.to/guides/how_to_build_a_candidate_sourcing_or_job_board_app_with_unified # How to build a candidate sourcing or job board app with Unified.to ------ _January 4, 2024_ ## Why build with a unified API? API integrations are necessary for any modern SaaS app, especially for recruitment solutions like candidate sourcing and job boards that require a high volume of integrations to improve product value and support a broader user base. Instead of spending months or years developing ATS integrations individually, developers can leverage a unified API to integrate once to launch multiple integrations simultaneously. Unified APIs simplify integration efforts by offering consistent endpoints and data formats that reduce the complexity of managing numerous APIs. Unified APIs accelerate development cycles, enabling quicker deployment of new features and product updates for your recruitment software. We built Unified.to to make integration development as easy as possible for SaaS developers. ## Prerequisites Before starting, be sure to review our [Getting Started with Unified](https://unified.to/blog/start_here_getting_started_with_unified) article. It will walk you through registering for a free account and our onboarding. Once you have completed the onboarding steps, go ahead and as you like. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_build_a_candidate_sourcing_or_job_board_app_with_unified-0.png) Next, deploy the [Unified.to](https://unified.to/) Embedded Authorization widget in your product's user-interface. Check out our to learn more. ## Overview These are main steps that you will need to support to build your candidate sourcing or job board app: 1. read active jobs to search relevant candidates for 2. apply a candidate to a job Let's go into more detail for each. ## Step 1: Read Jobs Before searching or finding relevant candidates, you'll need to know which jobs your customer wants you to search for. [Unified.to](https://unified.to/) can help your app seamlessly import jobs from your customer's ATS. To get a list of existing jobs from your end user's ATS, simply call the `listJobs` [API endpoint](https://docs.unified.to/ats/job/List_all_jobs). ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; const sdk = new UnifiedTo({ security: { jwt: "", }, }); export async function getJobs(connectionId: string) { return await sdk.ats.listAtsJobs({ connectionId, }); } ``` The connection_id comes in from your end user's authorization of their ATS and is stored on your end. You can watch our for more detail. A `Job` will have specific fields such as `name` , `description`, `compensation`, and `location` that will be relevant to your software. Reference our [API docs](https://docs.unified.to/ats/job/model) for more information. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_build_a_candidate_sourcing_or_job_board_app_with_unified-1.png) Once you have the list of jobs, ask your customer to identify which jobs your app should identify candidates for. ## Step 2: Create an application Once you have a candidate, there are two ways to create a job application. 1. Send the interested candidate to the job's public web page to apply directly. Most `Job` objects will have a `public_job_url` field that contains a list of URLs. The downside to this method is that you lose control of the candidate's experience and not all ATS providers supply the public website. The upside of this method is that it is very easy to implement. 2. Create an application and candidate in the ATS directly using our Unified ATS API. This is easily accomplished by using the `createCandidate` [API endpoint](https://docs.unified.to/ats/candidate/Create_a_candidate) and `createApplication` [API endpoint](https://docs.unified.to/ats/application/Create_an_application). ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; import { AtsCandidate } from '@unified-api/typescript-sdk/dist/sdk/models/shared'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); export async function createCandidate(connectionId: string, atsCandidate: AtsCandidate) { const result = await sdk.ats.createAtsCandidate({ atsCandidate, connectionId, }); return result.atsCandidate?.id; } export async function createApplication(connectionId: string, candidateId: string, jobId: string) { const result = await sdk.ats.createAtsApplication({ atsApplication: { candidateId, jobId, }, connectionId, }); return result.atsApplication?.id; } ``` ## Step 3: uploading a resume/CV/cover letter (optional) You can also upload the candidate's documents. You will need to set the following fields in the `Document` [object](https://docs.unified.to/ats/document/model): - job_id - application_id - document_url Some ATS providers may require additional fields to be present. Be sure to check their [feature support page](https://app.unified.to/integrations/greenhouse?tab=support) for more information. For example, Greenhouse also requires `type` and `filename` fields. ## Step 4: Review the application status (optional) If you need to be informed on the status of the candidate's job application, you can use the `getApplication` [API endpoint](https://docs.unified.to/ats/application/Retrieve_an_application) or you can set up a webhook for that connection's modified applications. ## Conclusion It is easy to support many ATS integrations in your recruiting application and allow your end user to seamlessly move data between the two. Use Unified.to's [Unified ATS API](https://unified.to/ats) to launch integrations in days and harness recruitment data for your candidate sourcing or job board app. ## Additional resources - [Unified ATS API](https://unified.to/ats) - [API Documentation for the Unified ATS API](https://docs.unified.to/ats/overview) - [SDKs for Unified.to](https://docs.unified.to/overview/sdks) - - ## How to build a Discord support bot with Unified.to and Langbase URL: https://docs.unified.to/guides/how_to_build_a_discord_support_bot_with_unified_and_langbase # How to build a Discord support bot with Unified.to and Langbase ------ _December 12, 2024_ Want to build an AI-powered support bot that can answer questions with information from an knowledge base? In this guide, we'll show you how to combine Unified.to's data ingestion capabilities with Langbase's AI agent framework to create a powerful support bot. We'll pull content from your customers' knowledge management system (like Notion) through Unified.to, process it with Langbase's Memory Agents, and make it available to a Discord bot, powered by Unified.to's Messaging API, so it can answer support questions. We'll be using React and Typescript in these examples, which you can adapt to fit your framework of choice. ## Overview This guide walks you through how to: - Pull knowledge base content using Unified.to's KMS API - Process and prepare this content using Langbase's Memory Agents - Create an AI agent using Langbase Pipes - Send responses through Discord using Unified.to's Messaging API By combining Unified.to's unified APIs with Langbase's AI capabilities, we get the best of both worlds: easy access to your data sources and powerful AI features without the complexity of building everything from scratch. ## Before you begin Make sure you have: - A [Unified.to](https://app.unified.to/) account - A [Langbase](https://langbase.com/) account - Access to a knowledge management system (e.g., Notion) containing your support content - Unified.to allows you to easily access your customers' data and that's normally what you would use in production, but for the purposes of this guide you can follow along with your own Notion account - A Discord server where you want to deploy your bot > If you don't want to use real data from Notion or Discord, you can also take advantage of Unified.to's [sandbox environment](https://docs.unified.to/concepts/sandbox) and work with synthetic data instead. The following steps assume you already have your Unified.to account with your workspace ID and API key available. If you're new, check out our [Quick Start guide](https://docs.unified.to/quick-start) to get familiar with the platform. ## Set up your connections The first thing you'll do is activate your preferred KMS platform to be the source of data for your bot. We'll use Notion in this guide, but our [KMS API](https://unified.to/kms) supports 8 platforms today and more are being added on a regular basis. **Note:** If you're using the sandbox environment, for the steps below that ask you to enter your OAuth 2 credentials, just enter any random value e.g., `test123`. ### Activate Notion and Discord integrations 1. On app.unified.to, go to the [Integrations page](https://app.unified.to/integrations) 2. Search for and select Notion 3. Choose OAuth 2, enter your credentials (which can be anything if you're using the sandbox environment), and then click **Activate** Repeat steps 1-3 but for Discord. If you want to use real data from Notion and Discord, check out our how-to guides on setting up with them: - [How to set up and configure Notion](https://docs.unified.to/guides/how_to_set_up_and_configure_notion) - [How to get your Discord OAuth 2 credentials and bot token](https://docs.unified.to/guides/how_to_get_your_discord_oauth_2_credentials_and_bot_token) ### (Sandbox environment) Create a test connection from the web app If you're using the sandbox environment, then you can create test connections easily from the web app. 1. On app.unified.to, go to the [Connections](https://app.unified.to/connections) page 2. Click on **Create test connection** at the top right corner of the page (note: this will only show up if your current environment is the sandbox environment) 3. Click on the **Notion** and **Discord** integrations 4. Click **Done** ![Screenshot_2024-12-12_at_10.58.17_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_build_a_discord_support_bot_with_unified_and_langbase-0.png) Skip ahead to the 'Pull content from your knowledge base' step below. ### (Production environments) Add the Authorization component to your app To ask for your customers' permission to access their Notion and Discord accounts, you can add Unified.to's Authorization component to your front-end: 1. Add the following React component to your app. Make sure to replace `WORKSPACE_ID` with your actual workspace ID. ```javascript // TODO: Make sure to run 'npm install @unified-api/react-directory' import UnifiedDirectory from '@unified-api/react-directory'; ``` We also offer pre-built embedded components in Angular, Vue, and Svelte. See other ways to display the Authorization component in your app [here](https://app.unified.to/embed?tab=Authorization). - If your customers' mainly interact with you through message-based platforms, you could even send them the Authorization URL through an AI chat agent instead of a web front-end! The Authorization component displays a list of your integrations. When your user clicks on one of these integrations, the authorization flow will open up in a new tab. Your users will follow the steps to grant you access to their accounts. When that is done, they will be redirected back to your app (the default success URL location). 1. Handle the callback after a user authorizes an integration. The connection ID will be sent back in the success URL - we need to save this to make API calls later on. In this example, we'll save the connection ID to local storage: ```javascript function handleAuthCallback() { const urlParams = new URLSearchParams(window.location.search); const connectionId = urlParams.get('id'); if (connectionId) { console.log('New connection created:', connectionId); // Store this connection ID securely - you'll need it for API calls localStorage.setItem('unifiedConnectionId', connectionId); } } window.addEventListener('load', handleAuthCallback); ``` For this guide, you can be your own test user: 1. Click on the Notion integration in the Authorization component 2. Grant access to your Notion workspace 3. Repeat for Discord 4. Save both connection IDs - you'll need them for the next steps Note: In production, you should associate these connection IDs with your user's account in your database. For more information, see our guide on [How to associate connection IDs with your users](https://docs.unified.to/guides/how_to_associate_a_connection_id_with_your_end_user). ## Pull content from your knowledge base Now that you've got a connection ID, we can start making calls to the Unified API! First, let's write some functions to recursively fetch all content from your KMS: ```javascript async function fetchContent(connectionId: string, apiKey: string) { const spaces = await fetchAllSpaces(connectionId, apiKey); const allContent = []; for (const space of spaces) { const pages = await fetchPagesInSpace(connectionId, apiKey, space.id); allContent.push(...pages); } return allContent; } async function fetchAllSpaces(connectionId: string, apiKey: string) { const response = await fetch( `https://api.unified.to/kms/${connectionId}/space?limit=100`, { headers: { Authorization: `Bearer ${apiKey}`, } } ); if (!response.ok) { throw new Error(`Failed to fetch spaces: ${response.statusText}`); } return response.json(); } async function fetchPagesInSpace(connectionId: string, apiKey: string, spaceId: string) { const response = await fetch( `https://api.unified.to/kms/${connectionId}/page?limit=100&space_id=${spaceId}`, { headers: { Authorization: `Bearer ${apiKey}`, } } ); if (!response.ok) { throw new Error(`Failed to fetch pages: ${response.statusText}`); } return response.json(); } // Usage const content = await fetchContent(CONNECTION_ID, API_KEY); ``` **API reference**: [KMS](https://docs.unified.to/kms/overview) Call `fetchContent` in your app to see the results. You should see a collection of [Pages](https://docs.unified.to/kms/page/model). ## Process the content with Langbase Memory Agents Now that we have our knowledge base content, we need to prepare it for AI consumption. Instead of building a complex RAG pipeline ourselves, we'll use Langbase's [Memory Agents](https://langbase.com/docs/memory) to handle all the heavy lifting - from text processing to embedding generation and storage. First, let's install the Langbase SDK: ```shell npm install langbase ``` Now we can create memory to store our knowledge base: ```typescript import { Langbase } from 'langbase'; const langbase = new Langbase({ apiKey: process.env.LANGBASE_API_KEY!, }); async function createKnowledgeBase() { // Create a new memory const memory = await langbase.memory.create({ name: 'support-bot-knowledge', description: 'Knowledge base for our support bot', embedding_model: 'openai:text-embedding-3-large'// Using OpenAI's latest model }); return memory; } ``` **API reference:** [Create memory](https://langbase.com/docs/sdk/memory/create) Now let's process our Unified.to content and upload it to Langbase: ```typescript async function uploadContentToMemory(pages, memoryName: string) { for (const page of pages) { // Download the page content from Unified.to's download_url const content = await fetch(page.download_url).then(res => res.text()); // Create a buffer from the content const documentBuffer = Buffer.from(content); // Upload to Langbase with metadata await langbase.memory.documents.upload({ memoryName, documentName: page.title, document: documentBuffer, contentType: 'text/markdown', // Adjust based on your content type meta: { source: 'unified_kms', created_at: page.created_at, updated_at: page.updated_at, space_id: page.space_id } }); } } ``` **API reference:** [Upload document](https://langbase.com/docs/sdk/memory/document-upload) Langbase's Memory Agents will automatically: 1. Process the text (Unified.to's data models have already cleaned the data for you!) 2. Split content into optimal chunks 3. Generate embeddings using state-of-the-art models 4. Store and index everything for fast retrieval Once your content is uploaded, you can create a Pipe to handle natural language interactions. Pipes are custom AI agents that can be exposed as APIs, making them perfect for our Discord bot: ```typescript async function createSupportBotPipe(memoryName: string) { const pipe = await langbase.pipe.create({ name: 'Support Bot', type: 'chat', memory: memoryName, description: 'Support bot powered by our knowledge base' }); return pipe; } ``` **API reference**: [Create pipe](https://langbase.com/docs/sdk/pipe/create) This gives you a fully-functional RAG system with just a few API calls. No need to manage embeddings, vector stores, or complex AI infrastructure - Langbase handles all of that for you. ## Set up real-time message monitoring To make your support bot responsive, you'll need to set up webhooks to receive notifications when users send messages in Discord channels. 1. Create a webhook to monitor Discord messages: ```javascript async function createDiscordWebhook(connectionId: string, apiKey: string, hookUrl: string) { const response = await fetch( 'https://api.unified.to/unified/webhook', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ connection_id: connectionId, hook_url: hookUrl, object_type: 'messaging_message', event: 'created' }) } ); if (!response.ok) { throw new Error(`Failed to create webhook: ${response.statusText}`); } return response.json(); } createDiscordWebhook(CONNECTION_ID, API_KEY, 'https://localhost:8000/webhook') ``` **API reference**: [Create a webhook](https://docs.unified.to/unified/webhook/Create_webhook_subscription) 1. Set up an endpoint on your server to handle incoming webhook events. For example: ```javascript app.post('/webhook', async (req, res) => { // Verify webhook signature const { data, webhook, nonce, sig } = req.body; // Handle new messages for (const message of data) { // Ignore messages from the bot itself if (message.author_member.id === BOT_USER_ID) continue; // Process the message and generate a response const response = await generateBotResponse(message.message); // Send the response back to the channel await sendDiscordResponse( DISCORD_CONNECTION_ID, API_KEY, message.channel_id, response ); } res.sendStatus(200); }); ``` 1. Validate incoming webhooks (recommended): ```javascript const crypto = require('crypto'); function verifyWebhookSignature(data, nonce, signature, workspaceSecret) { const hmac = crypto.createHmac('sha1', workspaceSecret); const calculatedSignature = hmac .update(JSON.stringify(data) + nonce) .digest('base64'); return calculatedSignature === signature; } ``` With this setup, your bot can: 1. Receive notifications whenever a new message is sent in the monitored channels 2. Process those messages through your AI pipeline 3. Send responses back to the appropriate channel For more details on webhooks at Unified.to, see our [guide to webhooks](https://docs.unified.to/reference/webhooks). ## Send responses through Discord When you're ready to respond to a user's question, use our Messaging API to send the message back to Discord. Here's an example implementation with error handling and message formatting: ```javascript async function sendDiscordResponse(connectionId: string, apiKey: string, channelId: string, response: string) { const formattedResponse = formatDiscordMessage(response); const apiResponse = await fetch( `https://api.unified.to/messaging/${connectionId}/message`, { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_id: channelId, message: formattedResponse, message_html: formattedResponse, // Optional: Use for formatted text }) } ); if (!apiResponse.ok) { if (apiResponse.status === 401) { throw new Error('Authentication failed - check your API key'); } else if (apiResponse.status === 404) { throw new Error('Channel not found - verify channel ID'); } throw new Error(`Failed to send message: ${apiResponse.statusText}`); } return apiResponse.json(); } function formatDiscordMessage(response) { // Example formatting for Discord markdown return `**Bot Response:**\n${response}`; } // Example usage with different message types async function sendBotResponses(connectionId, apiKey, channelId) { // Simple text response await sendDiscordResponse( connectionId, apiKey, channelId, 'Here is the answer to your question...' ); // Response with code block await sendDiscordResponse( connectionId, apiKey, channelId, '```javascript\nconst example = "code block";\n```' ); // Response with rich formatting await sendDiscordResponse( connectionId, apiKey, channelId, '**Bold text** and *italic text* with a [link](https://example.com)' ); } ``` **API reference**: [Messaging](https://docs.unified.to/messaging/overview) ## Putting it all together Here's a high-level overview of how to combine all the components into a working support bot: ```typescript import { Langbase } from 'langbase'; async function initializeSupportBot() { // Initialize APIs const langbase = new Langbase({ apiKey: process.env.LANGBASE_API_KEY! }); // Get connection IDs - implement these based on how you manage your users const KMS_CONNECTION_ID = await getKMSConnection(user_id); const DISCORD_CONNECTION_ID = await getDiscordConnection(user_id); const UNIFIED_API_KEY = process.env.UNIFIED_API_KEY; // Step 1: Fetch content from KMS const kmsContent = await fetchContent(KMS_CONNECTION_ID, UNIFIED_API_KEY); // Step 2: Set up Langbase Memory and upload content const memory = await langbase.memory.create({ name: `support-kb-${user_id}`, description: 'Knowledge base for support bot' }); await uploadContentToMemory(kmsContent, memory.name); // Step 3: Create an AI agent using Langbase Pipe const pipe = await langbase.pipe.create({ name: `discord-support-bot`, type: 'chat', memory: memory.name, description: 'Support bot powered by our knowledge base' }); // Step 4: Set up Discord webhook for real-time messages const webhook = await createDiscordWebhook( DISCORD_CONNECTION_ID, UNIFIED_API_KEY, 'https://your-server.com/webhook' ); // Step 5: Handle incoming questions async function handleQuestion(channelId: string, question: string) { try { // Use the Pipe to generate a response const response = await langbase.pipe.chat({ pipe: pipe.name, messages: [{ role: 'user', content: question }] }); // Send the response back to Discord await sendDiscordResponse( DISCORD_CONNECTION_ID, UNIFIED_API_KEY, channelId, response.message ); } catch (error) { console.error('Error handling question:', error); } } return { memory, pipe, webhook, handleQuestion }; } // Usage example async function startBot() { const bot = await initializeSupportBot(); // Your webhook endpoint would call this when receiving messages app.post('/webhook', async (req, res) => { const { data, webhook, nonce, sig } = req.body; // Handle new messages for (const message of data) { // Ignore messages from the bot itself if (message.author_member.id === BOT_USER_ID) continue; await bot.handleQuestion(message.channel_id, message.message); } res.sendStatus(200); }); } ``` This implementation: 1. Initializes both Unified.to and Langbase clients 2. Creates a Memory AGent to store your knowledge base content 3. Sets up a Pipe to handle natural language interactions 4. Configures Discord webhooks for real-time messaging The bot will now: - Automatically receive messages from Discord through webhooks - Process questions using your knowledge base through Langbase Pipes - Send responses back to the appropriate Discord channel All of this happens with minimal infrastructure management on your part - Unified.to handles the integrations while Langbase manages the AI components. ## Conclusion Congratulations! You've built a powerful support bot by combining Unified.to's integration capabilities with Langbase's AI features. In this guide, you learned how to: - Pull knowledge base content from any system using Unified.to's KMS API - Process and embed that content using Langbase Memory Agents - Create an AI agent using Langbase Pipes - Handle real-time messages through Discord using Unified.to's Messaging API and webhooks As an added bonus, this architecture is incredibly flexible. Since you're using Unified.to's APIs, you can easily expand your knowledge sources - want to pull content from Confluence instead of Notion? Or deploy your bot on Slack? It's just a matter of changing the connection - your core logic stays the same. And with Langbase handling the AI components, you can enhance your bot's capabilities without managing complex infrastructure: - Experiment with multiple Memory Agents for different types of content - Customize your Pipe's behaviour through prompt engineering - Scale your bot without worrying about vector store management Happy building! ## How to Build a Fintech Application with Unified's Payments API URL: https://docs.unified.to/guides/how_to_build_a_fintech_application_with_unified_payments_api # How to Build a Fintech Application with Unified's Payments API ------ _September 9, 2025_ _Last Updated: July 2026_ With Unified, you can build fin-tech products that work with your end customers payment providers. With a single integration you can connect payment processors like Stripe, PayPal, GoCardless and more! You can do various things such as creating payments, generating payment links, and reading refund and payout records for reconciliation, all without building custom integrations for each payment provider. Refunds and payouts are read-only in the unified model: you retrieve them to reconcile, and issue them through the payment provider itself. In this guide, we will show you how to create and list payments as well as some related data. For the example we will be using Stripe, but this approach works for any of the payment integrations supported by Unified. [See the full list of supported payment integrations.](https://docs.unified.to/payment/integrations) --- ## Prerequisites - Node.js (v18+) - Unified account with a payment integration enabled (e.g., Stripe) - Unified API key - Your customer's payment processor connection ID --- ## Step 1: Setting up your project Set up your dependencies ```bash mkdir payments-demo cd payments-demo npm init -y npm install @unified-api/typescript-sdk dotenv ``` Add your credentials to `.env`: ```plain text UNIFIED_API_KEY=your_unified_api_key CONNECTION_STRIPE=your_customer_stripe_connection_id ``` --- ## Step 2: Initialize the SDK ```typescript import 'dotenv/config'; import { UnifiedTo } from '@unified-api/typescript-sdk'; const { UNIFIED_API_KEY, CONNECTION_STRIPE } = process.env; const sdk = new UnifiedTo({ security: { jwt: UNIFIED_API_KEY! }, }); ``` --- ## Step 3: How to Get Your Customer's Connection ID Before you can list payments, your customer must authorize your app to access their payment provider via Unified's embedded auth flow. Once authorized, you'll receive a connection ID for each integrations. ## Step 4: Listing Payments ```typescript export async function listPayments(connectionId: string) { const payments = await sdk.payment.listPaymentPayments({ connectionId, limit: 10, }); return payments; // PaymentPayment[] } ``` --- ## Step 5: Creating a Payment Link ```typescript export async function createPaymentLink(connectionId: string, amount: number, currency: string) { const link = await sdk.link.createPaymentLink({ connectionId, paymentLink: { amount, currency, isActive: true, successUrl: "", // Stripe typically requires line items on links lineitems: [ { itemName: "Order #123", unitAmount: amount, unitQuantity: 1, itemSku: "SKU-123", }, ], }, }); return link; // PaymentLink } ``` --- ## Step 6: Listing Refunds ```typescript export async function listRefunds(connectionId: string) { const refunds = await sdk.refund.listPaymentRefunds({ connectionId, limit: 10, }); return refunds; // PaymentRefund[] } ``` --- ## Step 7: Listing Payouts ```typescript export async function listPayouts(connectionId: string) { const payouts = await sdk.payout.listPaymentPayouts({ connectionId, limit: 10, }); return payouts; // PaymentPayout[] } ``` --- ## Step 8: Listing Subscriptions ```typescript export async function listSubscriptions(connectionId: string) { const subs = await sdk.subscription.listPaymentSubscriptions({ connectionId, limit: 10, }); return subs; // PaymentSubscription[] } ``` --- ## Step 9: Example Usage Here's how you might use these functions in your payment workflow: ```typescript async function main() { // 1. Create a payment const payment = await createPayment(CONNECTION_STRIPE!, 1000, "USD"); // 2. List payments const payments = await listPayments(CONNECTION_STRIPE!); // 3. Create a payment link const link = await createPaymentLink(CONNECTION_STRIPE!, 1000, "USD"); // 4. List links const links = await sdk.link.listPaymentLinks({ connectionId: CONNECTION_STRIPE!, limit: 10 }); // 5. List refunds const refunds = await listRefunds(CONNECTION_STRIPE!); // 6. List payouts const payouts = await listPayouts(CONNECTION_STRIPE!); // 7. List subscriptions (creation may not be implemented for some providers) const subscriptions = await listSubscriptions(CONNECTION_STRIPE!); console.log("Payment:", payment); console.log("Payments count:", payments.length); console.log("Payment Link URL:", link.url); console.log("Links count:", links.length); console.log("Refunds count:", refunds.length); console.log("Payouts count:", payouts.length); console.log("Subscriptions count:", subscriptions.length); } main(); ``` --- Happy building! 🎉 ## How to Build an E-Commerce Product Integration with Unified URL: https://docs.unified.to/guides/how_to_build_an_e_commerce_product_integration_with_unified # How to Build an E-Commerce Product Integration with Unified ------ _September 8, 2025_ Unified makes it possible for developers to build ecom products that work with your end customers preferred ecom platforms! With our Unified Commerce API, you can connect to dozens of ecom systems (like Shopify, BigCommerce, WooCommerce and more). This article will give you knowledge on how to create and list products, and to even create product images from the product description using Unified's GenAI integration. ## Requirements - Node.js (v1 - Unified account with ecom integration - Unified API key - Your end customer's e-commerce connection ID - Unified GenAI connection ID ## Step 1: Setting up your project ```bash mkdir ecommerce-demo cd ecommerce-demo npm init -y npm install @unified-api/typescript-sdk dotenv ``` Add your credentials to `.env`: ```shell UNIFIED_API_KEY=your_unified_api_key CONNECTION_SHOPIFY=your_customer_shopify_connection_id CONNECTION_GENAI=your_genai_connection_id ``` --- ## Step 2: Initialize the SDK ```typescript import 'dotenv/config'; import { UnifiedTo } from '@unified-api/typescript-sdk'; const { UNIFIED_API_KEY, CONNECTION_SHOPIFY, CONNECTION_GENAI } = process.env; const sdk = new UnifiedTo({ security: { jwt: UNIFIED_API_KEY! }, }); ``` --- ## Step 3: How to Get Your Customer's Connection ID Before you can create or list products, your customer must authorize your app to access their e-commerce store via Unified's  authentication flow. Once authorized, you'll receive a **connection ID** for that customer's integration (e.g., Shopify). Store this connection ID securely as shown above and use it in all API calls for that customer. See the [full list of supported integrations](https://unified.to/commerce). --- ## Step 4: Generate a Product Image from Description (with GenAI) You can use Unified GenAI to generate a product image based on the product description. ```typescript export async function generateProductImage(description: string) { const prompt = `Generate a high-quality product image for the following product description: ${description}`; const result = await sdk.genai.createGenaiPrompt({ connectionId: CONNECTION_GENAI!, prompt: { messages: [{ role: "USER", content: prompt }], maxTokens: 1024, temperature: 0.7, responseFormat: "image_url", // or whatever the GenAI API expects for image output }, }); return result.choices?.[0]?.message?.content || ""; } ``` _Explanation:_ - This function sends the product description to Unified GenAI and gets back an image URL. - You can use this image URL when creating the product in Shopify (or any other e-commerce platform). --- ## Step 5: Create a Product (with AI-generated Image) Here's how to create a product in Shopify, using the generated image. ```typescript export async function createProduct(connectionId: string, name: string, description: string, price: number) { // 1. Generate product image const imageUrl = await generateProductImage(description); // 2. Create the product const createProductResult = await sdk.commerce.createCommerceItem({ connectionId, commerceItem: { name, description, price, media: imageUrl ? [{ url: imageUrl }] : [], isActive: true, // Add any other fields as needed (e.g., tags, variants, metadata) }, }); return createProductResult; // commerceItem } ``` _Explanation:_ - Generates an image from the description. - Creates a product in Shopify (or any Unified-supported e-commerce platform) with the image and other details. --- ## Step 6: List Products You can list all products for a customer's e-commerce connection: ```typescript export async function listProducts(connectionId: string) { const productsResult = await sdk.commerce.listCommerceItems({ connectionId, limit: 10, }); return productsResult; // commerceItem[] } ``` _Explanation:_ - Fetches the first 10 products for the customer's store. - You can filter, paginate, or sort as needed. --- ## Step 7: Example Usage Here's how you might use these functions in your product workflow: ```typescript async function main() { const name = "AI-Generated T-Shirt"; const description = "A stylish t-shirt with a unique, AI-generated design."; const price = 29.99; // 1. Create the product with an AI-generated image const product = await createProduct(CONNECTION_SHOPIFY!, name, description, price); // 2. List products const products = await listProducts(CONNECTION_SHOPIFY!); console.log("Created product:", product); console.log("All products:", products); } main(); ``` --- ## How to build an invoicing system with Unified.to URL: https://docs.unified.to/guides/how_to_build_an_invoicing_system_with_unified # How to build an invoicing system with Unified.to ------ _January 3, 2024_ When building a SaaS app for invoicing, you'll need to exchange data within your customers' accounting app of choice. In this guide, we'll cover the basics of how to access the data you need for your invoicing app with the help of [Unified.to](https://unified.to/)'s unified accounting API. ## Prerequisites Before starting, be sure to review our [Getting Started with Unified](https://unified.to/blog/start_here_getting_started_with_unified) article. It will walk you through registering for a free account and onboarding. Once you have completed the onboarding steps, go ahead and as you like. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_build_an_invoicing_system_with_unified-0.png) The next step is to deploy the [Unified.to](https://unified.to/) Embedded Authorization widget in your product's user interface (UI). Check out our to learn more. ## Overview Here are the steps that you will need to support to build your invoicing app: 1. create an invoice 2. pay an invoice or cancel an invoice Let's go into more detail for each. ## Step 1: Create an invoice To generate an invoice, gather specific details from your end user, including essential information like the amount, line items/products, due date, and customer details. [Unified.to](https://unified.to/) can help with two of those values: customer and product. **Customers** To get a list of existing customers from your end user's accounting system, simply call the `listCustomers` [API endpoint](https://docs.unified.to/accounting/customer/List_all_customers). ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); export async function getCustomers(connectionId: string) { return await sdk.accounting.listAccountingContacts({ connectionId, }); } ``` The connection_id comes in from your end user's authorization of their accounting app and is stored on your end. You can watch our to learn more. If you need to create a new customer, use our `createCustomer` [API endpoint](https://docs.unified.to/accounting/customer/Create_a_customer). A `Customer` will have specific fields such as `name` , `emails`, `billing_address`, and tax information that will be relevant to your software. See our [API Documentation page](https://docs.unified.to/accounting/customer/model) for more information. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_build_an_invoicing_system_with_unified-1.png) **Products** Much like getting a list of existing customers, it is easy to get a list of existing products in your end user's accounting app using the `listItems` API endpoint. We call a product, an `Item`, and you can find the full data-model on our [API Documentation page](https://docs.unified.to/accounting/item/model). ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); export async function getItems(connectionId: string) { return await sdk.commerce.listCommerceItems({ connectionId, }); } ``` **Chart of accounts & tax rates** Each line item in an invoice will be associated with a customer, item, account and tax rate in your end user's accounting application. You can get a list of accounts by calling our `listAccounts` [API endpoint](https://docs.unified.to/accounting/account/List_all_accounts) which returns a list from their 'Chart of Accounts'. You can also get a list of tax rates by calling our `listTaxrates` [API endpoint](https://docs.unified.to/accounting/taxrate/List_all_taxrates). **Invoices** Once the end user has created an invoice, you will want to send that invoice into their accounting system as well. This is easily accomplished by using the `createInvoice` [API endpoint](https://docs.unified.to/accounting/invoice/Create_a_invoice). ```typescript import { UnifiedTo } from '@unified-api/typescript-sdk'; import { AccountingInvoice } from '@unified-api/typescript-sdk/dist/sdk/models/shared'; const sdk = new UnifiedTo({ security: { jwt: '', }, }); export async function createInvoice(connectionId: string, invoice: AccountingInvoice) { const result = await sdk.accounting.createAccountingInvoice({ accountingInvoice: invoice, connectionId, }); return result.accountingInvoice?.id; } ``` ## Step 2: Paying invoices Once that invoice is paid, you will need to communicate this to the accounting application. This is accomplished by using the `createPayment` [API endpoint](https://docs.unified.to/accounting/payment/Create_a_payment) which associates a payment with an invoice. At a minimum, a `Payment` will need an `invoice_id` , a `customer_id` and an `amount`. Some integrations may require additional values to be present, so check that [integration's feature support page](https://app.unified.to/integrations/quickbooks?tab=support). For example, Quickbooks also requires a `currency` field. ## Conclusion It's easy to support multiple accounting applications in your invoicing app and allow your end user to seamlessly move data between the two. By using Unitied.to's [Unified Accounting API](https://unified.to/accounting) you can add accounting integrations to your product and support your invoicing use case within days. ## Additional Resources - [Unified Accounting API](https://unified.to/accounting) - [API Documentation for the Unified Accounting API](https://docs.unified.to/accounting/overview) - [SDKs for Unified.to](https://docs.unified.to/overview/sdks) - - ## How to build Enterprise Search using RAG URL: https://docs.unified.to/guides/how_to_build_enterprise_search_using_rag # How to build Enterprise Search using RAG ------ _November 19, 2025_ In this article, we will be going into detail on how to build an enterprise search application as well as a Q&A bot using a RAG pipeline with Unified's data integrations and OpenAI embedding models. [RAG](https://developer.nvidia.com/blog/rag-101-demystifying-retrieval-augmented-generation-pipelines/) or Retrieval-Augmented-Generation is a powerful approach that can be leveraged to take a large amount of information and index it efficiently so that it can be searched quickly. This design pattern is very beneficial for enterprise search, Q&A bots, or any agent. A Q&A bot can be used to answer questions across a company's knowledge base, while an enterprise search application can be used to search corporate information from any internal data source. We will be using a few of Unified.to's integrations to access internal company knowledge in [file storage](https://unified.to/storage) (eg. Google Drive) and [knowledge management systems](https://unified.to/kms) (eg. Notion), embed their content using [OpenAI's models](https://unified.to/genai), and then store them in a vector DB and used to answer questions. ## **Prerequisites** - Node.js 19+ - Unified account with integrations enabled for Google Drive and Notion - Unified.to API key and GenAI Connection ID - OpenAI API key (As we are using OpenAI for embedding) - A vector DB (eg. Pinecone, or Chroma. For the demo we are using a in memory vector store) ## **Step 1: Setting your project up** ```typescript mkdir rag-bot cd rag-bot npm init -y npm install dotenv @unified-api/typescript-sdk ``` Add your credentials and keys to the `.env`: ```plain text UNIFIED_API_KEY=your_unified_api_key CONNECTION_GOOGLEDRIVE=your_gdrive_connection_id CONNECTION_NOTION=your_notion_connection_id CONNECTION_GENAI=your_genai_connection_id ``` ## **Step 2: Initialize the SDK** ```typescript import 'dotenv/config'; import { UnifiedTo } from '@unified-api/typescript-sdk'; const { UNIFIED_API_KEY, CONNECTION_GOOGLEDRIVE, CONNECTION_NOTION, CONNECTION_GENAI } = process.env; const sdk = new UnifiedTo({ security: { jwt: UNIFIED_API_KEY! }, }); ``` ## **Step 3: Fetch all the files from Google Drive** We will now use Unified's storage API to retrieve files from a Google Drive. ```typescript async function getAllGoogleDriveFiles(connectionId: string) { const files: any[] = []; let offset = 0; const limit = 100; let hasMore = true; while (hasMore) { const response = await sdk.storage.listStorageFiles({ connectionId, offset, limit, }); pages.push(...(response.files || [])); hasMore = (response.files?.length || 0) === limit; offset += limit; } return files; } const file = await sdk.storage.getStorageFile({ connectionId, id: fileId }); const downloadUrl = file.download_url; const content = await fetch(downloadUrl).then(res => res.text()); return content; } ``` ## **Step 4: Fetch Notion Pages** Now that we have the data from Google Drive, we will use Unified's connection to Notion to fetch data from Notion as well. ```typescript async function getAllNotionPages(connectionId: string) { const pages: any[] = []; let offset = 0; const limit = 100; let hasMore = true; while (hasMore) { const response = await sdk.storage.listStorageFiles({ connectionId, offset, limit, }); files.push(...(response.files || [])); hasMore = (response.files?.length || 0) === limit; offset += limit; } return pages; } async function getNotionPageContent(connectionId: string, pageId: string) { const page = await sdk.kms.getKmsPage({ connectionId, id: pageId, }); const content = await fetch(page.download_url).then(r => r.text()); return content; } ``` ## **Step 5: Building the vector DB** For the purpose of the demo, we will be creating a simple in memory vector store. But for production, you would use a cloud-based vector database like Pinecone, Chroma or Weaviate. ```typescript type Document = { id: string; source: string; content: string; embedding?: number[] }; const vectorDB: Document[] = []; ``` ## **Step 6: Embedding the documents** For the purpose of the example, we will be using OpenAI as the embedding provider in this step. Unified has [many integrations](https://unified.to/genai) that support embedding, but you can also use a single provider's SDK. Please remember to chunk the content properly and not embed the entire content at once. ### Chunking strategies: Chunking large documents effectively is super important to maintain context and relevance. Here are a few best practices for chunking: 1. **Semantic Chunking**: Instead of splitting by fixed lengths, break the document into chunks based on natural language boundaries, like paragraphs or sections, ensuring each chunk is coherent and meaningful. 2. **Overlap Between Chunks**: Include a slight overlap between adjacent chunks. This helps maintain continuity and context, especially for complex ideas that span multiple chunks. 3. **Fixed-Length Chunking**: If you use fixed-length chunking, aim for a length that balances between too small (losing context) and too large (losing efficiency). Typically, a few hundred tokens per chunk works well. 4. **Using Document Structure**: If your document has headings, subheadings, or other structural elements, use those to guide the chunking. This ensures that each chunk is logically complete. 5. **Contextual Embeddings**: Sometimes, you can embed the entire document first and then create embeddings for chunks that are derived from the document's embedding, ensuring that each chunk stays contextually relevant. 6. **Pre-processing and Cleaning**: Before chunking, clean up the text by removing unnecessary whitespace, special characters, or any irrelevant information to improve the quality of the embeddings. By following these strategies, you'll maintain the semantic integrity of the document and ensure that the vector database can retrieve meaningful context. There are several popular libraries that help with chunking and embedding in a streamlined way. A few notable ones include: 1. **LangChain**: This is a popular framework that integrates various language models and vector databases. It provides built-in utilities for chunking, embedding, and storing data in vector stores. 2. **Haystack**: Developed by deepset, Haystack is another robust framework for building search pipelines. It supports intelligent chunking, document retrieval, and integration with vector databases like FAISS, Milvus, and Pinecone. 3. **Transformers and Datasets from Hugging Face**: Hugging Face offers tools that can help with both embedding and chunking. You can use their Transformers library for embedding and their Datasets library to preprocess and chunk documents. 4. **SentenceTransformers**: This library is great for generating embeddings and can be combined with custom chunking logic to break documents into meaningful pieces before embedding. 5. **GPT-Index (formerly LlamaIndex)**: This library is designed to help build large language model applications and provides utilities for chunking, indexing, and querying documents effectively. These libraries often provide a lot of built-in functionality, making it easier to handle complex documents and ensure that your embeddings and vector storage are efficient and meaningful. ```typescript import { Configuration, OpenAIApi } from "openai"; const openai = new OpenAIApi(new Configuration({ apiKey: process.env.OPENAI_API_KEY })); async function embedText(text: string): Promise { const response = await openai.createEmbedding({ model: "text-embedding-3-large", //You can choose to use text-embedding-3-small too input: text, }); return response.data.data[0].embedding; } ``` ## **Step 7: Indexing Everything** Now that we have all the data collected, it is time to index all of it! When you store vectors in a vector database like Pinecone or Weaviate, you can attach `metadata` to each vector. This metadata can include things like a unique ID, the original text, source information, or any other relevant attributes. When you perform a similarity search and get the top matching vectors, the database returns the metadata along with the vectors. That way, you can easily link back to the original content. For instance, when you index a piece of text, you'd store the vector along with metadata like a document ID, the title, or even the source URL. When you retrieve the nearest neighbors, the metadata is returned, and you can use it to identify or fetch the original content from your database or any external source. ```typescript async function indexAllContent() { // Google Drive const gdriveFiles = await getAllGoogleDriveFiles(CONNECTION_GOOGLEDRIVE!); for (const file of gdriveFiles) { const content = await getFileContent(CONNECTION_GOOGLEDRIVE!, file.id); const embedding = await embedText(content); vectorDB.push({ id: file.id, source: "gdrive", content, embedding, filename: file.name, date_created: file.created_at, owner_id: file.user_id, }); } // Notion const notionPages = await getAllNotionPages(CONNECTION_NOTION!); for (const page of notionPages) { const content = await getNotionPageContent(CONNECTION_NOTION!, page.id); const embedding = await embedText(content); vectorDB.push({ id: page.id, source: "notion", content, embedding }); } } ``` ## **Step 8: Finding Answers** Now that we have the data ready, it is time to implement the Bot or enterprise search that will use this data to answer questions from a user. The basic flow would be: 1. Embed the question asked 2. Find similar docs in the Vector DB 3. Send the top `n` amount of docs as context paired with the question to Unified's GenAI endpoint. 4. Return the answer to the user. For the purpose of the example, we will be sending the top 3 most similar docs. ```typescript function cosineSimilarity(a: number[], b: number[]) { const dot = a.reduce((sum, ai, i) => sum + ai * b[i], 0); const normA = Math.sqrt(a.reduce((sum, ai) => sum + ai * ai, 0)); const normB = Math.sqrt(b.reduce((sum, bi) => sum + bi * bi, 0)); return dot / (normA * normB); } async function answerQuestion(question: string) { const questionEmbedding = await embedText(question); // For the purpose of the example we will be sending the top 3 docs const scored = vectorDB.map(doc => ({ ...doc, score: cosineSimilarity(questionEmbedding, doc.embedding!) })); const topDocs = scored.sort((a, b) => b.score - a.score).slice(0, 3); const context = topDocs.map(doc => doc.content).join('\n---\n'); const prompt = ` You are a helpful company knowledge bot. Use the following context to answer the question. Context: ${context}Question: ${question}Answer in a concise and accurate way. `; const result = await sdk.genai.createGenaiPrompt({ connectionId: CONNECTION_GENAI!, prompt: { messages: [{ role: "USER", content: prompt }], maxTokens: 256, temperature: 0.2 } }); return result.choices?.[0]?.message?.content || "No answer generated."; } ``` ## **Step 9: Usage** ```typescript (async () => { await indexAllContent(); const answer = await answerQuestion("How do I request vacation days?"); console.log("Bot answer:", answer); })(); ``` At Unified, most of our customers are using this simple RAG Pipeline flow to add AI-powered intelligence to their application. It can be used across industries and verticals as Unified supports 21 B2B SaaS categories and more than 350 integrations to retrieve end-customer data from. ## How to configure Bullhorn Redirect URI for creating a connection URL: https://docs.unified.to/guides/how_to_configure_bullhorn_redirect_uri_for_creating_a_connection # How to configure Bullhorn Redirect URI for creating a connection ------ _April 8, 2026_ When connecting Bullhorn to Unified, each customer must complete a one-time setup in their own Bullhorn account. ## Why this is required? Bullhorn requires every connected application to use its own API user, client ID, client secret, and approved redirect URI. The redirect URI cannot be updated through the Bullhorn web interface, so Bullhorn Support must be contacted to approve and configure the redirect URI. ## Steps your customer must complete ### 1. Create an API User in Bullhorn The customer must create a dedicated API user for the application they want to connect. During this process, Bullhorn will provide: - Client ID - Client Secret These credentials will later be used when creating the connection in Unified. **Note**: A new API user is required each time a new application is connected to Bullhorn. ### 2. Contact Bullhorn Support After creating the API user, the customer must contact Bullhorn Support and request that the redirect URI for the API user be updated. The customer should provide: - The API user or application they created - The Unified redirect URI Example request: Please update the redirect URI for our Bullhorn API user to the following Unified redirect URI: **`https://api.unified.to/oauth/code`** **Note**: if you are using CNAME (custom domain) the redirect URI will look like this **`https://YOUR_CUSTOM_DOMAIN/oauth/code`** ### 3. Wait for Bullhorn to approve and apply the Redirect URI change Bullhorn does not currently allow redirect URIs to be configured through the Bullhorn web UI. Because of this, the redirect URI change must be completed by Bullhorn Support before the connection can work. ### 4. Create the Connection in Unified Once Bullhorn Support confirms that the redirect URI has been configured on your customer's account, you can then proceed with creating a Bullhorn connection. ## Important Notes - Every customer must request the redirect URI change individually for their own Bullhorn account. - This is a Bullhorn limitation and cannot be configured directly in the Bullhorn UI or programmatically via Unified. - This is a one-time setup for each of your customers using Bullhorn - If the customer creates a new API user in the future, they will need to go through this process again. ## How to configure webhooks in HubSpot URL: https://docs.unified.to/guides/how_to_configure_webhooks_in_hubspot # How to configure webhooks in HubSpot ------ _February 23, 2024_ This guide walks you through how to configure webhooks in HubSpot to work with Unified.to. These webhooks allow you to get notifications about new events and changes from HubSpot e.g. new leads, updated deals, or deleted contacts. ## Before you begin - Make sure you have a HubSpot developer account. - Get your client ID, client secret, and developer API key. - Point the **Redirect URL** in your HubSpot app to `api.unified.to/oauth/code` - If you're using the EU data region, use instead`api-eu.unified.to/oauth/code` - Follow our [HubSpot developer guide](https://docs.unified.to/guides/how_to_register_a_hubspot_developer_app_and_get_your_oauth_2_credentials#how-to-register-a-hubspot-developer-app-and-get-your-oauth-2-credentials) to see how to do all of the above. ## Setting up Webhooks in Hubspot Legacy Apps ### Select the appropriate Legacy App to setup the webhook ![Screenshot_2025-11-25_at_12.21.18_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_configure_webhooks_in_hubspot-0.png) ### Set the target URL for the webhook HubSpot uses a single webhook URL per app, which means you need to configure your app to send any events to the Unified.to servers. 1. Navigate to your [HubSpot developer account](https://app.hubspot.com/). 2. Click **Apps.** 3. Select the app you wish to use to access your customers' data. 4. Click **Webhooks.** ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_configure_webhooks_in_hubspot-1.png) 5. Under **Target URL**: 1. If you are using the US data region, copy and paste in: `api.unified.to/webhook/workspace/hubspot` 2. If you using the EU data region, copy and paste in: `api-eu.unified.to/webhook/workspace/hubspot` If your URL is greyed out and you have subscriptions under **Event subscriptions**, then you'll need to unsubscribe from all of them. _WARNING: If these subscriptions and the target URL are used by other applications, this action will break them. In that case, you may want to consider creating a new developer app to use with Unified.to._ 6. You do **not** have to create any subscriptions manually. Unified.to will do that on your behalf. ### Add scopes for your webhooks In order for Unified.to to receive webhook data from HubSpot, you need to add scopes for the events you are interested in as well as a few others. 1. With your app page still open, click **Basic Info** and then click **Auth.** 2. Scroll down to **Scopes**: 1. Add `crm.objects.owners.read`as **Required**. 2. For any events where you want to read data, add them as **Required.** 1. Note: This applies to events that you'll be subscribed to as well as those that you will only call via the API. For example, if you want to create a Contacts webhook and also fetch Companies via the API, you'll need to add both `crm.objects.contacts.read` and `crm.objects.companies.read` as **Required** because Unified.to will pass all of these as required scopes in each auth flow. 3. For any events where you want to write data, add them as **Optional**. 4. For example, if you want to receive notifications about updates to HubSpot companies, you'll need to add `crm.objects.companies.read` as **Required**. If you want to write data to companies, add `crm.objects.companies.write` as **Optional**. 5. To see the full list of HubSpot scopes that Unified.to supports, see: [https://app.unified.to/integrations/hubspot?tab=oauth2](https://app.unified.to/integrations/hubspot?tab=oauth2). 3. The `oauth` scope is also required for all HubSpot integrations. This will be added by default if your app was created after April 2024. ![Screenshot_2024-08-26_at_2.35.36_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_configure_webhooks_in_hubspot-2.png) ### Select permission scopes on Unified.to When prompting your customers to authorize access to their third-party accounts, you will need to select the corresponding scopes in [Unified.to](https://unified.to/) as well. The following steps show you how to do that when using our Embedded Authorization component. 1. On app.unified.to, click on **Settings** and then [**Embedded Authorization**](https://app.unified.to/settings/embed?tab=Authorization)**.** 2. You will see a preview of the Embedded Authorization component on the left and a list of configuration options on the right. Click on **Options**. 3. Scroll down to **Permission scopes** and select **webhook** as well as any other permission scopes that you need e.g. the same scopes you added in HubSpot. You do not need to select `oauth` or `crm.objects.owners.read` on [Unified.to](https://unified.to/) as well will map these automatically on our end. 1. For example, using the examples from above for reading Contacts and Companies, the scopes in Unified.to would be `crm_contact_read` and `crm_company_read` ![c9a0cf8d-eca3-4b74-8d52-d84a2fb6029f.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_configure_webhooks_in_hubspot-3.png) 2. A mapping of Unified scopes to their HubSpot counterparts can be found [here](https://app.unified.to/integrations/hubspot?tab=oauth2). 3. Tip: Under **Integration categories,** select **CRM** to only see scopes related to CRM data objects. ## Setting up Webhooks in Hubspot Development Platform ### Create Webhook configuration file In your hubspot project folder, create a new folder `webhook` and inside add in the `webhook-hsmeta.json` file. ```javascript project-folder/ └── src/ └── app/ ├── app-hsmeta.json └── webhooks/ └── webhook-hsmeta.json ``` ### **Setup Webhook configuration** Now that you have the webhook config file ready, let's setup the configuration as follows: ```javascript { "uid": "webhooks", "type": "webhooks", "config": { "settings": { "targetUrl": "https://api.unified.to/webhook/workspace/hubspot", "maxConcurrentRequests": 10 }, "subscriptions": { "legacyCrmObjects": [ { "subscriptionType": "deals.propertyChange", "propertyName": "dealstage", "active": true }, { "subscriptionType": "deals.associationChange", "active": true } ] } } } ``` For the `targetUrl` : 1. If you are using the US data region, copy and paste in: `api.unified.to/webhook/workspace/hubspot` 2. If you using the EU data region, copy and paste in: `api-eu.unified.to/webhook/workspace/hubspot` For the `subscriptions` we need to use the `legacyCrmObjects` to define which object we want to get events for. ### Deploy Hubspot Project Once you've setup your webhook configuration, you can now upload the project via: ```javascript hs project upload ``` Once uploaded, your configuration should now show up in your app's webhook list: ![Screenshot_2025-11-25_at_12.34.02_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_configure_webhooks_in_hubspot-4.png) ## See also - [Introduction to webhooks](https://docs.unified.to/concepts/webhooks#introduction-to-webhooks) - [Create a webhook subscription](https://docs.unified.to/unified/webhook/Create_webhook_subscription) - [How to set up your scopes in HubSpot](https://docs.unified.to/guides/how_to_set_up_your_scopes_in_hubspot) ## How to Connect Anthropic to Real-Time SaaS Data with Unified.to MCP Server URL: https://docs.unified.to/guides/how_to_connect_anthropic_to_real_time_saas_data_with_unified_mcp_server # How to Connect Anthropic to Real-Time SaaS Data with Unified.to MCP Server ------ _August 28, 2025_ Anthropic's Claude models can power sophisticated workflows — but most products still need access to live customer data from CRMs, ATSs, HRIS, or accounting systems. That usually means writing brittle glue code, normalizing APIs, and maintaining webhook jobs. With Unified.to's MCP server, you can skip that complexity. Unified.to exposes 317+ SaaS integrations as real-time, callable tools that Claude can use directly — no custom integration logic required. In this guide, we'll walk through how to connect Anthropic to Unified.to MCP so your application can give Claude real-time access to customer SaaS data and actions. ## Authentication Every request to the Unified.to MCP server must include a token. You can pass this either as a URL parameter (`?token=...`) or in the `Authorization: bearer {token}` header. There are two authentication flows: - **Private (workspace key + connection)** Use your Unified.to workspace API key and add a `connection` parameter. This should never be exposed publicly. - **Public (end-user safe)** Generate a token in the format `{connectionID}-{nonce}-{signature}` using your workspace secret. Safe to share with customers. ## Integrating with Anthropic API **Manual Tool Orchestration:** 1. Call Unified's `/tools` endpoint and pass the tool list to Claude: ```python resp = client.messages.create( model="claude-3-5-sonnet-20241022", tools=tools, input="list the candidates and then analyse the resumes from their applications", ) ``` 2. Claude will return a `tool_use` block: ```json [ { "type": "tool_use", "id": "toolu_...", "name": "list_candidates", "input": { "limit": "100" } } ] ``` 3. Call Unified's `/tools/{id}/call` endpoint with the arguments. 4. Return the result to Claude as a `tool_result` block in your next message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_...", "content": "..." } ] ``` ## Controlling Tool Access Unified.to MCP gives you granular control over how tools are exposed to Anthropic: - `permissions` → restrict available scopes. - `tools` → allowlist specific tool IDs. - `aliases` → add synonyms so Claude better matches tool names. - `hide_sensitive=true` → automatically strip PII (emails, phone numbers, etc). - `include_external_tools=true` → expose all vendor API endpoints, not just Unified.to's normalized models. These options help you keep Anthropic outputs predictable and secure in production-grade workflows. ### Sample Snippet ```javascript import Anthropic from '@anthropic-ai/sdk'; // Claude model version const modelVersion = 'latest'; // Unified connection Id that you want to have the mcp to use tools for const connection = 'UNIFIED_CONNECTION_ID'; // location from where your unified account was created const dc = 'us'; // Optional: list of specific tools that you want to use const toolIds = ['get_messaging_message','list_messaging_message'] const params = new URLSearchParams({ token: process.env.UNIFIED_API_KEY || '', connection, dc, include_external_tools: includeExternal ? 'true' : 'false', }); if (toolIds.length > 0) { params.append('tools', toolIds.join(',')); } const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, }); const serverUrl = `${process.env.UNIFIED_MCP_URL}/sse?${params.toString()}`; let latestModel; if (modelVersion === 'latest') { // get the latest model from anthropic const models = await anthropic.models.list(); latestModel = models.data[0].id; } else { latestModel = modelVersion; } const completion = await anthropic.beta.messages.create({ model: latestModel, max_tokens: 1024, messages: [ { role: 'user', content: message, }, ], stream: false, mcp_servers: [ { type: 'url', url: `${process.env.UNIFIED_MCP_URL}/sse?${params.toString()}`, // change url as needed name: 'unifiedMCP', }, ], betas: ['mcp-client-2025-04-04'], }); console.log('response', JSON.stringify(completion, null, 2)); ``` ## Coverage and Infrastructure Unified.to MCP is built for real-world AI use cases: - **20,421+ real-time tools** (growing weekly) - **335+ integrations across 21 categories** (ATS, CRM, HRIS, Accounting, Messaging, File Storage, and more) - **Zero-storage architecture** — no caching, no liability - **Scoped security controls** — permissions, aliases, and PII redaction - **Multi-region deployment** — US, EU, and AU data centers for compliance Unified.to MCP works across all major LLM providers: **OpenAI, Anthropic, Google Gemini, and Cohere.** That means you can build once and connect to any agent client. [Explore the MCP docs](https://docs.unified.to/mcp?utm_source=chatgpt.com) or [book a demo](https://docs.unified.to/mcp?utm_source=chatgpt.com) > _Note: Unified.to MCP is currently in beta and should not be used in production systems yet. Contact us if you'd like to explore production use._ ## How to Connect Cohere to Real-Time SaaS Data with Unified.to MCP Server URL: https://docs.unified.to/guides/how_to_connect_cohere_to_real_time_saas_data_with_unified_mcp_server # How to Connect Cohere to Real-Time SaaS Data with Unified.to MCP Server ------ _August 28, 2025_ Cohere's LLMs can power rich workflows — but most products still need access to live customer data from CRMs, ATSs, HRIS, or accounting systems. That usually means writing brittle glue code, normalizing APIs, and maintaining webhook jobs. With Unified.to's MCP server, you can skip that complexity. Unified.to exposes 317+ SaaS integrations as real-time, callable tools that Cohere can use directly — no custom integration logic required. In this guide, we'll walk through how to connect Cohere to Unified.to MCP so your application can give Cohere real-time access to customer SaaS data and actions. ## Authentication Every request to the Unified.to MCP server must include a token. You can pass this either as a URL parameter (`?token=...`) or in the `Authorization: bearer {token}` header. There are two authentication flows: - **Private (workspace key + connection)** Use your Unified.to workspace API key and add a `connection` parameter. This should never be exposed publicly. - **Public (end-user safe)** Generate a token in the format `{connectionID}-{nonce}-{signature}` using your workspace secret. Safe to share with customers. ## Integrating [Unified.to](https://unified.to/) MCP Server with Cohere Cohere's chat API can directly consume tools defined by Unified.to MCP. **Manual Tool Orchestration:** 1. Call Unified's `/tools?type=cohere` endpoint and pass the tool list to Cohere: ```python response = co.chat( model="command-a-03-2025", messages=messages, tools=tools ) ``` 2. When Cohere requests a tool call, call Unified's `/tools/{id}/call` endpoint. 3. Pass the tool result back to Cohere in your next message ### Example Prompt and Response **Prompt:** ```plain text "Score this candidate for the Software Engineer job." ``` **What happens:** - Cohere discovers the available tools from Unified MCP (e.g., `fetch-candidate`, `fetch-job`, `score-candidate`). - Cohere calls `fetch-candidate` and `fetch-job` tools to get the data. - Cohere calls `score-candidate` with the data. - Cohere returns a response like: **Response:** ```plain text Candidate Jane Doe scored 92/100 for the Software Engineer job. Strengths: Python, distributed systems. Recommended for interview. ``` ## Advanced MCP Options Unified.to MCP gives you granular control over how tools are exposed to Cohere: - `permissions` → restrict available scopes. - `tools` → allowlist specific tool IDs. - `aliases` → add synonyms so Cohere better matches tool names. - `hide_sensitive=true` → automatically strip PII (emails, phone numbers, etc). - `include_external_tools=true` → expose all vendor API endpoints, not just Unified.to's normalized models. These options help you keep Cohere outputs predictable and secure in production-grade workflows. ### Sample Snippet ```javascript import { CohereClientV2 } from 'cohere-ai'; // Unified connection Id that you want to have the mcp to use tools for const connection = 'UNIFIED_CONNECTION_ID'; // location from where your unified account was created const dc = 'us'; // Optional: list of specific tools that you want to use const toolIds = ['get_messaging_message','list_messaging_message'] const cohereClient = new CohereClientV2({ token: process.env.COHERE_API_KEY || '', }); const params = new URLSearchParams({ token: process.env.UNIFIED_API_KEY || '', connection, type: 'cohere', dc, include_external_tools: includeExternal ? 'true' : 'false', }); if (toolIds.length > 0) { params.append('tools', toolIds.join(',')); } const tools = await fetch(`${process.env.UNIFIED_MCP_URL}/tools?${params.toString()}`); const toolsJson = await tools.json(); const completion = await cohereClient.chat({ model: 'command-a-03-2025', messages: [ { role: 'user', content: message, }, ], tools: toolsJson, }); for (const toolCall of completion?.message?.toolCalls || []) { // call mcp server with toolCallId const toolCallResponse = await fetch(`${process.env.UNIFIED_MCP_URL}/mcp/tools/${toolCall.function?.name}/call?${params.toString()}`, { method: 'POST', body: toolCall.function?.arguments || '{}', }); const toolCallResponseJson = await toolCallResponse.json(); console.log(JSON.stringify(toolCallResponseJson, null, 2)); } ``` ## The Most Complete MCP Server for AI-Native Teams Unified.to MCP is the most complete hosted MCP server available today: - **20,082+ real-time tools** (growing weekly) - **335+ integrations across 21 categories** (ATS, CRM, HRIS, Accounting, Messaging, File Storage, and more) - **Zero-storage architecture** — no caching, no liability - **Scoped security controls** — permissions, aliases, and PII redaction - **Multi-region deployment** — US, EU, and AU data centers for compliance This ensures your Cohere workflows aren't just demos — they're secure, scalable, and designed for production-grade use cases. ## Next Steps Unified.to MCP works across all major LLM providers: **OpenAI, Anthropic, Google Gemini and Cohere.** That means you can build once and connect to any agent client. [Explore the MCP docs](https://docs.unified.to/mcp?utm_source=chatgpt.com) or [book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified?utm_source=chatgpt.com) to see it live in action. > _Note: Unified.to MCP is currently in beta and should not be used in production systems yet. Contact us if you'd like to explore production use._ ## How to Connect Google Gemini to Real-Time SaaS Data with Unified.to MCP Server URL: https://docs.unified.to/guides/how_to_connect_google_gemini_to_real_time_saas_data_with_unified_mcp_server # How to Connect Google Gemini to Real-Time SaaS Data with Unified.to MCP Server ------ _August 28, 2025_ Google Gemini can power powerful workflows — but most products still need access to **live customer data** from CRMs, ATSs, HRIS, or accounting systems. That usually means writing brittle glue code, normalizing APIs, and maintaining webhook jobs. With **Unified.to's MCP server**, you can skip that complexity. Unified.to exposes 317+ SaaS integrations as **real-time, callable tools** that Gemini can use directly — no custom integration logic required. In this guide, we'll walk through how to connect Gemini to Unified.to MCP so your application can give Gemini real-time access to customer SaaS data and actions. ## Authentication Every request to the Unified.to MCP server must include a token. You can pass this either as a URL parameter (`?token=...`) or in the `Authorization: bearer {token}` header. There are two authentication flows: - **Private (workspace key + connection)** Use your Unified.to workspace API key and add a `connection` parameter. This should never be exposed publicly. - **Public (end-user safe)** Generate a token in the format `{connectionID}-{nonce}-{signature}` using your workspace secret. Safe to share with customers. ## Integrating with Google Gemini API **Manual Tool Orchestration:** 1. Call Unified's `/tools` endpoint and pass the tool list as `function_declarations` to Gemini. 2. Gemini will return a function call request: ```plain text function_call { name: "list_candidates" args { fields { key: "limit" value { string_value: "100" } } } } ``` 3. Call Unified's `/tools/{id}/call` endpoint. 4. Respond to Gemini with the tool result as a `functionResponse` in your next message: ```json { "role": "user", "parts": [ { "functionResponse": { "name": "list_candidates", "response": { ... } } } ] } ``` ## Controlling Tool Access Unified.to MCP gives you granular control over how tools are exposed to Gemini: - `permissions` → restrict available scopes. - `tools` → allowlist specific tool IDs. - `aliases` → add synonyms so Gemini can perform better matches for tool names. - `hide_sensitive=true` → automatically strip PII (emails, phone numbers, name, gender, etc). - `include_external_tools=true` → expose all vendor API endpoints, not just Unified.to's normalized models. These options help you keep Gemini outputs predictable and secure in production-grade workflows. ### Sample Snippet: ```javascript import { GoogleGenAI, mcpToTool } from '@google/genai'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; // Construct a new GEMINI client via GoogleGenAI // Gemini model version const modelVersion = 'latest'; // Unified connection Id that you want to have the mcp to use tools for const connection = 'UNIFIED_CONNECTION_ID'; // location from where your unified account was created const dc = 'us'; // Optional: list of specific tools that you want to use const toolIds = ['get_messaging_message','list_messaging_message'] const gemini = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY || '', }); // Construct the required params const params = new URLSearchParams({ token: process.env.UNIFIED_API_KEY || '', connection, type: 'gemini', dc, include_external_tools: true, // set to false if you don't want to include external tools }); if (toolIds.length > 0) { params.append('tools', toolIds.join(',')); } const serverUrl = `${process.env.UNIFIED_MCP_URL}/mcp?${params.toString()}`; const transport = new StreamableHTTPClientTransport(new URL(serverUrl)); const client = new Client({ name: 'unified-mcp', version: '1.0.0', }); await client.connect(transport); let latestModel; // Optional if (modelVersion === 'latest') { // get the latest model from gemini const models = await gemini.models.list(); latestModel = models.page.filter((model: any) => model.name.includes('gemini') && !model.name.includes('embedding')).pop()?.name || 'gemini-2.0-flash'; } else { latestModel = modelVersion; } const completion = await gemini.models.generateContent({ model: latestModel?.replace('model/', ''), contents: message, config: { tools: [mcpToTool(client)], }, }); for (const chunk of completion?.candidates || []) { console.log(JSON.stringify(chunk, null, 2)); if (chunk.content?.parts) { for (const part of chunk.content.parts) { if (part.text) { console.log(part.text); } } } } ``` ## Coverage and Infrastructure Unified.to MCP is built for real-world AI use cases: - **20,421+ real-time tools** (growing weekly) - **335+ integrations across 21 categories** (ATS, CRM, HRIS, Accounting, Messaging, File Storage, and more) - **Zero-storage architecture** — no caching, no liability - **Scoped security controls** — permissions, aliases, and PII redaction - **Multi-region deployment** — US, EU, and AU data centers for compliance Unified.to MCP works across all major LLM providers: **OpenAI, Anthropic, Google Gemini, and Cohere.** That means you can build once and connect to any agent client. - [Explore the MCP docs](https://docs.unified.to/mcp?utm_source=chatgpt.com) - [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified?utm_source=chatgpt.com) > _Note: Unified.to MCP is currently in beta and should not be used in production systems yet. Contact us if you'd like to explore production use._ ## How to Connect LLMs to Real-Time SaaS Data with Unified.to MCP Server URL: https://docs.unified.to/guides/how_to_connect_llms_to_real_time_saas_data_with_unified_mcp_server # How to Connect LLMs to Real-Time SaaS Data with Unified.to MCP Server ------ _August 25, 2025_ [Unified's MCP](https://docs.unified.to/mcp) server allows your application to give an LLM API real-time access to your customers' SaaS data and perform actions on that data — without you having to write custom business-logic code for every integration, which can often make things messy. For example, imagine you're building a [candidate assessment workflow](https://docs.unified.to/guides/how_to_build_a_candidate_assessment_product_with_unified), we'd usually call the API to fetch the data, and feed it into the LLM for it to analyze that data. With the Unified MCP server, we can enable OpenAI (or another LLM API) to directly access the data and perform actions making the workflow much simpler and cleaner. ## The Flow Here's how an LLM API connects to Unified's MCP server: - Your app connects to the MCP server using the HTTP endpoint. - You authenticate using you [Unified.to](https://unified.to/) API token generated and provider one of your customer's connection ID. - The LLM can then discover and call tools (like 'fetch candidate', 'score candidate', 'update job status') in real time. ## Authentication You must provide a token to the MCP server as a URL parameter (`?token={token}`) or in the Authorization header (`Authorization: bearer {token}`). **Private (Direct LLM API):** Use your [Unified.to](https://unified.to/) workspace API key as the token, and include a `connection` parameter for the customer's connection ID. _Example:_ ```plain text &connection= ``` _Note: Do not expose this token publicly._ ## Building the application with an LLM API and MCP If your application is using an LLM API that supports MCP (like OpenAI), you can use the [`mcp-use`](https://github.com/mcp-use/mcp-use) Python package to connect to Unified MCP and access your customer's data and actions. ### Dependencies The full list of dependencies and setup you need to do to get things running. ```bash mkdir unified-mcp-client cd unified-mcp-client python -m venv .venv source .venv/bin/activate pip install mcp-use python-dotenv requests openai touch client.py ``` ### Setting up your secrets Add your Unified API key, your customer's connection ID, and your workspace secret to a `.env` file: ```bash echo "UNIFIED_API_KEY=" >> .env echo "CONNECTION_ID=" >> .env echo ".env" >> .gitignore ``` # The MCP Client Class Here's an example using the `mcp-use` package: ```python # client.py import os import hashlib import secrets from dotenv import load_dotenv from mcp_use import MCPClient load_dotenv() CONNECTION_ID = os.getenv("CONNECTION_ID") TOKEN = os.getenv("API_KEY") MCP_URL = f"" client = MCPClient(MCP_URL) # List available tools tools = client.list_tools() print("Available tools:", [tool['id'] for tool in tools]) # Call a tool (example: call the first tool with no arguments) if tools: tool_id = tools[0]['id'] result = client.call_tool(tool_id, {}) print("Tool result:", result) ``` ## Integrating with OpenAI (LLM API) Once your MCP client is set up, you can connect it to OpenAI's API, which natively supports remote MCP servers. ### Configure OpenAI to Use Unified MCP When you send a chat completion request, specify the MCP server as a tool source. Here's a **candidate assessment example**: ```python import openai openai.api_key = os.getenv("OPENAI_API_KEY") response = openai.chat.completions.create( model="gpt-4o", messages=[ {"role": "user", "content": "Score this candidate for the Software Engineer job."} ], tools=[{ "type": "mcp", "url": MCP_URL, # The Unified MCP URL with your token }], tool_choice="auto" ) print(response.choices[0].message.content) ``` **No backend glue code required—OpenAI orchestrates the tool calls via Unified MCP.** ## Integrating with Anthropic API **Manual Tool Orchestration:** 1. Call Unified's `/tools` endpoint and pass the tool list to Claude: ```python resp = client.messages.create( model="claude-3-5-sonnet-20241022", tools=tools, input="list the candidates and then analyse the resumes from their applications", ) ``` 2. Claude will return a `tool_use` block: ```json [ { "type": "tool_use", "id": "toolu_...", "name": "list_candidates", "input": { "limit": "100" } } ] ``` 3. Call Unified's `/tools/{id}/call` endpoint with the arguments. 4. Return the result to Claude as a `tool_result` block in your next message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_...", "content": "..." } ] ``` ## Integrating with Google Gemini API **Manual Tool Orchestration:** 1. Call Unified's `/tools` endpoint and pass the tool list as `function_declarations` to Gemini. 2. Gemini will return a function call request: ```plain text function_call { name: "list_candidates" args { fields { key: "limit" value { string_value: "100" } } } } ``` 3. Call Unified's `/tools/{id}/call` endpoint. 4. Respond to Gemini with the tool result as a `functionResponse` in your next message: ```json { "role": "user", "parts": [ { "functionResponse": { "name": "list_candidates", "response": { ... } } } ] } ``` ## Integrating with Cohere **Manual Tool Orchestration:** 1. Call Unified's `/tools?type=cohere` endpoint and pass the tool list to Cohere: ```python response = co.chat( model="command-a-03-2025", messages=messages, tools=tools ) ``` 2. When Cohere requests a tool call, call Unified's `/tools/{id}/call` endpoint. 3. Pass the tool result back to Cohere in your next message ### Example Prompt and Response **Prompt:** ```plain text "Score this candidate for the Software Engineer job." ``` **What happens:** - Your chosen LLM discovers the available tools from Unified MCP (e.g., `fetch-candidate`, `fetch-job`, `score-candidate`). - The LLM calls `fetch-candidate` and `fetch-job` tools to get the data. - The LLM calls `score-candidate` with the data. - The LLM returns a response like: **Response:** ```plain text Candidate Jane Doe scored 92/100 for the Software Engineer job. Strengths: Python, distributed systems. Recommended for interview. ``` ## How to Connect OpenAI to Real-Time SaaS Data with Unified.to MCP Server URL: https://docs.unified.to/guides/how_to_connect_openai_to_real_time_saas_data_with_unified_mcp_server # How to Connect OpenAI to Real-Time SaaS Data with Unified.to MCP Server ------ _August 28, 2025_ OpenAI's LLMs can power rich workflows — but most products still need access to **live customer data** from CRMs, ATSs, HRIS, or accounting systems. That usually means writing brittle glue code, normalizing APIs, and maintaining webhook jobs. With Unified.to's MCP server, you can skip that complexity. Unified.to exposes 317+ SaaS integrations as real-time, callable tools that OpenAI can use directly — no custom integration logic required. In this guide, we'll walk through how to connect OpenAI to Unified.to MCP so your application can give OpenAI real-time access to customer SaaS data and actions. ## Authentication Every request to the Unified.to MCP server must include a token. You can pass this either as a URL parameter (`?token=...`) or in the `Authorization: bearer {token}` header. There are two authentication flows: - **Private (workspace key + connection)** Use your Unified.to workspace API key and add a `connection` parameter. This should never be exposed publicly. - **Public (end-user safe)** Generate a token in the format `{connectionID}-{nonce}-{signature}` using your workspace secret. Safe to share with customers. ## Integrating with OpenAI (LLM API) Once your MCP client is set up, you can connect it to OpenAI's API, which natively supports remote MCP servers. ### Configure OpenAI to Use Unified MCP When you send a chat completion request, specify the MCP server as a tool source. Here's a **candidate assessment example**: ```python import openai openai.api_key = os.getenv("OPENAI_API_KEY") response = openai.chat.completions.create( model="gpt-4o", messages=[ {"role": "user", "content": "Score this candidate for the Software Engineer job."} ], tools=[{ "type": "mcp", "url": MCP_URL, # The Unified MCP URL with your token }], tool_choice="auto" ) print(response.choices[0].message.content) ``` No backend glue code required—OpenAI orchestrates the tool calls via Unified MCP. ## Advanced MCP Options Unified.to MCP gives you granular control over how tools are exposed to OpenAI: - `permissions` → restrict available scopes. - `tools` → allowlist specific tool IDs. - `aliases` → add synonyms so OpenAI better matches tool names. - `hide_sensitive=true` → automatically strip PII (emails, phone numbers, etc). - `include_external_tools=true` → expose all vendor API endpoints, not just Unified.to's normalized models. These options help you keep OpenAI outputs predictable and secure in production-grade workflows. ### Sample Snippet ```javascript import OpenAI from 'openai'; import Anthropic from '@anthropic-ai/sdk'; // OpenAI model version const modelVersion = 'latest'; // Unified connection Id that you want to have the mcp to use tools for const connection = 'UNIFIED_CONNECTION_ID'; // location from where your unified account was created const dc = 'us'; // Optional: list of specific tools that you want to use const toolIds = ['get_messaging_message','list_messaging_message'] const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY || '', }); const params = new URLSearchParams({ token: process.env.UNIFIED_API_KEY || '', type: 'openai', dc, connection, include_external_tools: includeExternal ? 'true' : 'false', }); if (toolIds.length > 0) { params.append('tools', toolIds.join(',')); } const serverUrl = `${process.env.UNIFIED_MCP_URL}/sse?${params.toString()}`; let latestModel; if (modelVersion === 'latest') { // get the latest model from open ai const models = await openai.models.list(); latestModel = models.data[0].id; } else { latestModel = modelVersion; } const completion = await openai.responses.create({ model: latestModel, tools: [ { type: 'mcp', server_label: 'unifiedMCP', server_url: serverUrl, // change url as needed require_approval: 'never', }, ], instructions: `You are a helpful assistant`, input: message, }); for await (const chunk of completion.output) { console.log('chunk', chunk); console.log(JSON.stringify(chunk, null, 2)); } ``` ## Coverage and Infrastructure Unified.to MCP is the most complete hosted MCP server available today: - **20,421+ real-time tools** (growing weekly) - **335+ integrations across 21 categories** (ATS, CRM, HRIS, Accounting, Messaging, File Storage, and more) - **Zero-storage architecture** — no caching, no liability - **Scoped security controls** — permissions, aliases, and PII redaction - **Multi-region deployment** — US, EU, and AU data centers for compliance This ensures your OpenAI workflows aren't just demos — they're secure, scalable, and designed for production-grade use cases. Unified.to MCP works across all major LLM providers: **OpenAI, Anthropic, Google Gemini, and Cohere.** That means you can build once and connect to any agent client. - [Explore the MCP docs](https://docs.unified.to/mcp?utm_source=chatgpt.com) - [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified?utm_source=chatgpt.com) > _Note: Unified.to MCP is currently in beta and should not be used in production systems yet. Contact us if you'd like to explore production use._ ## How to Connect Zoom and Zoom Phone URL: https://docs.unified.to/guides/how_to_connect_zoom_and_zoom_phone # How to Connect Zoom and Zoom Phone ------ _July 2, 2026_ This guide walks you through creating a Zoom OAuth app, configuring the right permissions, and connecting your Zoom account. You will need a Zoom account with admin access for some features (user directory, call logs, and recordings). --- ## **What you need** - A [**Zoom Marketplace**](https://marketplace.zoom.us/) developer account - Zoom account admin access (required for directory, group, and Zoom Phone admin scopes) - A Zoom Phone license if you plan to use the Zoom Phone integration - Your Unified workspace (to store credentials and start the connection) > **Note:** Zoom and Zoom Phone are separate integrations in Unified. You can use one OAuth app for both, as long as that app includes the scopes for every feature you need. --- ## **Step 1: Create a Zoom OAuth app** 1. Sign in to the [**Zoom App Marketplace**](https://marketplace.zoom.us/). 2. Go to **Develop** → **Build App**. 3. Choose **General App** and click **Create**. 4. Enter an app name and basic details. On the **Basic Information** page: 1. Open **App Credentials** and note your **Development** Client ID and Client Secret. - Use development credentials while the app is still in testing. - After the app is published, switch to **Production** credentials. 2. Under **OAuth Information**, configure: - **OAuth Redirect URL** — use the exact URL for your Unified region (see below). - **OAuth Allow List** — add the same URL (or the base domain if Zoom accepts it). 3. If **Strict Mode URL** is enabled, the redirect URL must match character-for-character. ### **OAuth redirect URL by region** Add the redirect URL that matches where your Unified workspace is hosted: | **Region** | **Redirect URL** | | ---------- | -------------------------------------- | | US | `https://api.unified.to/oauth/code` | | EU | `https://api-eu.unified.to/oauth/code` | | AU | `https://api-au.unified.to/oauth/code` | > **Important:** Copy the URL exactly. A trailing slash, wrong subdomain, or `http` instead of `https` will cause the connection to fail. --- ## **Step 2: Add the required scopes (permissions)** In your Zoom app, open the **Scopes** page and add the permissions below. Only add scopes for the features you actually use. For each scope, Zoom may ask you to describe why your app needs it. Keep the explanation tied to your use case (e.g. 'Read call history for CRM logging' or 'List meetings for calendar sync'). ### **Required for sign-in (Zoom and Zoom Phone)** Add these for any connection: - `user_profile` - `user_info:read` - `user:read` ### **Zoom — users and groups** If you sync users or groups: | **Feature** | **Scopes to add** | | ------------ | ---------------------------------------------------- | | Read users | `user:read:user:admin`, `user:read:list_users:admin` | | Write users | `user:write:admin` | | Read groups | `group:read:admin`, `group:read:list_groups:admin` | | Write groups | `group:write:admin` | ### **Zoom — meetings and webinars** If you work with meetings or webinars: | **Feature** | **Scopes to add** | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Read meetings | `meeting:read:list_meetings` | | Create / update / delete meetings | `meeting:write:meeting`, `meeting:update:meeting`, `meeting:delete:meeting` | | Read webinars | `webinar:read:list_webinars` | | Manage webinars | `webinar:write:webinar`, `webinar:update:webinar`, `webinar:delete:webinar`, `webinar:write:registrant`, `webinar:update:registrant`, `webinar:delete:registrant`, `webinar:write:panelist`, `webinar:update:panelist`, `webinar:delete:panelist` | ### **Zoom Phone — calls, recordings, and users** If you use the Zoom Phone integration: | **Feature** | **Scopes to add** | | --------------------- | ---------------------------- | | Read Zoom Phone users | `phone:read:user:admin` | | Read call history | `phone:read:call_log:admin` | | Read call recordings | `phone:read:recording:admin` | > **Note:** Zoom Phone scopes require a Zoom Phone license on the account you connect. --- ## **Step 3: Add credentials in Unified** 1. In the Unified dashboard, open **Integrations**. 2. Enable **Zoom** and/or **Zoom Phone**. 3. Enter the **Client ID** and **Client Secret** from your Zoom app (development credentials while testing; production credentials after publish). 4. Save the integration. The Client ID and Client Secret in Unified must match the values shown in the Zoom Marketplace for the same app and environment (Development vs Production). --- ## **Step 4: Connect a Zoom account** 1. Start a new connection for Zoom or Zoom Phone (from the dashboard or your app's auth flow). 2. You will be redirected to Zoom to sign in and approve the requested permissions. 3. After approval, you are redirected back and the connection is created. > **Tip:** The Zoom user who authorizes the connection should be an account admin if you use admin-level scopes (users, groups, call logs, recordings). --- ## **Testing vs published apps** Zoom apps start in **development / testing** mode. This affects who can connect. ### **While the app is in testing** - Only Zoom accounts added as **authorized testers** (or users on the same Zoom account as the app owner) can complete OAuth. - Use the **Development** Client ID and Client Secret in Unified. - For each customer Zoom account that needs to connect **before** the app is published, that account must be added as a tester in the Zoom Marketplace: 1. Open your app in the [**Zoom Marketplace Developer portal**](https://marketplace.zoom.us/develop/applications). 2. Find **Add app for testing** / **Local Test** / **Authorized testers** (wording may vary). 3. Add the customer's Zoom email or account. If a customer sees an error that the app is not authorized, they are usually not on the tester list yet. ### **When the app is published** - Any Zoom customer can connect without being added as a tester. - Switch Unified to the **Production** Client ID and Client Secret from the Zoom app. - Existing connections may need to be **re-authorized** after you switch credentials or publish. > **Note:** Publishing goes through Zoom's review process. Zoom may require security review for certain scopes and can take several business days. Plan ahead if you need many customers to connect on a fixed timeline. --- ## **Checklist before connecting** Use this quick checklist if a connection fails: - [ ] Client ID and Client Secret in Unified match the Zoom app (same environment: Development or Production). - [ ] OAuth Redirect URL in Zoom is exactly `https://api.unified.to/oauth/code` (or the EU/AU URL for your region). - [ ] The same URL is on the OAuth Allow List. - [ ] All scopes your product needs are added in the Zoom app. - [ ] If the app is still in testing, the connecting user's Zoom account is an authorized tester. - [ ] The authorizing user has admin rights when using admin scopes. - [ ] For Zoom Phone: the account has Zoom Phone enabled. --- ## **Troubleshooting** ### **'Invalid redirect URI' or redirect errors** The redirect URL in Zoom does not match what Unified sends. Confirm the exact URL for your region and that Strict Mode is not blocking a valid redirect. ### **'App not authorized' or customer cannot sign in** The app is likely still in testing and the customer's Zoom account is not an authorized tester. Add them in the Zoom Marketplace, or publish the app. ### **Connection works but data is missing** Usually a missing scope. Compare the scopes in your Zoom app with the tables in Step 2 and add any that apply to the features you use. ### **Zoom Phone returns no calls or recordings** - Confirm `phone:read:call_log:admin` and/or `phone:read:recording:admin` are added. - Confirm the connected account has Zoom Phone and admin access to phone data. ### **After publishing, connections stopped working** Update Unified with the **Production** Client ID and Client Secret, then ask affected users to connect again. --- ## **Related Zoom products** Unified also supports **Zoom Calendar** as a separate integration with its own calendar-specific scopes. If you only need Zoom meetings, webinars, users, and Zoom Phone, follow this guide. If you need Zoom's native calendar product, enable the Zoom Calendar integration and add its scopes in the same (or a separate) Zoom OAuth app. For Zoom developer documentation, see [**developers.zoom.us**](https://developers.zoom.us/). ## How to create a Connection In Microsoft Teams URL: https://docs.unified.to/guides/how_to_create_a_connection_in_microsoft_teams # How to create a Connection In Microsoft Teams ------ _June 5, 2026_ Most Microsoft products follow the same standard process for creating a connection, but Microsoft Teams is slightly different. To access Unified Communications (UC) objects such as call records, Microsoft requires **application-level permissions** in addition to delegated permissions. This is necessary because Microsoft does not allow access to certain Teams resources using delegated permissions alone. Before your end-users can authorize a MS Teams connection, you must first register an application in Microsoft Azure and configure the required permissions. ## Step 1: Register Your Microsoft Application Follow the guide below to register your application and obtain your **Client ID** and **Client Secret**: [https://docs.unified.to/guides/how_to_get_your_oauth_2_credentials_for_microsoft_dynamics_365#how-to-get-your-oauth-2-credentials-for-microsoft-dynamics-365](https://docs.unified.to/guides/how_to_get_your_oauth_2_credentials_for_microsoft_dynamics_365#how-to-get-your-oauth-2-credentials-for-microsoft-dynamics-365) Once you have completed the application registration, continue with the permission setup steps below. ## Microsoft Teams Permission Types Microsoft Teams uses two types of permissions: 1. **Delegated Permissions** 2. **Application Permissions** ### Resources That Require Delegated Permissions The following categories only require delegated permissions: - Messaging - HRIS - Calendar ### Resources That Require Application Permissions The following category requires application permissions: - Call Records --- ## Step 2: Configure Delegated Permissions 1. Open your registered application in Azure Portal. 2. Navigate to **API Permissions**. 3. Click **Add a permission**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_connection_in_microsoft_teams-0.png) 4. Select **Microsoft Graph**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_connection_in_microsoft_teams-1.png) 5. Select **Delegated permissions**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_connection_in_microsoft_teams-2.png) 6. Search for and select the required delegated permissions. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_connection_in_microsoft_teams-3.png) 7. Click **Add permissions**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_connection_in_microsoft_teams-4.png) --- ## Step 3: Configure Application Permissions (Required for Call Records) If you need access to Call Records, repeat the steps above but select **Application permissions** instead of Delegated permissions. Search for and add the following permissions: ```plain text CallRecords.Read.All OnlineMeetingRecording.Read.All OnlineMeetingTranscript.Read.All CallRecordings.Read.All CallTranscripts.Read.All ``` ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_connection_in_microsoft_teams-5.png) After selecting all required permissions, click **Add permissions**. ## Step 4: Create the Connection After configuring permissions, create your Microsoft Teams connection. If you have not yet obtained your Client ID and Client Secret, follow this guide: [https://docs.unified.to/guides/how_to_get_your_oauth_2_credentials_for_microsoft_dynamics_365#how-to-get-your-oauth-2-credentials-for-microsoft-dynamics-365](https://docs.unified.to/guides/how_to_get_your_oauth_2_credentials_for_microsoft_dynamics_365#how-to-get-your-oauth-2-credentials-for-microsoft-dynamics-365) Enter the following credentials: - Client ID - Client Secret ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_connection_in_microsoft_teams-6.png) Click **Activate**. --- ## You're Ready to Go Your Microsoft Teams integration is now configured and ready to use. If you have any questions or encounter any issues during setup, please contact our support team. ## How to create a token-based connection in Highlevel URL: https://docs.unified.to/guides/how_to_create_a_token_based_connection_in_highlevel # How to create a token-based connection in Highlevel ------ _April 23, 2026_ This guide walks you through creating a **HighLevel token-based connection** using a **sub-account (location) API key** in Unified. --- ## ⚠️ Important Context HighLevel operates on two levels: - **Agency level (company)** - **Sub-account level (location)** ✅ _(token-based authentication works only here)_ To access **CRM** and **HRIS** data, you must use a **sub-account (location)** API key. > **Note:** HighLevel offers multiple ways to create connections. To avoid issues, follow every step in this guide carefully. --- ## ✅ Prerequisites - You must have **Admin** or **Owner** access to the sub-account. ## 🔧 Step-by-Step Setup 1. Navigate to Your Sub-Account Go to your **HighLevel sub-account (location)**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_token_based_connection_in_highlevel-0.png) 2. Open Private Integrations Go to **Settings → Private Integrations** _(inside the sub-account)_ ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_token_based_connection_in_highlevel-1.png) 3. Create a New Integration Click **Create Integration** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_token_based_connection_in_highlevel-2.png) 4. Name Your Integration Provide a clear name for your token (e.g., `Unified Integration`) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_token_based_connection_in_highlevel-3.png) 5. Select Required Scopes Choose scopes based on your use case: | Permission | Resource | HighLevel Scopes | | ---------- | ------------- | ------------------------------------------------ | | Read | CRM Contact | `contacts.readonly` | | Write | CRM Contact | `contacts.write` | | Read | CRM Company | `businesses.readonly`, `locations/tags.readonly` | | Write | CRM Company | `businesses.write`, `locations/tags.write` | | Read | CRM Deal | `opportunities.readonly` | | Write | CRM Deal | `opportunities.write` | | Read | HRIS Employee | `users.readonly` | | Write | HRIS Employee | `users.write` | | Read | CRM Lead | `contacts.readonly` | | Write | CRM Lead | `contacts.write` | | Read | CRM Pipeline | `opportunities.readonly` | | Read | CRM Event | `forms.readonly`, `contacts.readonly` | | Write | CRM Event | `contacts.write` | **Reference:** [https://marketplace.gohighlevel.com/docs/Authorization/Scopes](https://marketplace.gohighlevel.com/docs/Authorization/Scopes) 6. Create and Confirm Click **Create, t**hen click **Confirm** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_token_based_connection_in_highlevel-4.png) 7. Copy API Token You will receive an **API Token** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_token_based_connection_in_highlevel-5.png) Store it securely (you won't be able to retrieve it later) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_token_based_connection_in_highlevel-6.png) 8. Get Location ID To find your **Location ID**: ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_token_based_connection_in_highlevel-7.png) Look at the URL of your sub-account Example: ```plain text https://app.gohighlevel.com/v2/location/tG38xt5twyoGISw4o92p/settings/private-integrations/... ``` The value after `/location/` is your **Location ID** → `tG38xt5twyoGISw4o92p` 9. Connect in Unified - Go to: [https://app.unified.to/integrations/highlevel](https://app.unified.to/integrations/highlevel) - Select **API TOKEN** - Click **Activate** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_token_based_connection_in_highlevel-8.png) 10. Create Connection Paste **API Token and Location ID, then c**lick **Create Connection** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_token_based_connection_in_highlevel-9.png) --- ## ✅ Done Your HighLevel token-based connection should now be successfully set up in Unified. --- If it doesn't work, double-check: - You created the token at the **sub-account level** - The correct **scopes** are selected - The **Location ID** matches the sub-account If you have any more doubts or issues, please contact the [Unified.to](https://unified.to/) team for support. ## How to Create a Unified.to Connection to HighLevel URL: https://docs.unified.to/guides/how_to_create_a_unified_connection_to_highlevel # How to Create a Unified.to Connection to HighLevel ------ _March 19, 2026_ This guide explains how to connect an existing HighLevel account to [Unified.to](https://unified.to/). It assumes: - You already have a Unified.to account - You already have a HighLevel account - You have access to a HighLevel Developer (Agency) account --- ## Overview To create a connection between Unified.to and HighLevel, you will: 1. Create or configure a HighLevel OAuth app 2. Collect required credentials (Client ID, Client Secret, etc.) 3. Use Unified.to to create a connection 4. Authorize the connection via OAuth --- ## Step 1: Create a HighLevel OAuth App 1. Log in to your HighLevel Developer (Agency) account [link](https://marketplace.gohighlevel.com/apps) 2. Navigate to the **App Dashboard**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_unified_connection_to_highlevel-0.png) 1. Click **Create App** 2. Fill in the required details: - **App Name**: Any name (e.g., Unified Integration) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_unified_connection_to_highlevel-1.png) - **Redirect URL**: In **Advanced settings → Auth**, set the redirect URL to the value shown on the integration's **OAuth2 Credentials** page in [Unified.to](https://unified.to/). ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_unified_connection_to_highlevel-2.png) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_unified_connection_to_highlevel-3.png) - **Scopes**: Use the scopes listed on the HighLevel app setup page. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_unified_connection_to_highlevel-4.png) In [Unified.to](https://unified.to/), you can also find the required scopes listed below the integration page. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_unified_connection_to_highlevel-5.png) 3. Save the app in HighLevel. --- ## Step 2: Get Credentials After creating the app, go to **Manage → Secrets** and add: ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_unified_connection_to_highlevel-6.png) - **Client ID** - **Client Secret** To find the **Version ID**, open the install link and copy the Version ID from the resulting page. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_unified_connection_to_highlevel-7.png) Keep these secure. You will need them in Unified.to. --- ## Step 3: Create Connection in Unified.to 1. Log in to your Unified.to dashboard 2. Navigate to **Integrations**. 3. Select **HighLevel** as the integration 4. Enter the following details: - **Client ID** (from HighLevel) - **Client Secret** (from HighLevel) - **Version ID** (from HighLevel) 5. Click Activate 6. Go to **Embedded Components**. 7. Click **HighLevel**. --- ## Step 4: Authorize the Connection 1. You will be redirected to HighLevel 2. Log in (if not already logged in) 3. Select the account/location you want to connect 4. Approve the requested permissions Once approved, you will be redirected back to Unified.to --- ## Common Issues & Troubleshooting ### 1. Invalid Redirect URI - Ensure the redirect URL in HighLevel exactly matches the one used by Unified.to ### 2. Unauthorized / 401 Errors - Double-check Client ID and Client Secret - Ensure the OAuth app is active ### 3. Missing Data - Ensure correct scopes are selected in HighLevel - Some data may depend on the selected location/account --- ## Notes - HighLevel uses location-based access. Make sure you select the correct location during authorization - You can create multiple connections for different locations if needed --- If you run into issues, reach out to the Unified.to support team with: - Connection ID - Error logs - Steps to reproduce ## How to Create a Xero Connection in URL: https://docs.unified.to/guides/how_to_create_a_xero_connection_in # How to Create a Xero Connection in ------ _April 2, 2026_ Follow the steps below to create a Xero application and connect it to Unified.to. ## 1. Sign in to the Xero Developer Portal Go to the Xero Developer portal: [https://developer.xero.com/app/manage/](https://developer.xero.com/app/manage/) Sign in with your Xero account. ## 2. Create a New App From the dashboard, click **Create New App**. Choose the appropriate app type and fill in the required details: - App name - Company or organization name - Privacy policy URL - Terms and conditions URL (if required) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_xero_connection_in-0.png) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_xero_connection_in-1.png) ## 3. Add the Redirect URI While creating the app, add the OAuth 2.0 Redirect URI provided by Unified.to. You can find the correct redirect URI here: [https://app.unified.to/integrations/xero?tab=oauth2](https://app.unified.to/integrations/xero?tab=oauth2) > Important: The redirect URI may differ depending on your Unified.to region. Make sure you copy the exact value shown in your Unified.to dashboard. Examples: - US region - EU region - AU region If the redirect URI does not match exactly, Xero authentication will fail. After adding the redirect URI, click **Create App**. --- ## ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_xero_connection_in-2.png) ## 4. Copy the Client ID and Client Secret After the app is created, open the app and go to the **Configuration** tab. There you will find: - Client ID - Client Secret > Keep the Client Secret secure. Do not share it publicly or commit it to source control. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_xero_connection_in-3.png) ## 5. Add Credentials to Unified.to Open the Xero integration settings page in Unified.to: https://app.unified.to/integrations/xero?tab=auth Paste the following values: - Client ID - Client Secret Click **Activate**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_a_xero_connection_in-4.png) ## 6. Create a Connection Using Embedded Components To create a customer-facing connection flow: 1. Go to **Embedded Components** in Unified.to. 2. Select the **Xero** integration. 3. Launch the embedded authorization flow. 4. Complete the authorization process. This allows your end users to connect their Xero account directly from your application. ## Notes ### Journals Endpoint Availability The `Journals` endpoint is a premium Xero feature. According to Xero's documentation, the Journals API is only available starting with the **Advanced** pricing tier. Customers using lower Xero plans may receive authorization or access errors when calling this endpoint. ### Common Issues - Ensure the redirect URI in Xero exactly matches the one shown in Unified.to. - Make sure you are using the correct region-specific Unified.to redirect URI. - If you regenerate the Client Secret in Xero, you must update it in Unified.to. - Only users with sufficient permissions in Xero can authorize an organization. - If you have previously selected an organization for the same Xero app, Xero may automatically reuse that organization during future authorizations. ### Troubleshooting If authentication fails: - Verify the Client ID and Client Secret are correct. - Double-check the redirect URI. - Re-authorize the connection after saving any changes. - Remove the existing connection and create a new one if necessary. ## How to create and configure webhooks URL: https://docs.unified.to/guides/how_to_create_and_configure_webhooks # How to create and configure webhooks ------ _September 4, 2024_ This guide goes over how to create and configure webhooks with Unified.to. Webhooks allow you to receive real-time data events when there are updates in your customers' accounts. Looking for help with troubleshooting webhook issues? See: [How to troubleshoot unhealthy webhooks](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_webhooks). ## Before you begin This guide assumes you have a basic understanding of [webhooks](https://docs.unified.to/concepts/webhooks). ## Create a webhook Webhooks and connections are closely related; webhooks can only be created from existing connections and you can create multiple webhooks off a single connection. If you delete a connection, any webhooks that are associated with it will also be deleted. There are two ways to create a webhook: ### Method 1: Use the Unified API Send a POST request to `/unified/webhook`(see below for configuration options). For example: ```javascript POST /unified/webhook?include_all=true { hook_url: `${YOUR_WEBHOOK_URL}`, connection_id: `${CONNECTION_ID}` object_type: 'ats_candidate', // data to subscribe to event: 'updated', // type of event to listen for } ``` **API reference:** [Create a webhook](https://docs.unified.to/unified/webhook/Create_webhook_subscription) By including the optional parameter `include_all` in the query, the webhook will receive all historical data for the connection (see:[ Get all initial data for a connection](https://docs.unified.to/guides/how_to_create_and_configure_webhooks#get-all-initial-data-for-a-connection)). This is the payload that your server receives when webhook data comes in: | Name | Type | Desc | | --------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `data` | _object array_ | An array of objects specific to the webhook's connection. (eg. CRM contacts) | | `webhook` | [_Webhook_](https://docs.unified.to/unified/webhook/model) | The webhook object. You can use the id to manage or delete your webhook. | | `nonce` | _string_ | Random string | | `sig256` | _string_ | A security signature generated using HMAC-SHA1. It combines your workspace secret with the payload data and nonce i.e `HMAC-SHA256(workspace.secret, data + nonce)` . Use this to verify the authenticity of incoming webhooks. | | `type` | _enum_ | `INITIAL-PARTIAL, INITIAL-COMPLETE, VIRTUAL, NATIVE` | A note on the `type` enums: - `INITIAL-PARTIAL`: included with each page of results for the initial sync - `INITIAL-COMPLETE`: included with last page (e.g. last 5 elements for the limit 100) of initial sync or with an empty list (no more results) - `VIRTUAL`: included with every page when reading new data from a virtual webhook - `NATIVE`: included with every page when reading new data from a native webhook ### Method 2: Use the Unified.to app If you prefer to create webhooks through the UI, you can do so at [app.unified.to](https://app.unified.to/). 1. Navigate to **Integrations** > [**Webhooks**](https://app.unified.to/webhooks). 2. Click **New Webhook**. 3. Click on the input and a list of your connections will appear. Select the connection from which you want to create a webhook. 4. Choose how to configure your webhook (see below). 5. Click **Save**. Information about the other fields can be found under their respective tooltips in the UI. ### Webhook configuration options Whether you use the API or our web UI, these are the configuration options you can specify when creating a webhook: 1. **Object:** The data you are interested in e.g. Deal, Company, Application, etc. 2. **Event:** The events you want to subscribe to. Note: The _updated_ event will also be sent when the data object is first _created_. 3. **Type:** For integrations that support both native and virtual webhooks, select the type of webhook you want. Virtual webhooks are our way of supporting webhooks for API providers that don't offer them. For more details, see: [Understanding virtual webhooks](https://docs.unified.to/guides/understanding_virtual_webhooks#understanding-native-and-virtual-webhooks). 4. **Filter (optional):** Specify any properties you want to filter incoming events by. See: [Use filters to refine webhook events](https://docs.unified.to/guides/how_to_create_and_configure_webhooks#use-filters-to-refine-webhook-events) 5. **Fields (optional):** Comma-separated list of fields to include in the webhook payload. Leave blank to include all fields. The 'raw' field is NOT included by default. If you want to include all fields as well raw, just enter 'raw'. To read more about raw fields, see: [Raw and custom fields](https://docs.unified.to/reference/fields). 6. **Interval (virtual webhooks only):** Specify how often you want Unified.to to poll the API provider for new data. ## List your webhooks To view your webhooks, make a GET request to `/unified/webhook` or visit [app.unified.to](https://app.unified.to/) and navigate to Integrations > [**Webhooks**](https://app.unified.to/webhooks). **API reference:** [Get all webhooks](https://docs.unified.to/unified/webhook/Returns_all_registered_webhooks) ## Get all initial data for a connection When you create a webhook, you can choose to fetch all existing data immediately. 1. If you are using the API, add the `include_all` query parameter when creating the webhook. 2. If you are using the UI, check off **Initially sync all data** in the configuration options. Once the initial sync is complete, you will receive a final payload with `type: INITIAL-COMPLETE` and the requested webhook will continue to work as normal. **API reference:** [Create a webhook subscription](https://docs.unified.to/unified/webhook/Create_webhook_subscription) ## A note on retrieving webhook data Our system will POST initial data to your webhook in chunk sizes up to the limit supported by the integration. The limit can be found on the integration details page under **Feature support**. When performing the initial retrieval, depending on the amount of data being sent, the integration's API's rate-limiting rules, and your server's performance, it could take a very long time. If you do not want to get all of the existing data (for example, you already have it or don't need it), then do not include the `include_all` parameter in the request. After the initial retrieval is completed, whenever updated data is available, your webhook will be called with a list of data objects, up to the max limit. If there are more entries than the limit, then your webhook URL will be POSTed to multiple times. For details about the webhook payload that is sent to your server, [click here](https://docs.unified.to/concepts/webhooks#webhook-payload). ## Specify which fields to receive from a webhook If you only want a subset of fields when receiving updated data, you can define them with the `fields` parameter in both the API and the UI. This is useful if you don't want the entire payload being sent to your servers. For more details, see: [Fields](https://docs.unified.to/reference/fields). **API reference:** [Create a webhook subscription](https://docs.unified.to/unified/webhook/Create_webhook_subscription) ## Use filters to refine webhook events _Note: This feature is only available for virtual webhooks. For more information about virtual webhooks, see:_ [_Understanding virtual webhooks_](https://docs.unified.to/guides/understanding_native_and_virtual_webhooks#understanding-native-and-virtual-webhooks) Some virtual webhooks support filters, allowing you to filter the events you receive. For instance, if you're subscribed to Deal events from a CRM, you could filter by `company_id` to only receive events from that specific company. Filter availability varies by integration type and data model. To check which filters are supported for a particular webhook, refer to the **List** section under the **Feature Support** tab of the respective integration. Parameters that end in `_id` or `type` can be used as filter options when creating a webhook, helping you tailor your subscriptions to your specific needs. ## Trigger webhooks manually If you don't want to wait for an event to come in while testing webhooks on a Tester plan, you can trigger a webhook call right away. 1. In the web app, navigate to [Webhooks](https://app.unified.to/webhooks). 2. Click the **Trigger** icon next to your webhook: ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_and_configure_webhooks-0.png) This can also be accomplished with the API. **API reference:** [Trigger a webhook](https://docs.unified.to/unified/webhook/Trigger_webhook) **Note:** When you trigger a webhook, we will schedule the next dispatch event to go out right away - this is treated like a regular webhook event. Therefore, if your webhook is listening for update events and no new updates have occurred since the last run, then no new events will be sent to your server. ## Frequently asked questions **Is there a page_max_limit for webhooks?** An integration's `page_max_limit` typically comes directly from the API provider. You can see that limit under the **Feature Support** tab on the integration details page**.** You don't have to set limits for webhooks; they will use the integration's max limit. **Does changing the interval for virtual webhooks affect the initial retrieval?** Increasing frequency of virtual webhooks has no effect on the initial retrieval of historical data. Each page read is scheduled to be read one after another as soon as possible. **How do I distinguish between an updated event and a created event?** When a new data object is created, it triggers both created and updated events. If you want to track both of these events but also tell them apart, we recommend subscribing to the updated event and then compare `created_at` and `updated_at` for incoming data. If they are the same, that means the data object was newly created. **What happens if the webhook dispatch fails? Is there a retry mechanism in place?** Our retry mechanism will retry 3 times immediately before using a Fibonacci backoff strategy. Read more about it [here](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_webhooks#a-note-on-our-webhook-retry-mechanism). **Why do I receive duplicate webhook events and how should I handle them?** Duplicate webhook events can occur for several reasons: - Some integrations (like HubSpot) trigger separate events for each property change on an object - Rapid changes to objects can trigger multiple events that may arrive out of order due to network conditions To handle duplicates effectively: 1. Always use the object's latest state when updating your database 2. Implement atomic writes to avoid race conditions. Only update records if the incoming data is newer: ```javascript -- Pseudo-code example IF incoming_object.updated_at >= stored_object.updated_at THEN UPDATE record ELSE -- Skip update as we already have newer data END IF ``` ## See also - [How to troubleshoot unhealthy webhooks](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_webhooks) - [Understanding virtual webhooks](https://docs.unified.to/guides/understanding_virtual_webhooks#understanding-native-and-virtual-webhooks) ## How to create Connection with Hubspot URL: https://docs.unified.to/guides/how_to_create_connection_with_hubspot # How to create Connection with Hubspot ------ _December 17, 2025_ Hubspot provides Free Developer Sandbox Account. You can create that from following [link](https://offers.hubspot.com/free-cms-developer-sandbox) . 1. Create an account. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-0.png) 1. Provide name of the account ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-1.png) After account is created it will redirect you to following page:- ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-2.png) 1. Goto Development page from the size menu ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-3.png) 1. Create a project in IDE and install and install using following command ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-4.png) ```javascript npm install -g @hubspot/cli && hs init ``` 1. Create your Personal Access Key. It will be inside the keys menu ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-5.png) 1. Paste that key in your terminal when your hubspot application ask for it. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-6.png) 1. Now we will create a project. run following command in that same project. ```javascript hs get-started ``` ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-7.png) 1. Write Y to upload that project in the hubspot application. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-8.png) 1. Again select Y for following ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-9.png) 1. It will redirect to hubspot page. goto to following page:- Connected Apps→ Manage Location → click the check box → Save. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-10.png) 1. In your project(code) you will see following folder structure. GOTO→ yourproject (account-testing) → app-hsmeta.json ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-11.png) 1. Following will be the scopes and redirect URL. You can remove which you want. However, do remember those removed scopes before creating a connection in unified. Also remember to add redirect URL as follows. ```javascript { "uid": "get_started_app", "type": "app", "config": { "description": "A Unified.to token app for testing", "name": "My Get Started app", "distribution": "private", "auth": { "type": "oauth", "redirectUrls": [ "https://api.unified.to/oauth/code", ], "requiredScopes": [ "oauth", "crm.objects.companies.read", "crm.objects.contacts.read", "crm.objects.deals.read", "crm.objects.owners.read", "crm.pipelines.orders.read", "crm.objects.contacts.write", "crm.pipelines.orders.read", "crm.pipelines.orders.write", "crm.objects.deals.write", "sales-email-read", "content", "tickets", "crm.lists.read", "settings.users.teams.read" ], "optionalScopes": [], "conditionallyRequiredScopes": [] }, "permittedUrls": { "fetch": ["https://api.hubapi.com"], "iframe": [], "img": [] }, "support": { "supportEmail": "support@example.com", "documentationUrl": "https://example.com/docs", "supportUrl": "https://example.com/support", "supportPhone": "+18005555555" } } } ``` 1. You can update those changes using following command in terminal. ```javascript hs project upload ``` 1. When the code is uploaded successfully you can check that by going to following Development→ Project → Select your project. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-12.png) 1. To check your scopes click on the project Component. It will show your app-hsmeta.json ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-13.png) 1. To get the client ID and Client Secret click on the Auth button in your project component. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-14.png) 1. Paste the Client ID and Client Secret in the app.unified.to ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-15.png) 1. To get developer key goto Development→ keys → developer API key. If no keys are present than generate developer api key. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_create_connection_with_hubspot-16.png) 1. Paste that key in the [app.unified.to](https://app.unified.to/) and click activate. 2. Goto Embedded components→ click on hubspot→ select your project→ click on approve. Your Hubspot application is created. ## How to customize portal URLs for Stripe and GoCardless URL: https://docs.unified.to/guides/how_to_customize_portal_urls_for_stripe_and_gocardless # How to customize portal URLs for Stripe and GoCardless ------ _November 13, 2024_ This guide explains how to customize the portal URLs for Stripe and GoCardless contacts to control payment method flows and redirects. Portal URLs allow your customers to manage their payment methods and perform other operations through a dedicated UI. ## Before you begin This guide assumes you have: - A basic understanding of the Unified API - An active integration with either Stripe or GoCardless ## Understanding portal URLs When working with the `accounting_contact` object for Stripe and GoCardless integrations, each contact includes a `portal_url` field. This URL provides access to the contact's portal UI where they can perform various operations, such as managing payment methods. For example, a typical response might look like: ```javascript { "id": 77558, "portal_url": "" } ``` ## Customize the portal URL You can customize the portal URL's behaviour by appending specific query parameters to your API calls. The following parameters are available: 1. `success_url` (optional): Specifies where users should be redirected after completing their action 2. `type` (optional): Specifies the type of action the link is intended for. Available values: - `portal`: Generates a standard portal UI link (default) - `add_payment_method`: Generates a portal UI URL specifically for adding a new payment method ### Add a payment method with custom redirect To create a URL that allows customers to add a payment method and redirects them upon completion: 1. Start with the original portal URL from the contact object: ```javascript https://api.unified.to/accounting/{connection_id}/contact/{contact_id}/portal ``` 2. Append the `success_url` and `type` parameters: ```javascript https://api.unified.to/accounting/{connection_id}/contact/{contact_id}/portal?success_url=YOUR_REDIRECT_URL&type=add_payment_method ``` Note: Both `success_url` and `type` parameters are optional. If `type` is not specified, the system defaults to `type=portal`. ## See also - [Accounting contact object reference](https://docs.unified.to/accounting/contact/model) ## How to filter webhook events URL: https://docs.unified.to/guides/how_to_filter_webhook_events # How to filter webhook events ------ _July 24, 2024_ Webhook filters allow you to only receive data about the events you care about. For instance, if you're subscribed to CRM Deal events from ActiveCampaign, you could filter by `company_id` to receive data only about deals for a specific company. Please note that filters are only supported for **virtual** webhooks at this moment, with few exceptions (e.g. Box supports native webhook filtering). Read more about the differences between native and virtual webhooks here: [Understanding virtual webhooks](https://docs.unified.to/guides/understanding_virtual_webhooks) Filter availability varies by integration type and data model. To determine which filters are supported for a particular virtual webhook, refer to the 'List' section on the Feature Support page of the respective integration. Parameters that end in `_id` or `type` can be used as filter options when creating a webhook, helping you tailor your subscriptions to your specific needs. For example, this is the Feature Support list for CRM Deals by ActiveCampaign: ![CRM_Deal.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_filter_webhook_events-0.png) Notice that this data model supports virtual webhooks as specified under ‘Webhook.' Under ‘List Options', the parameter `company_id` is shown, indicating that it is possible to use this as a filter. When creating webhooks via the [Unified.to](https://unified.to/) web app, you can specify your filter parameters in the webhook creation form: ![Webhook_creation.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_filter_webhook_events-1.png) When creating a webhook programatically via our API, you can include `filters` in the payload with a string dictionary of values to filter by. For example, in Javascript: ```javascript const options = { method: 'POST', url: 'https://api.unified.to/unified/webhook', headers: { authorization: 'bearer YOUR_ACCESS_TOKEN' }, data: { filters: { company_id: '12345', }, // other payload data if needed }, }; const results = await axios.request(options); ``` Review our complete guide on the webhook API here: [Create webhook subscriptions](https://docs.unified.to/unified/webhook/Create_webhook_subscription). ## How-to Get a Private Support Channel for Your Unified Integration URL: https://docs.unified.to/guides/how_to_get_a_private_support_channel_for_your_unified_integration # How-to Get a Private Support Channel for Your Unified Integration ------ _May 1, 2026_ [Unified.to](https://unified.to/) offers customers 3 platforms for support: - Slack - Discord - Microsoft Teams # Slack We use Slack Connect to invite our customers into private support channels that we create in our Slack account. Just reach out to your account manager, and they will create a channel for you and invite your product team. This is our preferred method of working with your team to get them to success faster. # Discord Our [Discord server](https://discord.gg/85z7HF7JbD) offers both public and private channels for support. The public channels are used for anyone who is just trying out [Unified.to](https://unified.to/). Once you dedicate your product team's time to integrate Unified.to into your application, please let us know and we will create a private channel for you. # Microsoft Teams For enterprise customers, Unified also offers shared private channels in Microsoft Teams. Please let us know if you cannot use Slack nor Discord (as per your IT policy), and we will be happy yo setup Teams access and a private channel. But, you will need your IT to also setup Teams access to the Unified MS Teams account for your team, by following this guide: This guide is for your **Microsoft Entra (Azure AD) administrator**. It walks through the configuration needed on your tenant so we can collaborate with you in a Microsoft Teams shared channel. ## Background Unified.to wants to invite a user from your organization into a Microsoft Teams **shared channel** (sometimes referred to as a "private cross-org channel"). Unlike standard Teams channels, which require external users to be added as guests in your tenant, shared channels use **Microsoft Entra B2B direct connect**. This lets your user access the channel from inside their own Microsoft Teams client, without switching tenants and without being added to our directory. For B2B direct connect to work, **both tenants must trust each other**. We have already configured our side. This document covers the symmetric configuration required on yours. ## What you'll be enabling You're enabling B2B direct connect with a single specific external tenant — Unified.to. Nothing else changes: - We are not added as guests in your directory. - Your users are not added as guests in ours. - Your existing default cross-tenant access policy is unaffected for every other organization. - The settings can be reverted at any time by removing the entry. ## Prerequisites - A user account in your Entra tenant with one of the following roles: - Global Administrator, **or** - Security Administrator, **or** - Conditional Access Administrator (with Cross-tenant access settings permission) - Your tenant must have Microsoft Entra ID (any edition — Free is sufficient for the basic configuration described here) - A modern browser signed in to your Microsoft 365 admin account ## Our tenant details You'll need these values during the setup: | Field | Value | | ------------------------- | -------------------------------------- | | Organization name | Unified.to | | Primary domain | `unified.to` | | Microsoft Entra tenant ID | `4b8acf48-e5c8-4484-896b-95b1b824ddda` | ## Step-by-step setup The full process takes about five minutes. 1. Open the Microsoft Entra admin center Go to https://entra.microsoft.com and sign in with your admin account. 2. Navigate to Cross-tenant access settings In the left navigation, expand **Entra ID** → **External Identities** → **Cross-tenant access settings**. (If you don't see External Identities directly, click "Show more" or use the search bar at the top: search for "Cross-tenant access settings".) 3. Add Unified.to as an organization 4. Make sure you're on the **Organizational settings** tab (not "Default settings"). 5. Click **+ Add organization**. 6. In the side panel, paste either of the following into the "Tenant ID or domain name" field: - Tenant ID: `4b8acf48-e5c8-4484-896b-95b1b824ddda` - Domain: `unified.to` 7. Microsoft will resolve the entry and display **Unified.to** with our tenant ID. 8. Click **Add**. You should see a confirmation toast: "Successfully added Unified.to tenant to organizational settings." Unified.to now appears in the list with both Inbound access and Outbound access showing **"Inherited from default"**. We'll override that next. ### 4. Configure Inbound access This controls whether Unified.to users can access resources in your tenant via B2B direct connect (such as joining a shared channel that we host). 1. In the 'Unified.to row', click the **Inbound access** link ("Inherited from default"). 2. In the panel that opens, click the **B2B direct connect** tab. 3. Select the **Customize settings** radio. 4. On the **External users and groups** sub-tab: - Access status: **Allow access** - Applies to: **All Unified.to users and groups** 5. Switch to the **Applications** sub-tab: - Access status: **Allow access** - Applies to: **All applications** 6. Click **Save**. The Save button greys out when the save completes. 7. Close the panel (X in the top-right) to return to the Organizational settings list. The Inbound access column for Unified.to should now read **"Configured"**. ### 5. Configure Outbound access This controls whether your users can access resources hosted by Unified.to (such as the shared channel we'll invite them into). 1. In the 'Unified.to row', click the **Outbound access** link ("Inherited from default"). 2. In the panel that opens, click the **B2B direct connect** tab. 3. Select the **Customize settings** radio. 4. On the **Users and groups** sub-tab: - Access status: **Allow access** - Applies to: **All [your organization name] users** 5. Switch to the **External applications** sub-tab: - Access status: **Allow access** - Applies to: **All external applications** 6. Click **Save**. 7. Microsoft will display a consent dialog titled **"Are you sure?"**, explaining that enabling outbound B2B direct connect lets external organizations access limited contact data about your users for the purpose of sending connection requests. Click **Yes** to confirm. 8. Close the panel. The Outbound access column for Unified.to should now read **"Configured"**. ### 6. Verify The Unified.to row in your Organizational settings should now show: | Inbound access | Outbound access | | -------------- | --------------- | | Configured | Configured | Both fields should be hyperlinked. If either still says "Inherited from default", reopen that link and re-save. ## What about Microsoft Teams settings? In most tenants, the Teams admin center already permits shared channel collaboration with external orgs by default. If a user from your side reports they cannot accept our shared-channel invite, your Teams admin should verify: - Microsoft Teams admin center → **Teams** → **External access** → "Allow Teams accounts not managed by an organization" or per-domain allow list (if your org uses an allow list, add `unified.to`). - Microsoft Teams admin center → **Teams** → **Teams policies** → the policy assigned to the affected user has **"Create shared channels"** and **"Invite external users to shared channels"** enabled (or at minimum, **"Join external shared channels"**). These Teams-level settings are usually permissive by default. The Entra cross-tenant configuration above is the part that's typically missing. ## Replication delay Cross-tenant configuration can take up to 15 minutes to propagate across Microsoft's services. If we attempt to invite your user into the shared channel right after you finish, and it fails, please ask us to wait ten minutes and retry before troubleshooting further. ## Letting us know you're done Once Inbound and Outbound both read "Configured" for Unified.to on your side, please let us know. We'll then send the shared-channel invite to the user(s) you specify, and they'll see the invitation appear in their own Microsoft Teams client. ## Reverting later To remove the trust relationship at any time: 1. Microsoft Entra admin center → External Identities → Cross-tenant access settings → Organizational settings. 2. Find the Unified.to row, click the trash icon at the right, and confirm. This restores your default policy for our tenant. Any active shared channels become inaccessible to your users at that point. ## Reference Microsoft's official documentation: - B2B direct connect overview: [https://learn.microsoft.com/entra/external-id/b2b-direct-connect-overview](https://learn.microsoft.com/entra/external-id/b2b-direct-connect-overview) - Configure cross-tenant access settings: [https://learn.microsoft.com/entra/external-id/cross-tenant-access-settings-b2b-direct-connect](https://learn.microsoft.com/entra/external-id/cross-tenant-access-settings-b2b-direct-connect) - Shared channels in Microsoft Teams: [https://learn.microsoft.com/microsoftteams/shared-channels](https://learn.microsoft.com/microsoftteams/shared-channels) ## How to get your 8x8 Connect API Key and Account ID: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_8x8_connect_api_key_and_account_id_step_by_step_guide # How to get your 8x8 Connect API Key and Account ID: Step-by-step guide ------ _July 17, 2025_ 8x8 Connect supports authentication with API Key and Account ID. To connect, you'll need to generate secure API Key and Account ID from your account settings. This short guide shows you how to generate one, whether you're testing in your sandbox or helping customers connect their 8x8 Connect account. ## **API Keys** **Step 1:** View the API Keys Section of the Connect Dashboard. This may be accessed [here](https://connect.8x8.com/messaging/api-keys). ![611133d-image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_8x8_connect_api_key_and_account_id_step_by_step_guide-0.png) **Step 2:** Click the "Create API Key" button. ![6dc9838-image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_8x8_connect_api_key_and_account_id_step_by_step_guide-1.png) **Step 3:** Name the API Key in the Pop Up, this can be any value. We recommend using a memorable name related to the API key's intended purpose such as "HealthCareApp_Production". Once the value is entered in, click **save**. ![a5372f9-image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_8x8_connect_api_key_and_account_id_step_by_step_guide-2.png) **Step 4:** The new API key should now be located in the list, you can perform a partial search at the top for the name. Only the last 6 characters will be shown, click the document button highlighted in red to reveal the entire API key. You can return to this page to retrieve the API key's value at any time. ![9f50dec-image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_8x8_connect_api_key_and_account_id_step_by_step_guide-3.png) ## Account ID and SubAccount ID The **accountId** and **subAccountId** can be found in your 8x8 Connect via **API keys** page.  ![adcd1c81-272d-4b47-9adb-c733f026a2d2.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_8x8_connect_api_key_and_account_id_step_by_step_guide-4.png) Above we can see the Accountid is : **test11_9tT31** with SubaccountId : **test11_9tT31_hq.** Your account may have multiple subaccounts which can be seen if you click the drop down in **Subaccount ID.** You've now created your 8x8 Connect API Key and Account ID - just one of many if you support multiple vendors. Each one adds new auth flows, schema differences, and ongoing maintenance. Unified.to removes that overhead with unified objects that work across 660 APIs. Map once, launch everywhere. Your team ships features instead of debugging vendor-specific edge cases. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how much faster you can launch customer integrations with Unified.to. ## How to get your ADP Workforce Now Oauth2 client ID, Oauth2 client secret, Oauth2 PEM certificate and Oauth2 private key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_adp_workforce_now_oauth2_client_id_oauth2_client_secret_oauth2_pem_certificate_and_oauth2_private_key_step_by_step_guide # How to get your ADP Workforce Now Oauth2 client ID, Oauth2 client secret, Oauth2 PEM certificate and Oauth2 private key: Step-by-step guide ------ _April 24, 2023_ When connecting ADP Workforce Now to other platforms, authentication requires a Oauth2 client ID, Oauth2 client secret, Oauth2 PEM certificate and Oauth2 private key. Follow these step-by-step instructions to generate one from your ADP Workforce Now account, or provide them to your customers if they need to connect their account. 1. Log into your ADP account and access the ADP API Central portal. You will need the paid `ADP API Central` [application](https://apps.adp.com/en-US/apps/410612). 2. Select a project or create a new project ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_adp_workforce_now_oauth2_client_id_oauth2_client_secret_oauth2_pem_certificate_and_oauth2_private_key_step_by_step_guide-0.png) 3. Enter the project details. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_adp_workforce_now_oauth2_client_id_oauth2_client_secret_oauth2_pem_certificate_and_oauth2_private_key_step_by_step_guide-1.png) 4. Select domain ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_adp_workforce_now_oauth2_client_id_oauth2_client_secret_oauth2_pem_certificate_and_oauth2_private_key_step_by_step_guide-2.png) 5. Inside the Production integration Page, select the most suitable option for you. 6. After creating a new Project, select Development Credentials ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_adp_workforce_now_oauth2_client_id_oauth2_client_secret_oauth2_pem_certificate_and_oauth2_private_key_step_by_step_guide-3.png) 7. This page will Provide the Client ID, Secret Key and Certificates 8. Select Manage Certificate to generate Certificate ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_adp_workforce_now_oauth2_client_id_oauth2_client_secret_oauth2_pem_certificate_and_oauth2_private_key_step_by_step_guide-4.png) 9. Fill out the form, which will provide the PEM Key required to create a connection. 10. The processing time for the certificate is few minutes. Generated Certificate will be required to create a connection. 11. If asked, select `api` permission. For Unified, you need to have access to the following APIS for your App: - Job Applications API - Job Applicants API - Job Requisitions API - Client Data - Validation Tables API - Worker Leaves API - Worker API To add the API to your application scope, contact your ADP representative. Make sure you provide the following: - Organization name - Application name - List of APIs to be added Generating a ADP Workforce Now Oauth2 client ID, Oauth2 client secret, Oauth2 PEM certificate and Oauth2 private key is the first step. The harder part is building pipelines that stay reliable as customers add more systems. Unified.to gives you options: deliver data via API, streaming webhooks, database sync, or even as MCP tools for AI agents. One integration layer that adapts to your architecture instead of forcing tradeoffs. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how we simplify integration delivery across 660 integrations and 32 categories. ## How to get your Amazon S3 AWS Region, AWS S3 Key and AWS S3 Secret: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_amazon_s3_aws_region_aws_s3_key_and_aws_s3_secret_step_by_step_guide # How to get your Amazon S3 AWS Region, AWS S3 Key and AWS S3 Secret: Step-by-step guide ------ _December 22, 2023_ Developers connecting to Amazon S3 will need an AWS Region, AWS S3 Key and AWS S3 Secret for authentication. Here's how to generate, configure, and copy your Amazon S3 AWS Region, AWS S3 Key and AWS S3 Secret so you can use it in your integration or share steps with your customers. # **Generating AWS Access Key ID and Secret Access Key** You can easily access your AWS S3 account using a Access key and Secret Access key of your AWS account. 1. If you don't have one already then go to your account and click on **My Security Credentials** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_amazon_s3_aws_region_aws_s3_key_and_aws_s3_secret_step_by_step_guide-0.png) 2. Then select **Access keys (access key ID and secret access key)** section. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_amazon_s3_aws_region_aws_s3_key_and_aws_s3_secret_step_by_step_guide-1.png) There is an important notification on the section, which recommends you to create an IAM Role instead of creating root access keys. 3. Click on **Create New Access Key** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_amazon_s3_aws_region_aws_s3_key_and_aws_s3_secret_step_by_step_guide-2.png) 4. Download the Key pairs to your system for future use. 5. Click on _**Show Access key**_ and you will get your _**Access Key ID**_ and _**Secret Access Key.**_ ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_amazon_s3_aws_region_aws_s3_key_and_aws_s3_secret_step_by_step_guide-3.png) 6. You need to use this _**Access Key ID**_ and _**Secret Access Key**_ to connect to your AWS connect and access the S3 bucket. With your Amazon S3 AWS Region, AWS S3 Key and AWS S3 Secret in hand, you're ready to connect. But managing keys, tokens, and refresh flows across every vendor quickly becomes a security risk. Unified.to abstracts all of that behind a single, secure layer. We never store customer data, credentials are always under your control, and our permissioning model removes the risk of misconfigured scopes. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how Unified keeps integrations real-time, compliant, and safe at scale. ## How to get your Ashby API Key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_ashby_api_key_step_by_step_guide # How to get your Ashby API Key: Step-by-step guide ------ _April 24, 2023_ Ashby supports authentication with an API Key. To connect, you'll need to generate an secure API Key from your account settings. This short guide shows you how to generate one, whether you're testing in your sandbox or helping customers connect their Ashby account. 1. Sign in to Ashby and click "Admin" in the top navigation bar 2. Navigate to Integrations > API Keys ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_ashby_api_key_step_by_step_guide-0.png) 3. Click + New in the upper right corner 4. Add a name for the new API Key, click Create API Key ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_ashby_api_key_step_by_step_guide-1.png) 5. Set necessary API Scopes, e.g. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_ashby_api_key_step_by_step_guide-2.png) To read Applications, you will also need the "Offers" READ permission. To use Webhooks, you will also need the "API Keys" WRITE permission. 6. Click "Save and Continue" 7. Copy the API Key ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_ashby_api_key_step_by_step_guide-3.png) Generating a Ashby API Key is the first step. The harder part is building pipelines that stay reliable as customers add more systems. Unified.to gives you options: deliver data via API, streaming webhooks, database sync, or even as MCP tools for AI agents. One integration layer that adapts to your architecture instead of forcing tradeoffs. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how we simplify integration delivery across 547 integrations and 30 categories. ## How to get your Brex API token: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_brex_api_token_step_by_step_guide # How to get your Brex API token: Step-by-step guide ------ _March 21, 2025_ If you're building an integration with Brex, you'll need an API token from your customer's Brex account to authenticate requests. This guide shows you (or your customers) how to generate one from Brex's account settings. ### **Generate a user token** 1. Sign in to [dashboard.brex.com](https://dashboard.brex.com/) as an [account admin or card admin](https://developer.brex.com/docs/roles_permissions_scopes) . 2. Go to [_Developer > Settings_](https://dashboard.brex.com/settings/developer) . 3. Click _Create Token_ . 4. Create a name for your token that will help you identify it. Choose what level of data access you need for your application; these are the [scopes](https://developer.brex.com/docs/roles_permissions_scopes) your token will have. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_brex_api_token_step_by_step_guide-0.png) 5. The next screen will confirm your previous selections. Make sure it looks good, then select _Allow Access_ . ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_brex_api_token_step_by_step_guide-1.png) 6. Your token is now created. Copy and store the token securely. You won't be able to see it again. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_brex_api_token_step_by_step_guide-2.png) 7. Back on the developer page of your Brex dashboard, you should see your token listed now. As a security measure, part of the string is obfuscated. If you lose it, create a new one and replace the token. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_brex_api_token_step_by_step_guide-3.png) **Caution**: Your user token is private and should not be shared. Never check it into version control or save it somewhere publicly accessible. If your user token is compromised or leaked, make sure to revoke it. ### **Token revocation and expiration** User tokens will expire if they are not used to make an API call for 90 days. If your token is compromised, or you no longer need it, revoke the user token from the developer page in your Brex dashboard. Once revoked, any calls made with this token will immediately begin to fail. You've now created your Brex API token - just one of many if you support multiple vendors. Each one adds new auth flows, schema differences, and ongoing maintenance. Unified.to removes that overhead with unified objects that work across 475 APIs. Map once, launch everywhere. Your team ships features instead of debugging vendor-specific edge cases. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how much faster you can launch customer integrations with Unified.to. ## How to get your Ceridian Dayforce Username, Password and Client Namespace: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_ceridian_dayforce_username_password_and_client_namespace_step_by_step_guide # How to get your Ceridian Dayforce Username, Password and Client Namespace: Step-by-step guide ------ _April 24, 2023_ Developers connecting to Ceridian Dayforce will need an Username, Password and Client Namespace for authentication. Here's how to generate, configure, and copy your Ceridian Dayforce Username, Password and Client Namespace so you can use it in your integration or share steps with your customers. To get the Dayforce client namespace, follow these steps: 1. Log in to Dayforce. 2. Navigate to the "Company" tab. 3. Click on "Settings". 4. Under the "General" section, you should find the "Client Namespace" field. The value in this field is the Dayforce client namespace. Getting your Ceridian Dayforce Username, Password and Client Namespace is the first step. The bigger challenge is supporting dozens of vendors - each with unique auth flows, schemas, and maintenance requirements. Unified.to is a real-time integration platform built to replace that work. Instead of building and maintaining 514 custom connectors, you get one secure, normalized API layer across 30 SaaS tools. On top of that, our usage-based pricing scales cleanly with your product. No per-connection fees, no setup charges, and no lock-in, just infrastructure economics that grow with you. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to discover how predictable integration costs should be. ## How to get your Crelate API key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_crelate_api_key_step_by_step_guide # How to get your Crelate API key: Step-by-step guide ------ _August 18, 2023_ To access Crelate data or automate workflows through an integration, an API key is required. This guide walks you through where to find it and how to set it up for development or end-customer use. **Where Is My API Key?** Your API Key can be found in your Settings Tab under "My Settings & Preferences". You will have to enable your API Key. Once Your API is enabled, you will be presented with your API Key. If you ever need to regenerate a new API Key, you can do so by clicking "Regenerate New Key". However, please note: **Any Change in API KEY (for instance generating a new key or turning API Access off) will result in any exsisting links with the old API key to fail.** **Managing API** If you would like to manage your API, you can do so by going into your settings and clicking API Management under (Advanced Settings). You will presented with a screen that will allow you to manage users who have turned on API Access. If you click on a user who has enabled their API, administrators will have the option of turning off API Access for that user. Administrators can do this by clicking "Clear API Key". **Clearing the API key will cause any references to that API Key to fail and will turn off that users API access to that specific API key**. If that user would like API access again, they will have to regenerate another API key. To start, select your navigation menu and select "**Your Profile & Preferences**" ![mceclip1.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_crelate_api_key_step_by_step_guide-0.png) Next, navigate to **API Access** ![mceclip0.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_crelate_api_key_step_by_step_guide-1.png) You've now created your Crelate API key - just one of many if you support multiple vendors. Each one adds new auth flows, schema differences, and ongoing maintenance. Unified.to removes that overhead with unified objects that work across 514 APIs. Map once, launch everywhere. Your team ships features instead of debugging vendor-specific edge cases. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how much faster you can launch customer integrations with Unified.to. ## How to get your Dashlane API Key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_dashlane_api_key_step_by_step_guide # How to get your Dashlane API Key: Step-by-step guide ------ _March 26, 2025_ When connecting Dashlane to other platforms, authentication requires an API Key. Follow these step-by-step instructions to generate one from your Dashlane account, or provide them to your customers if they need to connect their account. ## Generate a Dashlane API key To access the Dashlane API endpoints, you'll need an API key. This key can be generated in two ways: directly through the **Admin Console** or the **Dashlane CLI**. ### **Generate an API key using Dashlane Admin Console** 1. Open the Admin Console and navigate to the **Integrations** section. 2. Select **Public API** and then **Create key**. ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_dashlane_api_key_step_by_step_guide-0.png) 3. Enter a name for the key and select **Generate key**. ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_dashlane_api_key_step_by_step_guide-1.png) 4. Copy or download the bearer token. The bearer token will be used to authenticate and access the endpoints. **Important**: Save your bearer token securely in a Secret on your vault, as it will only be displayed once. [Add and manage secrets in Dashlane](https://support.dashlane.com/hc/articles/17020356625682) ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_dashlane_api_key_step_by_step_guide-2.png) ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_dashlane_api_key_step_by_step_guide-3.png) You've now retrieved your Dashlane API Key. That's one system covered, but scaling integrations one by one means repeating this process hundreds of times. Unified.to handles this for you: 475 integrations out of the box, all with normalized schemas, real-time delivery, and Virtual Webhooks. No polling scripts, no retry logic, no maintenance backlog. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how Unified cuts integration work from quarters to days. ## How to get your Deel Access Token: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_deel_access_token_step_by_step_guide # How to get your Deel Access Token: Step-by-step guide ------ _August 26, 2023_ When connecting Deel to other platforms, authentication requires an Access Token. Follow these step-by-step instructions to generate one from your Deel account, or provide them to your customers if they need to connect their account. **Generating Access Tokens** 1. Navigate to **Apps & Integrations > Developer Center**. 2. Select the type of Token, whether Organization or Personal. The former ties the keys to the whole organization while the latter ties the keys to an individual - token will expire once this person leaves the company. ![181cd5c-accesstoken.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_deel_access_token_step_by_step_guide-0.png) 1. Add a token name and click on the **Next** button. ![8e6d69f-accessscopes.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_deel_access_token_step_by_step_guide-1.png) 1. In the popup, select the scopes for the access token and click **Generate**. `people:read` , `organizations:read`, `payslips:read`, `time-off:read` 2. Make sure to copy and save your newly generated token because you won't be able to see it again! Generating a Deel Access Token is the first step. The harder part is building pipelines that stay reliable as customers add more systems. Unified.to gives you options: deliver data via API, streaming webhooks, database sync, or even as MCP tools for AI agents. One integration layer that adapts to your architecture instead of forcing tradeoffs. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how we simplify integration delivery across 475 integrations and 29 categories. ## How to get your Dialpad API Key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_dialpad_api_key_step_by_step_guide # How to get your Dialpad API Key: Step-by-step guide ------ _April 24, 2023_ Dialpad supports authentication with an API Key. To connect, you'll need to generate an secure API Key from your account settings. This short guide shows you how to generate one, whether you're testing in your sandbox or helping customers connect their Dialpad account. To create and access your API key, you need to follow the below steps. Note that only Company Admins can create API keys on Dialpad. 1. Navigate to Admin Settings > My Company > Authentication > API Keys 2. Select **Add Key** 3. Name your key and set the expiration terms 4. Select desired **Additional Scopes** 5. Select **Save** [video](https://use.vg/wWKws7) Note: API keys can only be created by Dialpad customers on the Pro and Enterprise plans, and you must be a Company Admin to create and access an API key. You've now retrieved your Dialpad API Key. That's one system covered, but scaling integrations one by one means repeating this process hundreds of times. Unified.to handles this for you: 475 integrations out of the box, all with normalized schemas, real-time delivery, and Virtual Webhooks. No polling scripts, no retry logic, no maintenance backlog. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how Unified cuts integration work from quarters to days. ## How to get your Discord OAuth 2 credentials and bot token URL: https://docs.unified.to/guides/how_to_get_your_discord_oauth_2_credentials_and_bot_token # How to get your Discord OAuth 2 credentials and bot token ------ _May 21, 2024_ _Last updated: May 2026_ This guide explains how to register your application with Discord and obtain your OAuth 2 credentials and bot token. You'll need these credentials to activate the Discord integration and access Discord via Unified.to's unified API. ## Register your application in Discord 1. Sign into Discord and navigate to the [Developer Portal](https://discord.com/developers/applications). ![discord_1.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_discord_oauth_2_credentials_and_bot_token-0.png) 2. Click **New Application** at the top right of the page. 3. In the **Create an application** window: - Enter a name for your application - Check the box to agree to Discord's terms of service and developer policy - Click **Create** ![discord_2.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_discord_oauth_2_credentials_and_bot_token-1.png) Discord will create a new application and take you to the **General Information** tab for your application's page. Think of this application as the container for both your OAuth 2 credentials and your bot user. ## Get your Discord OAuth 2 credentials Once your application is registered, you can obtain its OAuth 2 credentials. These authenticate your app and let it access the Discord API through Unified.to. 1. On your application's page, select the **OAuth2** tab from the left sidebar. 2. Under **Client Information**, make note of your **Client ID** and **Client Secret** — you'll need these later. 3. Under **Redirects**, click **Add Redirect**. 4. Enter `https://api.unified.to/oauth/code` as the redirect URL. (If your workspace is hosted in the EU or AU region, use the corresponding regional redirect URL.) 5. Click **Save Changes** at the bottom of the page. ![discord_4.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_discord_oauth_2_credentials_and_bot_token-2.png) > **Keep these private.** Never commit your Client Secret or Bot Token to source control, share them in screenshots, or include them in client-side code. Store them in environment variables or a secrets manager. ## Retrieve your Discord bot token The Discord integration also requires a **Bot Token** to access the Discord API. 1. Select **Bot** from the left sidebar. 2. Under **Build-A-Bot > Token**, copy the **Bot Token**. 3. If no token is shown (for a new bot), click **Reset Token** to generate one, confirm in the dialog, and copy the new token that appears. > **Note:** Resetting the token invalidates the previous one. If a bot is already running in production with the existing token, resetting will break those connections until you update them. You also won't be able to view a token again after it's generated, so store it securely right away. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_discord_oauth_2_credentials_and_bot_token-3.png) ## Configure bot permissions and intents Enable the permissions and privileged gateway intents that match the data your integration accesses. Enable only what you need — Unified.to maps each capability to the minimum required access. The Discord integration supports messages and channels (via the Messaging API) and members (via the HRIS API): - **To list channels** (Messaging Channel `list`/`get`): no privileged intent is required. The bot needs the standard **View Channels** permission in the server. - **To read or write messages** (Messaging Message `list`/`create`/`update`/`get`/`remove`): enable **Message Content Intent** under **Privileged Gateway Intents**. Note that Message Content Intent is gated by Discord and may require verification once your bot is in a larger number of servers. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_discord_oauth_2_credentials_and_bot_token-4.png) - **To read members** (HRIS Employee `list`/`get`, which maps Discord guild members to the unified employee object — a Unified.to abstraction, not a Discord-native concept): enable **Server Members Intent** under **Privileged Gateway Intents**. (Discord's documentation refers to this as the Guild Members Intent; the developer portal UI labels it "Server Members Intent.") After enabling the intents you need, click **Save Changes** at the bottom of the page. Granting intents or permissions you don't use increases risk and can complicate Discord's review process once your bot reaches a larger number of servers. ## Activate Discord in Unified.to 1. Navigate to [Integrations](https://app.unified.to/integrations) in Unified.to. 2. Find and click on the **Discord** integration card. 3. On the integration details page, select **Your OAuth 2 credentials**, then enter your: - Discord **Client ID** - Discord **Client Secret** - Discord **Bot Token** 4. Click **Activate** to save your changes and enable the Discord integration. ![discord_7.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_discord_oauth_2_credentials_and_bot_token-5.png) You can now use the Unified API to build new experiences with Discord. → [Start your 30-day free trial](https://app.unified.to/login) → [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) ## How to get your ELMO client ID and client secret: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_elmo_client_id_and_client_secret_step_by_step_guide # How to get your ELMO client ID and client secret: Step-by-step guide ------ _December 11, 2023_ ELMO supports authentication with client ID and client secret. To connect, you'll need to generate secure client ID and client secret from your account settings. This short guide shows you how to generate one, whether you're testing in your sandbox or helping customers connect their ELMO account. **Authentication Methods** ELMO uses three main credentials for API authentication: - **Client ID:** A unique identifier for your application. - **Client Secret:** A secure key used in conjunction with the Client ID. **Step-by-Step Setup** 1. **Request Access:** Contact your **ELMO Account Manager** to confirm your organization has API access enabled. 2. **Assign Permissions:** - Log in to the **ELMO Administration portal**. - Navigate to **Security Profiles** and add a profile with the **API Access Manager**role. - Assign this profile to the specific user who will manage the integration. 3. **Generate Credentials:** - Have a Company Admin access the **API Management console**. - Generate and securely record the **Client ID** and **Client Secret**. You've now created your ELMO client ID and client secret - just one of many if you support multiple vendors. Each one adds new auth flows, schema differences, and ongoing maintenance. Unified.to removes that overhead with unified objects that work across 475 APIs. Map once, launch everywhere. Your team ships features instead of debugging vendor-specific edge cases. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how much faster you can launch customer integrations with Unified.to. ## How to get your Gainsight Client ID, Client Secret and Gainsight API Domain: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_gainsight_client_id_client_secret_and_gainsight_api_domain_step_by_step_guide # How to get your Gainsight Client ID, Client Secret and Gainsight API Domain: Step-by-step guide ------ _November 15, 2025_ Gainsight supports authentication with Client ID, Client Secret and Gainsight API Domain. To connect, you'll need to generate secure Client ID, Client Secret and Gainsight API Domain from your account settings. This short guide shows you how to generate one, whether you're testing in your sandbox or helping customers connect their Gainsight account. To generate the M2M OAuth key: 1. Navigate to **Administration > Connectors 2.0**. 2. Click **Create Connection**. The Create Connection dialog appears. 3. From the **Connector** dropdown list, select **Gainsight API**. 4. In the **Name of the connection** field, enter the name of the connection. 5. In the **Authentication Type**, select the OAuth. 6. Click **Generate OAuth Credentials**. New OAuth API Key and OAuth API Secret keys are generated, which you can copy and use for authentication in all of the REST API requests to Gainsight. **Note**: - M2M OAuth can be created and managed only by super admins. - M2M OAuth cannot be used for Event APIs. - Two different M2M connections cannot have the same name for a single connection. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_gainsight_client_id_client_secret_and_gainsight_api_domain_step_by_step_guide-0.png) Generating a Gainsight Client ID, Client Secret and Gainsight API Domain is the first step. The harder part is building pipelines that stay reliable as customers add more systems. Unified.to gives you options: deliver data via API, streaming webhooks, database sync, or even as MCP tools for AI agents. One integration layer that adapts to your architecture instead of forcing tradeoffs. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how we simplify integration delivery across 475 integrations and 29 categories. ## How to get your Greenhouse API Key and Job board token: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_greenhouse_api_key_and_job_board_token_step_by_step_guide # How to get your Greenhouse API Key and Job board token: Step-by-step guide ------ _April 24, 2023_ Developers connecting to Greenhouse will need an API Key and Job board token for authentication. Here's how to generate, configure, and copy your Greenhouse API Key and Job board token so you can use it in your integration or share steps with your customers. 1. To create a Greenhouse "Harvest" API key, click the Configure icon on your Greenhouse navigation bar and select "_Dev Center_" on the left. ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_greenhouse_api_key_and_job_board_token_step_by_step_guide-0.png) 1. Click "_API Credential Management_". ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_greenhouse_api_key_and_job_board_token_step_by_step_guide-1.png) 1. Click "_Create New API Key_", and select "_Harvest_" for the API Type. ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_greenhouse_api_key_and_job_board_token_step_by_step_guide-2.png) 1. Click "_Manage Permissions_". ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_greenhouse_api_key_and_job_board_token_step_by_step_guide-3.png) 2. Copy your Harvest API key to a secure location then click _"I have stored the API key"_. ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_greenhouse_api_key_and_job_board_token_step_by_step_guide-4.png) 3. Set Jobs, Job Posts, and Candidates, Applications, Scorecards permissions. ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_greenhouse_api_key_and_job_board_token_step_by_step_guide-5.png) 4. Click Save 5. Optionally, you can specify the job board token for your organization. Go to the "_Configure Job Boards_" page. (Configure gear icon > Job Boards & Posts) 6. Find and edit the job board. (Ellipsis icon > Edit Board Settings) ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_greenhouse_api_key_and_job_board_token_step_by_step_guide-6.png) 1. On the _Edit Your Job Board_ page, find the _URL_ section. Copy this value as this is the token. ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_greenhouse_api_key_and_job_board_token_step_by_step_guide-7.png) Getting your Greenhouse API Key and Job board token is the first step. The bigger challenge is supporting dozens of vendors - each with unique auth flows, schemas, and maintenance requirements. Unified.to is a real-time integration platform built to replace that work. Instead of building and maintaining 558 custom connectors, you get one secure, normalized API layer across 30 SaaS tools. On top of that, our usage-based pricing scales cleanly with your product. No per-connection fees, no setup charges, and no lock-in, just infrastructure economics that grow with you. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to discover how predictable integration costs should be. ## How to get your Helpscout API Key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_helpscout_api_key_step_by_step_guide # How to get your Helpscout API Key: Step-by-step guide ------ _June 8, 2024_ Developers connecting to Helpscout will need an API Key for authentication. Here's how to generate, configure, and copy your Helpscout API Key so you can use it in your integration or share steps with your customers. To generate, view, or regenerate the API key, click the "person" icon on the top right of your account, next to the search, then click **Your Profile**. ![your-profile.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_helpscout_api_key_step_by_step_guide-0.png) Next, click the **Authentication** link in the menu on the left and select **API Keys** tab ![api-keys.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_helpscout_api_key_step_by_step_guide-1.png) **Note:** If you don't see an API key in your profile, that's because you haven't been granted the "Docs: Create new, edit settings & Collections" permission by the Account Owner or an Administrator. Generating a Helpscout API Key is the first step. The harder part is building pipelines that stay reliable as customers add more systems. Unified.to gives you options: deliver data via API, streaming webhooks, database sync, or even as MCP tools for AI agents. One integration layer that adapts to your architecture instead of forcing tradeoffs. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how we simplify integration delivery across 475 integrations and 29 categories. ## How to get your HiBob Service User ID and Service User Token: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_hibob_service_user_id_and_service_user_token_step_by_step_guide # How to get your HiBob Service User ID and Service User Token: Step-by-step guide ------ _April 24, 2023_ If you're building an integration with HiBob, you'll need an Service User ID and Service User Token from your customer's HiBob account to authenticate requests. This guide shows you (or your customers) how to generate one from HiBob's account settings. ## **Step 1: Create a service user** Service Users are created and managed in the Service Users section in Bob. 1. **If you have direct access to Bob:** Go to the Service Users configuration page to create a new user and copy the ID and token. See [Manage service users](https://help.hibob.com/hc/en-us/articles/27875098648465-Manage-service-users). 2. **If you don't have direct access to Bob**: Ask a Bob Admin to generate the credentials for you. You'll need this ID and token to authenticate your API requests. ## **Step 2: Creating a permission group** By default, service users have no access permissions. To assign them access: 1. Create a dedicated Permission Group. 2. Add the Service User to this group. This group should include only the permissions required for the API operations the service user will perform. - **If you have access to Bob:** [Create a service user permission group](https://help.hibob.com/hc/en-us/articles/29550415706897). - **If not**: Ask a Bob Admin to create one and assign the user to it. ## **Step 3: Set permissions** A new permission group does not have any permissions enabled by default. **Notes:** - **Ask Bob Admin** to help determine which permissions are required for this service user. To learn more, see [Manage service users](https://help.hibob.com/hc/en-us/articles/27875098648465). - **Default employee data permissions**: To read basic employee data via the People search API, assign the Default Employee Fields permissions. This includes access to the "root," "about," "employment," and "work" categories. To learn more, see [Permissions for Default Employee Fields in People Search API](https://help.hibob.com/hc/en-us/articles/27875098648465#h_01JB9DYK9SZEJ7MF4DA7RHJE03). Permissions are grouped into: ### **3.1 Features** Grant access to Bob features you need to access via the API you plan to use with this service user. For example, if you want to access reports, you may need to activate some options in **Features > Reports**. 1. From the top right, click **Edit**. 2. Select the area of Bob you'd like to manage. 3. Check or uncheck the permissions this group should have. ![e2e3b78-image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_hibob_service_user_id_and_service_user_token_step_by_step_guide-0.png) ### **3.2 People's data** Grant access to people's data in each area of Bob for the API you plan to use with this service user. 1. From the top right, click **Edit**. 2. Select the area of Bob you'd like to manage. 3. Mark the checkbox(es) to enable or disable the permissions you'd like to give the service users in this group. ![5da0461-image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_hibob_service_user_id_and_service_user_token_step_by_step_guide-1.png) **'View', 'edit' and 'view history'** To access Employee data via the API you need to grant basic view access to each category: - **View all employees' [Category name] sections** If you want to access history data (such as all the entries in the Lifecycle or Work table), you also need to grant the history access: - **View all employees' [Category name] section histories**. If you want to update data or access sensitive data that requires an extra layer of permissions, you need to grant the edit permission: - **Edit all employees' [Category name] sections** ### **3.3 Access rights** Grant access to the people whose data you want to access via the API you plan to use with this service user. 1. From the **People's data** tab, click **Access data for**. 2. Select whose data these permissions apply to (by default, the access rights are set to all employees using the **Lifecycle status equals Any** condition). Options are: - **Everyone**: all employees marked as Employed in the system. > **Note**: By default, the service user's permission group grants access to all **active** employees. To access inactive employees you need to use **select by condition** and remove the condition: **Lifecycle status equals Employed** from the permission group's **Access data for** settings. - **Select people by condition**: click **Edit**, select conditions from the dropdown menu, then click **Apply**. - **Select people by name**: click **Edit**, select specific employees, then click **Apply**. ![c66efe9d529d03feeac57366db8107db3dec560db38cf0c6bd9910e1bcc618d3-image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_hibob_service_user_id_and_service_user_token_step_by_step_guide-2.png) To learn more, see [Create a service user permission group](https://help.hibob.com/hc/en-us/articles/29550415706897). You've now created your HiBob Service User ID and Service User Token - just one of many if you support multiple vendors. Each one adds new auth flows, schema differences, and ongoing maintenance. Unified.to removes that overhead with unified objects that work across 546 APIs. Map once, launch everywhere. Your team ships features instead of debugging vendor-specific edge cases. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how much faster you can launch customer integrations with Unified.to. ## How to get your HighLevel API Key and Location ID: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide # How to get your HighLevel API Key and Location ID: Step-by-step guide ------ _April 24, 2023_ HighLevel supports authentication with API Key and Location ID. To connect, you'll need to generate secure API Key and Location ID from your account settings. This short guide shows you how to generate one, whether you're testing in your sandbox or helping customers connect their HighLevel account. This guide walks you through creating a **HighLevel token-based connection** using a **sub-account (location) API key** in Unified. --- ## ⚠️ Important Context HighLevel operates on two levels: - **Agency level (company)** - **Sub-account level (location)** ✅ _(token-based authentication works only here)_ To access **CRM** and **HRIS** data, you must use a **sub-account (location)** API key. > **Note:** HighLevel offers multiple ways to create connections. To avoid issues, follow every step in this guide carefully. --- ## ✅ Prerequisites - You must have **Admin** or **Owner** access to the sub-account. ## 🔧 Step-by-Step Setup ### 1. Navigate to Your Sub-Account Go to your **HighLevel sub-account (location)**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide-0.png) ### 2. Open Private Integrations - Go to **Settings → Private Integrations** _(inside the sub-account)_ ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide-1.png) ### 3. Create a New Integration - Click **Create Integration** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide-2.png) ### 4. Name Your Integration - Provide a clear name for your token (e.g., `Unified Integration`) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide-3.png) ### 5. Select Required Scopes Choose scopes based on your use case: | Permission | Resource | HighLevel Scopes | | ---------- | ------------- | ------------------------------------------------ | | Read | CRM Contact | `contacts.readonly` | | Write | CRM Contact | `contacts.write` | | Read | CRM Company | `businesses.readonly`, `locations/tags.readonly` | | Write | CRM Company | `businesses.write`, `locations/tags.write` | | Read | CRM Deal | `opportunities.readonly` | | Write | CRM Deal | `opportunities.write` | | Read | HRIS Employee | `users.readonly` | | Write | HRIS Employee | `users.write` | | Read | CRM Lead | `contacts.readonly` | | Write | CRM Lead | `contacts.write` | | Read | CRM Pipeline | `opportunities.readonly` | | Read | CRM Event | `forms.readonly`, `contacts.readonly` | | Write | CRM Event | `contacts.write` | **Reference:** [https://marketplace.gohighlevel.com/docs/Authorization/Scopes](https://marketplace.gohighlevel.com/docs/Authorization/Scopes) ### 6. Create and Confirm - Click **Create** - Then click **Confirm** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide-4.png) ### 7. Copy API Token - You will receive an **API Token** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide-5.png) - Store it securely (you won't be able to retrieve it later) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide-6.png) ### 8. Get Location ID To find your **Location ID**: ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide-7.png) - Look at the URL of your sub-account Example: ```plain text https://app.gohighlevel.com/v2/location/tG38xt5twyoGISw4o92p/settings/private-integrations/... ``` - The value after `/location/` is your **Location ID** → `tG38xt5twyoGISw4o92p` ### 9. Connect in Unified - Go to: [https://app.unified.to/integrations/highlevel](https://app.unified.to/integrations/highlevel) - Select **API TOKEN** - Click **Activate** --- ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide-8.png) ### 10. Create Connection - Paste: - **API Token** - **Location ID** - Click **Create Connection** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_highlevel_api_key_and_location_id_step_by_step_guide-9.png) ## ✅ Done Your HighLevel token-based connection should now be successfully set up in Unified. --- If it doesn't work, double-check: - You created the token at the **sub-account level** - The correct **scopes** are selected - The **Location ID** matches the sub-account If you have any more doubts or issues, please contact the [Unified.to](https://unified.to/) team for support. You've now created your HighLevel API Key and Location ID - just one of many if you support multiple vendors. Each one adds new auth flows, schema differences, and ongoing maintenance. Unified.to removes that overhead with unified objects that work across 546 APIs. Map once, launch everywhere. Your team ships features instead of debugging vendor-specific edge cases. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how much faster you can launch customer integrations with Unified.to. ## How to get your HubSpot developer key and OAuth 2 credentials (Legacy Apps) URL: https://docs.unified.to/guides/how_to_get_your_hubspot_developer_key_and_oauth_2_credentials_legacy_apps # How to get your HubSpot developer key and OAuth 2 credentials (Legacy Apps) ------ _February 12, 2023_ This guide will show you how to retrieve your OAuth 2 credentials and developer API key in HubSpot. **Note**: HubSpot allows you to create different types of apps - public and private. This guide is about public apps. ## Register a HubSpot Developer Account First, you'll need to register for a free account on HubSpot if you haven't already. 1. Navigate to the [HubSpot Developers portal](https://developers.hubspot.com/get-started). 2. Click **Create App Developer Account.** 3. Follow the onboarding to complete your account setup. ## Get your HubSpot Developer API key Developer account API keys are separate from standard API keys - they can be used to manage subscriptions for webhooks and other HubSpot features. 1. Inside the HubSpot developer dashboard, open the sidebar and click [Apps](https://app.hubspot.com/developer/47094559/applications). 2. Click **Get HubSpot API key**. 3. Follow the instructions to create a Developer API key. 4. Click **Show key** to reveal your key. You will need this for the final step. ## Create a HubSpot app A HubSpot app is a container for your integration settings and is where you'll create and find your OAuth 2 credentials. 1. On the Development page → Legacy Apps→ click **Create Legacy App →** Select **Public App.** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_hubspot_developer_key_and_oauth_2_credentials_legacy_apps-0.png) 1. Give your app a name and description. 2. Click on the **Auth** tab. 3. Under **Redirect URLs,** enter: **`https://api.unified.to/oauth/code`** ![Screenshot_2024-08-16_at_3.41.50_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_hubspot_developer_key_and_oauth_2_credentials_legacy_apps-1.png) 4. Under **Scopes,** click **Add new scopes** and select the permissions that your application will require. For example, if your application reads Deals, then select `crm.deal.read` ![ba5f5e7e-5c03-49b4-8662-75d3a50f4f01.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_hubspot_developer_key_and_oauth_2_credentials_legacy_apps-2.png) 5. Click **Create app** to finish setting up your app. ## (optional) Add the `oauth` scope to your app For apps created before April 2024, the `oauth` scope may be missing from your app's auth settings. Apps created after this date have it on by default. This scope is required for the HubSpot integration to work. 1. Under **Scopes**, click **Add new scope.** 2. Search for **oauth** and select it. 3. Click **Update**. ## Get your OAuth 2 credentials 1. After creating your app, you will be redirected to the App Info tab. Click again on the **Auth** tab. 2. Your unique **Client ID and Client secret** will be displayed. You will need these for the next step. ![Screenshot_2024-08-16_at_3.52.29_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_hubspot_developer_key_and_oauth_2_credentials_legacy_apps-3.png) ## Activate the HubSpot integration in Unified.to with your client ID and secret 1. Open a new tab and navigate to [the HubSpot integration in Unified.to](https://app.unified.to/integrations/hubspot) 2. You will need the following three pieces of information from the earlier steps: 1. **Client ID** 2. **Client secret** 3. **Developer API key** from the developer dashboard 3. Copy and paste these values into their respective fields on the page. Make sure that **Your OAuth 2 credentials** is selected above. ![Screenshot_2024-08-16_at_4.19.04_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_hubspot_developer_key_and_oauth_2_credentials_legacy_apps-4.png) ## See also - [How to set up your scopes in HubSpot](https://docs.unified.to/guides/how_to_set_up_your_scopes_in_hubspot) - [How to configure webhooks in HubSpot](https://docs.unified.to/guides/how_to_configure_webhooks_in_hubspot) ## How to get your Humaans API Key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_humaans_api_key_step_by_step_guide # How to get your Humaans API Key: Step-by-step guide ------ _November 14, 2023_ When connecting Humaans to other platforms, authentication requires an API Key. Follow these step-by-step instructions to generate one from your Humaans account, or provide them to your customers if they need to connect their account. 1. Log in to the Humaans Admin Dashboard 2. Go to Settings 3. Open API access tokens 4. Click Create new token 5. Give it a name (e.g. "Unified.to") 6. Copy the generated token 7. Paste it into the API Token field 8. Click Authorize **Note:** You'll need **Admin access** in Humaans to see "API access tokens." You've now retrieved your Humaans API Key. That's one system covered, but scaling integrations one by one means repeating this process hundreds of times. Unified.to handles this for you: 475 integrations out of the box, all with normalized schemas, real-time delivery, and Virtual Webhooks. No polling scripts, no retry logic, no maintenance backlog. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how Unified cuts integration work from quarters to days. ## How to get your iCIMS API Username, API Password and Customer ID: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_icims_api_username_api_password_and_customer_id_step_by_step_guide # How to get your iCIMS API Username, API Password and Customer ID: Step-by-step guide ------ _April 24, 2023_ When connecting iCIMS to other platforms, authentication requires a API Username, API Password and Customer ID. Follow these step-by-step instructions to generate one from your iCIMS account, or provide them to your customers if they need to connect their account. # How to get your iCIMS API credentials This guide explains how to obtain the required credentials for connecting iCIMS. You'll need to work with iCIMS support to get these credentials. ## Required credentials To connect iCIMS, you'll need: - Customer ID (your unique platform identifier) - API Username - API Password ## Steps to obtain your credentials ### Step 1: Contact iCIMS support 1. Reach out to your iCIMS account representative or support team 2. Request API access for integration purposes 3. Specify that you need: - Customer ID - API Username - API Password ### Step 2: Receive your credentials iCIMS support will provide you with: - Your unique Customer ID - API Username - API Password ### Step 3: Verify access 1. Once you receive your credentials, keep them in a secure location 2. Enter the credentials in the inputs on this page to create a connection to your iCIMS account ## Important notes - These credentials must be provided by iCIMS support - The Customer ID is unique to your organization Getting your iCIMS API Username, API Password and Customer ID is the first step. The bigger challenge is supporting dozens of vendors - each with unique auth flows, schemas, and maintenance requirements. Unified.to is a real-time integration platform built to replace that work. Instead of building and maintaining 514 custom connectors, you get one secure, normalized API layer across 30 SaaS tools. On top of that, our usage-based pricing scales cleanly with your product. No per-connection fees, no setup charges, and no lock-in, just infrastructure economics that grow with you. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to discover how predictable integration costs should be. ## How to get your JobDiva Client ID, Username/email and Password: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_jobdiva_client_id_username_email_and_password_step_by_step_guide # How to get your JobDiva Client ID, Username/email and Password: Step-by-step guide ------ _August 18, 2023_ If you're building an integration with JobDiva, you'll need an Client ID, Username/email and Password from your customer's JobDiva account to authenticate requests. This guide shows you (or your customers) how to generate one from JobDiva's account settings. # Obtain a JobDiva Client ID Contact JobDiva Support to obtain a Client ID to use for your integration. # Create a JobDiva API User A JobDiva admin must establish a dedicated API username and password. In JobDiva: 1. Navigate to Settings > My Team > Add User. 2. Add a new user. 3. Allow to access JobDiva API Calls permission for the user. With your JobDiva Client ID, Username/email and Password in hand, you're ready to connect. But managing keys, tokens, and refresh flows across every vendor quickly becomes a security risk. Unified.to abstracts all of that behind a single, secure layer. We never store customer data, credentials are always under your control, and our permissioning model removes the risk of misconfigured scopes. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how Unified keeps integrations real-time, compliant, and safe at scale. ## How to get your Jobvite API Key, API Key Secret and Email: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_jobvite_api_key_api_key_secret_and_email_step_by_step_guide # How to get your Jobvite API Key, API Key Secret and Email: Step-by-step guide ------ _August 18, 2023_ To access Jobvite data or automate workflows through an integration, a API Key, API Key Secret and Email are required. This guide walks you through where to find it and how to set it up for development or end-customer use. ## How do I get my Jobvite API key and secret? Your API key and secret are both provided by Jobvite's Customer Success team. To request API credentials, file a support ticket at: [https://www.jobvite.com/support/submit-a-case/](https://www.jobvite.com/support/submit-a-case/) (Note: you will need to be logged in to submit a case. If you don't see a button titled **Submit a Request**, reach out to your account representative i.e. your Customer Success Manager or Account Manager) ### Submit a ticket - Select the **issue type** from the dropdown menu - Add a **Subject** - Add a detailed **description** of the request: - **API names** that you want to grant access to - **Email** of a dedicated Jobvite user to determine that an update was made by the API and not an actual user. This email address needs to accept the Jobvite registration process. Assign the **Administrator** role to this user. Alternatively, you may start a support chat (see bottom right corner of Help Center) to chat with their support team for guidance on retrieving an API Key. Please make sure that Jobvite gives you access to: - New User module - Enable New Contact Details UI **NOTE:** Please note that access to the Contacts API requires that the Jobvite Customer has enabled the Jobvite Engage product on their account. The contacts API is not supported for customers who have not enabled the Jobvite Engage product (additional fee required). JobVite Contacts are required for access to Candidates. You've now retrieved your Jobvite API Key, API Key Secret and Email. That's one system covered, but scaling integrations one by one means repeating this process hundreds of times. Unified.to handles this for you: 652 integrations out of the box, all with normalized schemas, real-time delivery, and Virtual Webhooks. No polling scripts, no retry logic, no maintenance backlog. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how Unified cuts integration work from quarters to days. ## How to get your Lever API Key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_lever_api_key_step_by_step_guide # How to get your Lever API Key: Step-by-step guide ------ _April 24, 2023_ When connecting Lever to other platforms, authentication requires an API Key. Follow these step-by-step instructions to generate one from your Lever account, or provide them to your customers if they need to connect their account. 1. To generate API credentials, navigate to Settings > Integrations and API > API Credentials. 2. Click the Generate New Key button ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_lever_api_key_step_by_step_guide-0.png) 3. Input a name for the key that reflects the service or integration that will be using the key 4. Under the 'Permissions' heading, select "Select all" ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_lever_api_key_step_by_step_guide-1.png) 5. Click the Copy Key button next to the API key (which can be found next to the 'Key name' field). ![image](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_lever_api_key_step_by_step_guide-2.png) 6. Click done Generating a Lever API Key is the first step. The harder part is building pipelines that stay reliable as customers add more systems. Unified.to gives you options: deliver data via API, streaming webhooks, database sync, or even as MCP tools for AI agents. One integration layer that adapts to your architecture instead of forcing tradeoffs. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how we simplify integration delivery across 606 integrations and 32 categories. ## How to get your Loxo Agency slug, API Key and Agency ID: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_loxo_agency_slug_api_key_and_agency_id_step_by_step_guide # How to get your Loxo Agency slug, API Key and Agency ID: Step-by-step guide ------ _August 18, 2023_ If you're building an integration with Loxo, you'll need an Agency slug, API Key and Agency ID from your customer's Loxo account to authenticate requests. This guide shows you (or your customers) how to generate one from Loxo's account settings. ### Where do I find my API Key? To get your API Key, we'll need to go to Loxo. Once there, click on your profile icon and then Settings. ![Loxo-Settings-1024x565.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_loxo_agency_slug_api_key_and_agency_id_step_by_step_guide-0.png) In the Settings view, look for the API Keys card and click on it. ![Loxo-Settings-API-1024x577.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_loxo_agency_slug_api_key_and_agency_id_step_by_step_guide-1.png) In the API Keys view, click the Add button. ![Loxo-Settings-API-Key-1024x571.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_loxo_agency_slug_api_key_and_agency_id_step_by_step_guide-2.png) ### How can I get my Agency Slug? To get your agency slug, navigate to your agency's career page. You can find it under your profile's menu. ![Loxo-Career-Page-1024x570.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_loxo_agency_slug_api_key_and_agency_id_step_by_step_guide-3.png) On the Careers page, your agency slug will be the first part of the path, after the domain. **Example**: `https://app.loxo.co/weblime-consultants` = _weblime-consultants_ You've now retrieved your Loxo Agency slug, API Key and Agency ID. That's one system covered, but scaling integrations one by one means repeating this process hundreds of times. Unified.to handles this for you: 524 integrations out of the box, all with normalized schemas, real-time delivery, and Virtual Webhooks. No polling scripts, no retry logic, no maintenance backlog. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how Unified cuts integration work from quarters to days. ## How to get your Microsoft Active Directory / Entra ID Client ID, Client Secret and Tenant ID: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_microsoft_active_directory_entra_id_client_id_client_secret_and_tenant_id_step_by_step_guide # How to get your Microsoft Active Directory / Entra ID Client ID, Client Secret and Tenant ID: Step-by-step guide ------ _April 24, 2023_ Developers connecting to Microsoft Active Directory / Entra ID will need an Client ID, Client Secret and Tenant ID for authentication. Here's how to generate, configure, and copy your Microsoft Active Directory / Entra ID Client ID, Client Secret and Tenant ID so you can use it in your integration or share steps with your customers. # Microsoft AD/Entra ID Client ID and Secret Authentication ## **Step: 1 Register an application** Registering your application establishes a trust relationship between the 3rd-party application and the Microsoft identity platform. The trust is unidirectional: the application trusts the Microsoft identity platform, and not the other way around. Once created, the application object can't be moved between different tenants. 1. Sign in to the [Microsoft Entra admin center](https://entra.microsoft.com/). 2. If you have access to multiple tenants, use the **Settings** icon in the top menu to switch to the tenant in which you want to register the application from the **Directories + subscriptions** menu. 3. Browse to **Identity** > **Applications** > **App registrations** and select **New registration**. 4. Enter a display **Name** for the application. 5. Specify who can use the application in the **Supported account types** section 6. Select **Register** to complete the initial app registration. ![portal-02-app-reg-01.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_active_directory_entra_id_client_id_client_secret_and_tenant_id_step_by_step_guide-0.png) When registration finishes, the Microsoft Entra admin center displays the app registration's **Overview** pane. On this page, the app was assigned values for: - **Application (client) ID** which uniquely identifies your application in the Microsoft cloud ecosystem, across all tenants. - **Object ID** which uniquely identifies your application in your tenant. ![portal-03-app-reg-02.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_active_directory_entra_id_client_id_client_secret_and_tenant_id_step_by_step_guide-1.png) ## Step 2: Generate Client Secret Credentials are used by [confidential client applications](https://learn.microsoft.com/en-us/entra/identity-platform/msal-client-applications) that access a web API. Examples of confidential clients are web apps, other web APIs, or service-type and daemon-type applications. Credentials allow your application to authenticate as itself, requiring no interaction from a user at runtime. ![portal-05-app-reg-04-credentials.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_active_directory_entra_id_client_id_client_secret_and_tenant_id_step_by_step_guide-2.png) For this case, go to the Client Secrets tab, to generate a new Client Secret. The generated secret's value will be the one that will be used in Unified's authentication process. ## **Step 3: Configure permissions for Microsoft Graph** Microsoft Graph exposes [application permissions](https://learn.microsoft.com/en-us/graph/permissions-overview#application-permissions) for apps that call Microsoft Graph with their own identity. These permissions always require administrator consent. Preconfigure the application permissions the app needs when you register the app. An administrator can consent to these permissions either by using the [Microsoft Entra admin center](https://entra.microsoft.com/) when they install the app in their organization, or you can provide a sign-up experience in the app through which administrators can consent to the permissions you configured. Once Microsoft Entra ID records the administrator consent, the app can request tokens without having to request consent again. To configure application permissions for the app in the app registrations experience on the Microsoft Entra admin center, follow these steps: - On the application's **API permissions** page, choose **Add a permission**. - Select **Microsoft Graph** > select **Application permissions**. - In the **Select Permissions** dialog, choose the permissions to configure to the app. The following screenshot shows the **Select Permissions** dialog box for Microsoft Graph application permissions. ![Screenshot_2025-09-18_at_11.37.04_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_active_directory_entra_id_client_id_client_secret_and_tenant_id_step_by_step_guide-3.png) ## **Step 4: Grant tenant-wide admin consent in Enterprise apps pane** You can grant tenant-wide admin consent through the **Enterprise applications** pane if the application is already provisioned in your tenant. For example, an app could be provisioned in your tenant if at least one user consents to the application. For more information, see [How and why applications are added to Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity-platform/how-applications-are-added). To grant tenant-wide admin consent to an app listed in **Enterprise applications** pane: 1. Sign in to the [Microsoft Entra admin center](https://entra.microsoft.com/) as at least a [Cloud Application Administrator](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#cloud-application-administrator). 2. Browse to **Entra ID** > **Enterprise apps** > **All applications**. 3. Enter the name of the existing application in the search box, and then select the application from the search results. 4. Select **Permissions** under **Security**. ![grant-tenant-wide-admin-consent.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_active_directory_entra_id_client_id_client_secret_and_tenant_id_step_by_step_guide-4.png) 5. Carefully review the permissions that the application requires. If you agree with the permissions the application requires, select **Grant admin consent**. ## Step 5: Setup Authentication in Unified Setup the Authentication credentials in Unified using the client ID, and generated secret, along with the tenant ID ![Screenshot_2025-09-18_at_12.09.32_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_active_directory_entra_id_client_id_client_secret_and_tenant_id_step_by_step_guide-5.png) You've now created your Microsoft Active Directory / Entra ID Client ID, Client Secret and Tenant ID - just one of many if you support multiple vendors. Each one adds new auth flows, schema differences, and ongoing maintenance. Unified.to removes that overhead with unified objects that work across 514 APIs. Map once, launch everywhere. Your team ships features instead of debugging vendor-specific edge cases. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how much faster you can launch customer integrations with Unified.to. ## How to get your Microsoft Azure AD OAuth 2 credentials URL: https://docs.unified.to/guides/how_to_get_your_microsoft_azure_ad_oauth_2_credentials # How to get your Microsoft Azure AD OAuth 2 credentials ------ _June 3, 2024_ This guide will show you how to obtain your OAuth 2 credentials for Microsoft Azure Active Directory (Azure AD), which are also needed for other integrations in the Microsoft Azure ecosystem e.g. Outlook, Teams, and Dynamics. ## Register an app in Microsoft Azure Portal 1. Navigate to [Microsoft Azure Portal](https://portal.azure.com/) 2. Search for **Azure Active Directory** and select it. ![oidc-microsoft-1.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_azure_ad_oauth_2_credentials-0.png) 3. From the sidebar, under Manage, click **App registrations** and then **New registration.** ![oidc-microsoft-2.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_azure_ad_oauth_2_credentials-1.png) 4. Fill in the following form to register a new app: 1. Enter any name you'd like for your app. 2. Select **Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)** to allow anyone to log in. 3. Under **Redirect URI**, enter [`https://api.unified.to/oauth/code`](https://api.unified.to/oauth/code) 4. Click **Register**. ![oidc-microsoft-3.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_azure_ad_oauth_2_credentials-2.png) ## Get your client ID and client secret 1. After registering your app and on the resulting page, copy the **Client ID** (found next to **Application (client) ID**) 2. Click on **Add a certificate or secret** (found next to **Client credentials)** ![oidc-microsoft-4.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_azure_ad_oauth_2_credentials-3.png) 3. Under **Client secrets (0)**, click **New client secret**. ![oidc-microsoft-5.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_azure_ad_oauth_2_credentials-4.png) 4. Click **Add** - do not change anything else in the dialog. ![oidc-microsoft-6.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_azure_ad_oauth_2_credentials-5.png) 5. On the next page, copy the **Value** ![oidc-microsoft-7.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_azure_ad_oauth_2_credentials-6.png) ## Enter your OAuth credentials on Unified.to 1. Take note of your Microsoft client ID and client secret from the above steps 2. Navigate to the [app.unified.to](https://app.unified.to/) and find the integration page for the Microsoft integration you are interested in e.g. [https://app.unified.to/integrations/microsoft_ad](https://app.unified.to/integrations/microsoft_ad) 3. Enter your client ID 4. Enter your client secret ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_azure_ad_oauth_2_credentials-7.png) 5. Make sure that you enable the correct permission scopes for your application. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_microsoft_azure_ad_oauth_2_credentials-8.png) ## Troubleshooting **I'm getting an 'invalid client secret' error** This error indicates a problem with the client ID and/or secret that you generated. Please ensure you copied the correct values to the integration settings page on Unified.to, or regenerate these credentials in Microsoft Azure Active Directory and then try again. ## How to get your OAuth 2 credentials for Gmail URL: https://docs.unified.to/guides/how_to_get_your_oauth_2_credentials_for_gmail # How to get your OAuth 2 credentials for Gmail ------ _June 25, 2024_ _Last updated: May 2026_ To access Google services such as Gmail (also known as Google Mail) through the [Unified API](https://unified.to/technology), you'll generate and retrieve your [OAuth](https://unified.to/embeddedauth) 2 credentials in the Google Cloud Console, then enter them in Unified.to. ## Create or select a project on Google Cloud Console 1. Navigate to the [Google Cloud Console](https://console.cloud.google.com/). 2. From the top nav bar, select an existing project or create a new one. - If creating a new project, name it whatever you want and click **Create**. ![Screenshot_2024-09-09_at_9.56.18_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_oauth_2_credentials_for_gmail-0.png) ## Enable the Gmail API 1. With your project selected, open the left sidebar and navigate to **APIs & Services > Library** (or search for **Library** from the top nav bar). 2. Search for **Gmail API** and click the matching result. 3. Click **Enable**. ## Configure your OAuth consent screen Google requires you to configure the OAuth consent screen before it will let you create an OAuth client ID. The consent screen is what users see when they grant your app access to their Gmail. 1. Open the left sidebar and navigate to **APIs & Services > OAuth consent screen**. 2. Choose the user type: - **Internal** — for Google Workspace organization-internal apps. - **External** — for consumer Gmail accounts or public apps. 3. Fill in the required fields: app name, user support email, and developer contact information (and, for production, your authorized domains). 4. Complete the consent screen setup. ## Get your OAuth 2 credentials 1. Navigate to **APIs & Services > Credentials** (or search for **Credentials** from the top nav bar). 2. Click **Create Credentials** and select **OAuth client ID**. 3. Select **Web application** as the application type, and name your app whatever you want. 4. Under **Authorized redirect URIs**, enter `https://api.unified.to/oauth/code` (or the corresponding EU/AU regional URL if your workspace is hosted there). 5. Click **Create**. 6. A dialog will appear displaying your new **Client ID** and **Client Secret**. Copy both — you'll need them for the next step. > Store your Client Secret securely. Never commit it to source control or include it in client-side code; keep it in environment variables or a secrets manager. ## Activate Gmail in Unified.to 1. Navigate to the [Gmail integration page on Unified.to](https://app.unified.to/integrations/googlemail). 2. On the **Authorization** tab, with **OAuth 2** selected as the authentication method, enter your **Client ID** and **Client Secret**. 3. Click **Update** to save your changes and activate the Gmail integration. ## Set your OAuth 2 reauthentication policy If you're connecting a **Google Workspace** account, make sure your reauthentication policy is set correctly for the integration to keep functioning. This step requires Google Workspace admin access and doesn't apply to consumer Gmail accounts. 1. In the Google Workspace Admin Console, navigate to **Menu > Security > Access and data control > Google Cloud session control**. 2. On the left, select the organizational unit where you want to set the session length. For all users, select the top-level organizational unit. (An organizational unit initially inherits its parent's settings.) 3. Under **Reauthentication policy**, select **Never require reauthentication**. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_oauth_2_credentials_for_gmail-1.png) You can now use the Unified API to build new experiences with Gmail. Happy building! → [Start your 30-day free trial](https://app.unified.to/login) → [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) ## How to get your OAuth 2 credentials for Microsoft Dynamics 365 URL: https://docs.unified.to/guides/how_to_get_your_oauth_2_credentials_for_microsoft_dynamics_365 # How to get your OAuth 2 credentials for Microsoft Dynamics 365 ------ _October 29, 2024_ _Last updated: May 2026_ This guide explains how to obtain your OAuth 2.0 credentials and configure API permissions for Microsoft Dynamics 365 business applications in Microsoft Entra ID (formerly Azure Active Directory). ## Background Business applications in Microsoft Dynamics 365 use Microsoft's identity platform, Entra ID, for secure authentication. By registering your application, you establish a trusted connection that allows Unified.to to access Dynamics 365 data through the Dynamics 365 Web API (Dataverse) and, where applicable, Microsoft Graph and other Microsoft APIs. This works through: - **Delegated access**: Your application acts on behalf of the signed-in user. The user is redirected to Microsoft's sign-in page, authenticates, and grants your application consent, with permissions approved by both the administrator and the user. - **OAuth 2.0 credentials**: A client ID (which identifies your application) and a client secret (which serves as your application's password). - **Permissions**: Specific capabilities your application requests, which must be approved by an administrator. For more information, see [What is the Microsoft identity platform?](https://learn.microsoft.com/en-us/entra/identity-platform/v2-overview) ## Before you begin Ensure you have: - Access to the Microsoft Entra admin center ([entra.microsoft.com](https://entra.microsoft.com/)) - Administrator access to Microsoft Entra ID ## Register your application You'll first register an application in the Microsoft Entra admin center. This provides you with a client ID and client secret. 1. Sign in to the [Microsoft Entra admin center](https://entra.microsoft.com/). 2. In the left sidebar, go to **App registrations**, then click **New registration**. ![Screenshot_2024-10-31_at_11.43.24_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_oauth_2_credentials_for_microsoft_dynamics_365-0.png) 3. Configure your application: - Enter a meaningful name (for example, your product name). - Under **Supported account types**, select **Accounts in any organizational directory (Any Microsoft Entra ID directory - Multitenant)**. - In the **Redirect URI** section: - Select **Web** as the platform. - Enter `https://api.unified.to/oauth/code` as the redirect URI (or the corresponding EU/AU regional URL if your workspace is hosted there). - Click **Register**. The redirect URI is where Microsoft sends users and authorization codes after they sign in and grant access, so it must exactly match what Unified.to expects. ![Screenshot_2024-10-31_at_11.45.21_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_oauth_2_credentials_for_microsoft_dynamics_365-1.png) ## Get your Dynamics 365 client ID and secret 1. **Retrieve your client ID:** - On your application's overview page, locate the **Application (client) ID**. - Copy this value — this is your client ID. 2. **Create a client secret:** - In the left menu, select **Certificates & secrets**. - Under **Client secrets**, click **New client secret**. - Provide a description (optional, but helpful for tracking). - Set the expiration to its maximum value, unless your security policy requires shorter rotation. - Click **Add**. - Immediately copy the secret **Value** (not the secret ID). You won't be able to view it again after leaving this page, so store it securely right away — never commit it to source control or share it client-side. ## Configure Dynamics 365 API permissions 1. **Set up Microsoft Graph permissions.** These are used to obtain information about your users. - In the left menu, click **API permissions**. - Click **Add a permission**, select **Microsoft Graph**, then choose **Delegated permissions**. - Search for and add the following permissions: - `openid` - `email` - `offline_access` - Click **Add permissions**. 2. **Set up permissions for your Dynamics 365 business application.** - From the **API permissions** screen, click **Add a permission** again. - Under **More Microsoft APIs**, select the API backing your Dynamics instance: - If you're using **Dynamics Sales**, select **Dynamics CRM**. - If you're using **Dynamics Customer Engagement**, select **Customer Insights**. - Choose **Delegated permissions**. - Search for and add the following delegated permission on the Dynamics API: - `user_impersonation` (required for basic access) - Click **Add permissions**. ![Screenshot_2024-10-31_at_11.58.11_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_oauth_2_credentials_for_microsoft_dynamics_365-2.png) Depending on the specific Dynamics app or environment, you may see slightly different API names (for example, Dataverse or a specific Dynamics module), but you should always choose the API backing your Dynamics instance and add `user_impersonation`. Consent to these permissions is granted when an administrator connects the integration through Unified.to's authorization flow. ## Enter your Dynamics 365 credentials on Unified.to 1. Go to [app.unified.to/integrations](https://app.unified.to/integrations) and search for the Dynamics 365 integration you're using. 2. Enter your client ID and client secret from the steps above. 3. Save your changes. You can now use the Microsoft Dynamics 365 integration in your application. Happy building! → [How to troubleshoot unhealthy connections](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_connections) [→ Start your 30-day free trial](https://app.unified.to/login) [→ Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) ## How to get your OAuth 2 credentials in PipeDrive URL: https://docs.unified.to/guides/how_to_get_your_oauth_2_credentials_in_pipedrive # How to get your OAuth 2 credentials in PipeDrive ------ _August 26, 2024_ This guide explains how to create a public developer app in Pipedrive and obtain your OAuth 2 credentials. You'll need these credentials in order to authenticate your app with Unified.to. _Note: The state parameter is not automatically available in Marketplace Manager. To enable it for your app, please write to marketplace.devs@pipedrive.com._ ## Before you begin Before you create a developer app in Pipedrive, ensure: - You have a Pipedrive developer sandbox account. If you don't have one, sign up at [Pipedrive's developer portal](https://developers.pipedrive.com/). ## Access the Developer Hub 1. Log in to your Pipedrive account. 2. Click on your profile name in the upper right corner of the top navigation bar. 3. Select **Developer Hub** from the drop-down menu. ![Screenshot_2024-08-26_at_3.02.21_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_oauth_2_credentials_in_pipedrive-0.png) ## Create a Pipedrive public app 1. Click **Create an app** (or "+ Create an app" if you have existing apps). 2. Select **Create public app**. 3. Fill in your app details under **Basic info**. 1. In the **Callback URL** field, enter: `https://api.unified.to/oauth/code` ![Screenshot_2024-08-26_at_3.06.15_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_oauth_2_credentials_in_pipedrive-1.png) 4. Click **Save.** You will be redirected to the **OAuth & access scopes** page after your app is created. ## Configure scopes and get your OAuth 2 credentials 1. Select the scopes your app requires. Only choose scopes that are necessary for your app's functionality. 1. For example, if you want to be notified about new or updated deals from Pipedrive, select **Deals** and choose whether you need read-only access or full (i.e. create, update, read, delete) access. 2. Scroll down to the **Client ID** section. 3. Here you'll find your `client_id` and `client_secret`. Make a note of them as you will need it for the final step. 4. Click **Save**. ## Activate the Pipedrive integration in Unified.to with your client ID and secret 1. Open a new tab and navigate to [the Pipedrive integration in Unified.to](https://app.unified.to/integrations/hubspot) 2. You will need the following two pieces of information from the earlier steps: 1. **Client ID** 2. **Client secret** 3. Copy and paste these values into their respective fields on the page. Make sure that **OAuth 2** is selected under **Authentication Method**. 4. Click **Activate**. ## How to get your OpenAI API Key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_openai_api_key_step_by_step_guide # How to get your OpenAI API Key: Step-by-step guide ------ _February 8, 2024_ To access OpenAI data or automate workflows through an integration, an API Key is required. This guide walks you through where to find it and how to set it up for development or end-customer use. ## How to get an OpenAI API key (LLM + Ads) ### 1) Create / sign in to your OpenAI account 1. Go to [https://platform.openai.com/](https://platform.openai.com/) 2. Sign in (or create an account). ### 2) Create an API key (for LLM / general API use) 1. In the OpenAI Platform, open **API keys**: - [https://platform.openai.com/api-keys](https://platform.openai.com/api-keys) 2. Click **Create new secret key**. 3. Give it a recognizable name (e.g., `unified-prod`, `unified-dev`). 4. Copy the key immediately and store it in your password manager / secrets vault. - You won't be able to see the full key again after you close the dialog. **Use this key for:** - LLM requests (Responses API / Chat Completions) - Embeddings - Ads API ### 3) Add billing (if required) If you get "insufficient_quota" or similar errors: 1. Open **Billing** in the OpenAI Platform: - [https://platform.openai.com/account/billing](https://platform.openai.com/account/billing) 2. Add a payment method and/or purchase credits (depending on your plan). ### OpenAI Ads --- OpenAI does **not** issue a separate "Ads API key". for the Unified API, you use the **same OpenAI Platform API key** created above. Getting your OpenAI API Key is the first step. The bigger challenge is supporting dozens of vendors - each with unique auth flows, schemas, and maintenance requirements. Unified.to is a real-time integration platform built to replace that work. Instead of building and maintaining 510 custom connectors, you get one secure, normalized API layer across 30 SaaS tools. On top of that, our usage-based pricing scales cleanly with your product. No per-connection fees, no setup charges, and no lock-in, just infrastructure economics that grow with you. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to discover how predictable integration costs should be. ## How to get your Paycom SID/Username and Token/Password: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_paycom_sid_username_and_token_password_step_by_step_guide # How to get your Paycom SID/Username and Token/Password: Step-by-step guide ------ _August 28, 2023_ To access Paycom data or automate workflows through an integration, a SID/Username and Token/Password are required. This guide walks you through where to find it and how to set it up for development or end-customer use. Please request assistance from your Paycom Account Representative for the following items: 1. Enabling the REST API for your Paycom instance 2. Whitelisting your OrgChart server IP address 3. Creating an API Service User 4. Generating valid authentication credentials (SID and Token) Getting your Paycom SID/Username and Token/Password is the first step. The bigger challenge is supporting dozens of vendors - each with unique auth flows, schemas, and maintenance requirements. Unified.to is a real-time integration platform built to replace that work. Instead of building and maintaining 475 custom connectors, you get one secure, normalized API layer across 29 SaaS tools. On top of that, our usage-based pricing scales cleanly with your product. No per-connection fees, no setup charges, and no lock-in, just infrastructure economics that grow with you. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to discover how predictable integration costs should be. ## How to get your PCRecruiter Username, Password, Database ID, App Key and App ID: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_pcrecruiter_username_password_database_id_app_key_and_app_id_step_by_step_guide # How to get your PCRecruiter Username, Password, Database ID, App Key and App ID: Step-by-step guide ------ _August 18, 2023_ Developers connecting to PCRecruiter will need an Username, Password, Database ID, App Key and App ID for authentication. Here's how to generate, configure, and copy your PCRecruiter Username, Password, Database ID, App Key and App ID so you can use it in your integration or share steps with your customers. To find your Database ID: 1. Go to **System** in the top navigation bar. 2. Go to **API** in the menu that opens. 3. Go to **Api Settings** under API. 4. This will open a menu that displays the correct **"Api Database Id"**. Getting your PCRecruiter Username, Password, Database ID, App Key and App ID is the first step. The bigger challenge is supporting dozens of vendors - each with unique auth flows, schemas, and maintenance requirements. Unified.to is a real-time integration platform built to replace that work. Instead of building and maintaining 579 custom connectors, you get one secure, normalized API layer across 31 SaaS tools. On top of that, our usage-based pricing scales cleanly with your product. No per-connection fees, no setup charges, and no lock-in, just infrastructure economics that grow with you. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to discover how predictable integration costs should be. ## How to get your SAP SuccessFactors (OpenID Connect) Username, Password, Client ID, Client Secret, Dependency Name, IAS Host and API URL: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_sap_successfactors_openid_connect_username_password_client_id_client_secret_dependency_name_ias_host_and_api_url_step_by_step_guide # How to get your SAP SuccessFactors (OpenID Connect) Username, Password, Client ID, Client Secret, Dependency Name, IAS Host and API URL: Step-by-step guide ------ _August 13, 2025_ To access SAP SuccessFactors (OpenID Connect) data or automate workflows through an integration, a Username, Password, Client ID, Client Secret, Dependency Name, IAS Host and API URL are required. This guide walks you through where to find it and how to set it up for development or end-customer use. ## **Procedure** 1. Sign into the administration console of your SAP Cloud Identity Services instance. 2. Go to **Applications and Resources** **[Your OIDC Application]** **Application APIs** **Client Authentication** **Secrets**, add a new secret. Once created, copy the client ID and secret for later use. Note that you won't be able to retrieve the secret from the system later. For more information, see [Configure Secrets for API Authentication](https://help.sap.com/docs/cloud-identity-services/aa08922a434a456ba44982c9f4f4d790/dev-configure-secrets-for-api-authentication). You've now retrieved your SAP SuccessFactors (OpenID Connect) Username, Password, Client ID, Client Secret, Dependency Name, IAS Host and API URL. That's one system covered, but scaling integrations one by one means repeating this process hundreds of times. Unified.to handles this for you: 475 integrations out of the box, all with normalized schemas, real-time delivery, and Virtual Webhooks. No polling scripts, no retry logic, no maintenance backlog. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how Unified cuts integration work from quarters to days. ## How to get your SAP SuccessFactors Username, Company ID, Password and API URL: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_sap_successfactors_username_company_id_password_and_api_url_step_by_step_guide # How to get your SAP SuccessFactors Username, Company ID, Password and API URL: Step-by-step guide ------ _April 24, 2023_ When connecting SAP SuccessFactors to other platforms, authentication requires a Username, Company ID, Password and API URL. Follow these step-by-step instructions to generate one from your SAP SuccessFactors account, or provide them to your customers if they need to connect their account. Please follow the following instructions to obtain your SAP SuccessFactors API credentials. ## Step 1: Find your API Server URL - Go to this [link](https://help.sap.com/docs/SAP_SUCCESSFACTORS_PLATFORM/93f95815070049ebaaff042d8322d518/af2b8d5437494b12be88fe374eba75b6.html#api-servers) and find your data-center (DC). Remember your API Server URL. For example, if your domain was https://salesdemo2.successfactors.com, search for `salesdemo2` ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_sap_successfactors_username_company_id_password_and_api_url_step_by_step_guide-0.png) ## Step 2: Find your Username and Company ID - To find your SAP SuccessFactors `username`, log-in to SuccessFactors, then click on your profile image in the upper-right corner to view your username. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_sap_successfactors_username_company_id_password_and_api_url_step_by_step_guide-1.png) - You SHOULD create a new username/password just for API access so that you do not use your own login credentials. - To find your `Company ID`, you can click on `Show version information` in the top-right menu ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_sap_successfactors_username_company_id_password_and_api_url_step_by_step_guide-2.png) - An alternate method to find your `Company ID` under the company settings ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_sap_successfactors_username_company_id_password_and_api_url_step_by_step_guide-3.png) ## Step 3: Set Security Configuration - Set the `Allow Admin to Access OData API through Basic Authentication` permission in `Admin Center > OData API Basic Authentication Configuration` page. - If you require specific IP Addresses to access the SuccessFactors API, then use the following IP addresses: `44.199.69.244` `3.65.142.239` `13.239.151.208`. This is performed in the `Admin Center > OData API Basic Authentication Configuration` or the `API Center > OData IP Allowlisting` pages. You've now created your SAP SuccessFactors Username, Company ID, Password and API URL - just one of many if you support multiple vendors. Each one adds new auth flows, schema differences, and ongoing maintenance. Unified.to removes that overhead with unified objects that work across 514 APIs. Map once, launch everywhere. Your team ships features instead of debugging vendor-specific edge cases. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how much faster you can launch customer integrations with Unified.to. ## How to get your Shopify Admin API access token and Store ID: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_shopify_admin_api_access_token_and_store_id_step_by_step_guide # How to get your Shopify Admin API access token and Store ID: Step-by-step guide ------ _November 14, 2023_ If you're building an integration with Shopify, you'll need an Admin API access token and Store ID from your customer's Shopify account to authenticate requests. This guide shows you (or your customers) how to generate one from Shopify's account settings. ## How do I find my Shopify access token and store ID? Your access token and store ID can be found under **Settings** _>_ **Apps and sales channels** _>_ **Develop Apps** _>_ **Your_App**_._ If you have not used access tokens in the Shopify admin before, this guide will walk you through how to do that as well as how to retrieve your unique store ID. Note: These instructions are adapted from the [Custom apps](https://help.shopify.com/en/manual/apps/app-types/custom-apps#get-the-api-credentials-for-a-custom-app) guide in Shopify. ### Step 1: Activate custom app development 1. In the Shopify admin dashboard, click **Settings** > [**Apps and sales channels**](https://admin.shopify.com/settings/apps). 2. Click **Develop apps**. ![Screenshot_2024-08-08_at_1.52.04_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_shopify_admin_api_access_token_and_store_id_step_by_step_guide-0.png) 3. Read the disclaimer, and then click **Allow custom app development**. ### Step 2: Create a custom app 1. On the same page, click **Create an app**. 2. In the modal that appears, enter the **App name** and select an **App developer**. This can be the store owner or any collaborator with the **Develop apps** permission. ![Screenshot_2024-08-13_at_2.06.21_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_shopify_admin_api_access_token_and_store_id_step_by_step_guide-1.png) 3. Click **Create app**. ### Step 3: Configure Admin API scopes 1. On the same page, click **Configure Admin API scopes** ![b16e9ec4-a348-4f1d-86fa-f4a3cd115591.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_shopify_admin_api_access_token_and_store_id_step_by_step_guide-2.png) 2. Select all of the following checkboxes (tip: use the search bar to quickly look these up) - **Customers:** `read_customers` - **Inventory:** `read_inventory`, `write_inventory` - **Locations:** `read_locations` - **Orders:** `read_orders`, `write_orders` - **Products:** `read_products`, `write_products` ![Screenshot_2024-08-14_at_12.30.29_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_shopify_admin_api_access_token_and_store_id_step_by_step_guide-3.png) 1. Click **Save** ### Step 4: Install your app and retrieve the access token 1. Navigate to **API credentials** and then click **Install app** ![Screenshot_2024-08-13_at_2.13.26_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_shopify_admin_api_access_token_and_store_id_step_by_step_guide-4.png) 2. In the modal that appears, click **Install** 3. The page will now display a form containing your access token. ![Screenshot_2024-08-08_at_4.22.49_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_shopify_admin_api_access_token_and_store_id_step_by_step_guide-5.png) 4. Click on **Reveal token once** 5. Copy and paste the token to the **Sign in and Authorize page** ### Step 6: Retrieve your Shopify store ID 1. While you are in the Shopify admin dashboard, observe the URL in your address bar - it should look something like this: `https://admin.shopify.com/store/9564e1-49` 2. Your unique Shopify store ID is the part that comes after `/store` e.g. `9564e1-49` in the above example 3. Copy and paste the ID to the **Sign in and Authorize** page You've now retrieved your Shopify Admin API access token and Store ID. That's one system covered, but scaling integrations one by one means repeating this process hundreds of times. Unified.to handles this for you: 475 integrations out of the box, all with normalized schemas, real-time delivery, and Virtual Webhooks. No polling scripts, no retry logic, no maintenance backlog. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how Unified cuts integration work from quarters to days. ## How to get your Wayfair Client ID, Client Secret and Vendor Encrypted Key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_wayfair_client_id_client_secret_and_vendor_encrypted_key_step_by_step_guide # How to get your Wayfair Client ID, Client Secret and Vendor Encrypted Key: Step-by-step guide ------ _November 19, 2024_ If you're building an integration with Wayfair, you'll need an Client ID, Client Secret and Vendor Encrypted Key from your customer's Wayfair account to authenticate requests. This guide shows you (or your customers) how to generate one from Wayfair's account settings. Your `Encrypted API Key` can be found in the UI for Supplier management of Vendor apps. ## Steps: 1. Go to the Supplier Integration Status page 2. Select "Vendor Applications" tab 3. Hit "Manage" on the Catalog API row 4. Select your Vendor from the list 5. Grant the needed permissions (see overview of permissions below) 1. Note: Only the permissions that correspond to capabilities that a given Vendor has adopted will be viewable + grantable. 6. Overview of permissions: 1. **read-product-classes**; Gets the product classes that suppliers can list items for sale 2. **read-product-catalog**; Export the supplier product catalog 3. **write-product-generic-description**; Update the generic descriptions for products 4. **read-product-generic-description-update-status**; Checks the status of generic description updates 5. **read-product-generic-descriptions-by-part**; Get the existing generic descriptions given a supplier part number 6. **read-media-metadata-tags**; Get the metadata tags associated with media; 7. **read-media-uploaded**; Get the list of media that was uploaded for a product 8. **read-media-uploaded-status**; Get the uploaded status for a given media request 9. **write-media-upload-from-url;** Uploads new media and associates it with a product 10. **read-product-create-submit-status;** Check the status of a new product request 11. **read-product-create-questions**; Get the questions required by a product class for listing new items for sale 12. write-product-create-submit; Submit a new product to be listed on site 13. read-supplier-brand-associations; Get the supplier brands that are authorized for the supplier to sell 7. On the Vendor application page, can copy their encrypted key by pressing the "Copy Encrypted Key" button. 1. This encrypted key is just an encrypted supplier ID + name. This is more secure than using inputing SuID into the Vendor's system. Generating a Wayfair Client ID, Client Secret and Vendor Encrypted Key is the first step. The harder part is building pipelines that stay reliable as customers add more systems. Unified.to gives you options: deliver data via API, streaming webhooks, database sync, or even as MCP tools for AI agents. One integration layer that adapts to your architecture instead of forcing tradeoffs. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how we simplify integration delivery across 475 integrations and 29 categories. ## How to get your Workable API Token and Subdomain: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_workable_api_token_and_subdomain_step_by_step_guide # How to get your Workable API Token and Subdomain: Step-by-step guide ------ _April 24, 2023_ Workable supports authentication with API Token and Subdomain. To connect, you'll need to generate secure API Token and Subdomain from your account settings. This short guide shows you how to generate one, whether you're testing in your sandbox or helping customers connect their Workable account. ## Generate an API access token This guide shows how to generate an API access token for your Workable account. To start, open the drop-down menu on the top right of your screen and select "Integrations" or press [here](https://www.workable.com/backend/account/integrations): ![1a4f1f5-integrations_small.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_workable_api_token_and_subdomain_step_by_step_guide-0.png) Now click on the "Generate new token" button: ![7d9ac5c-Screenshot_2022-08-11_at_10.39.40_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_workable_api_token_and_subdomain_step_by_step_guide-1.png) Once you click the generate button, you'll see the new access token being generated: ![59ebf0e-Screenshot_2022-08-11_at_10.40.04_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_workable_api_token_and_subdomain_step_by_step_guide-2.png) This token type does not support `r_employees` scope. There are also new scopes like `w_employees` and `r_account` that are not currently supported by access tokens already used for ATS related endpoints. You need to contact support to enable them for your account tokens. ### Subdomain You will also need the account subdomain which you can find in the company profile settings page: ![6b1bcd3-Screenshot_2022-08-11_at_12.34.55_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_get_your_workable_api_token_and_subdomain_step_by_step_guide-3.png) ### Revoking The Access Token Be aware that you can revoke this access token anytime, but if you decide to proceed, every script or application that accesses the Workable API through this token will stop functioning. To keep things running smoothly after revoking your current token, you will have to generate a new access token and update every script or application bound to the previous one. You've now retrieved your Workable API Token and Subdomain. That's one system covered, but scaling integrations one by one means repeating this process hundreds of times. Unified.to handles this for you: 547 integrations out of the box, all with normalized schemas, real-time delivery, and Virtual Webhooks. No polling scripts, no retry logic, no maintenance backlog. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how Unified cuts integration work from quarters to days. ## How to get your yotpo APP Key / Store ID and Secret Key: Step-by-step guide URL: https://docs.unified.to/guides/how_to_get_your_yotpo_app_key_store_id_and_secret_key_step_by_step_guide # How to get your yotpo APP Key / Store ID and Secret Key: Step-by-step guide ------ _April 4, 2025_ To access yotpo data or automate workflows through an integration, a APP Key / Store ID and Secret Key are required. This guide walks you through where to find it and how to set it up for development or end-customer use. # **Retrieving your app key** **Your app key is sometimes referred to as your Store ID.** **To retrieve your app key:** 1. **In Yotpo Reviews, click the Profile icon at the top right corner of the screen.** 2. **Select Account Settings >** [**General Settings**](https://settings.yotpo.com/#/general_settings)**. You'll find your App Key at the bottom of the General Settings section.** **If you need a Secret Key, you'll need to generate it.** # **Generating your secret key** **Your secret key is sometimes referred to as your API secret.** **To generate your secret key:** 1. **Follow the steps above to get to your** [**Account Settings**](https://settings.yotpo.com/#/general_settings)**.** 2. **From your General Settings, click Get secret key. We'll send an email with a verification code to the email address associated with your account.** **3. Paste the code from the email and click Submit. Your secret key will be revealed on the next screen. Remember, only share your secret key with parties that should have access to your data.** Generating a yotpo APP Key / Store ID and Secret Key is the first step. The harder part is building pipelines that stay reliable as customers add more systems. Unified.to gives you options: deliver data via API, streaming webhooks, database sync, or even as MCP tools for AI agents. One integration layer that adapts to your architecture instead of forcing tradeoffs. [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) to see how we simplify integration delivery across 506 integrations and 30 categories. ## How to Handle Attachments in Invoice, Bill and Credit Memo URL: https://docs.unified.to/guides/how_to_handle_attachments_in_invoice_bill_and_credit_memo # How to Handle Attachments in Invoice, Bill and Credit Memo ------ _August 5, 2025_ Unified.to provides a standardized way to read, upload, and manage attachments (e.g., invoices, receipts, contracts) across various integrations like accounting, CRM, and etc platforms. Here's everything you need to know to get started with attachments via the `Storage File` endpoint. --- ### 🔍 How to Check If an Integration Supports Attachments To check if a specific integration supports reading or writing attachments: 1. **Go to the Integration Page** in the Unified.to dashboard. 2. Navigate to **Feature Support**. 3. Select the relevant **endpoint** (e.g., `Accounting → Invoice`). 4. Look under **Readable Fields** — if `attachments` is listed, the endpoint supports reading attachments. 🛠️ **Tip:** You can also check support programmatically using the `/storage` endpoint. --- ### 📂 How to List All Attachments To fetch all available attachments across an integration: - Use the `GET /storage` (Storage File List) endpoint. ⚠️ **Note:** Most integrations **do not support** listing all attachments globally. Unified.to reflects this limitation. However, some integrations like **Sage** _do_ support it. --- ### 📌 How to Fetch Attachments for a Specific Record (e.g., Invoice) To get attachments for a specific entity like an invoice, bill, or candidate: - Use the `GET /storage` endpoint. - Pass the corresponding `parent_id` (e.g., `invoice_id`) in the query parameters. Example: ```plain text http CopyEdit GET /storage-file?parent_id=inv_1234 ``` --- ### 🧾 How to Get Details of a Specific Attachment To retrieve metadata (name, file type, size, etc.) of a particular attachment: - Use either: - `GET /storage/:id` — to get a single attachment - `GET /storage` — to filter and retrieve multiple files with query params --- ### 📥 How to Download an Attachment We return a secure `download_url` in the response when you fetch an attachment. You can use this URL to download the file directly. ⚠️ **Note:** The `download_url` is **time-limited** and expires after a few minutes for security reasons. Always use the URL shortly after fetching. --- ### 📤 How to Upload or Create an Attachment To upload a new attachment: 1. Go to the **Integration Page** and confirm the `storage-file` endpoint is supported under Feature Support. 2. Use the `POST /storage-file` endpoint with required fields. Key fields: - `name` - `data` (base64 encoded) | `download_url` - `parent_id` (e.g., `invoice_id`, `bill_id`, `employee_id`) Unified.to will automatically associate the attachment with the parent entity. Example payload: ```json json CopyEdit { "file_name": "invoice-august.pdf", "file_data": "base64-encoded-data", "parent_id": "inv_1234" } ``` --- ### 🧷 How to Associate an Attachment with an Entity When uploading a file, simply set the `parent_id` to the ID of the resource (invoice, bill, employee, etc.). Unified.to handles the rest — no extra linking needed. --- ### 💬 Questions or Requests? - If you're unsure whether an integration supports attachments for a specific resource type, reach out to our support team. - If a feature isn't currently supported, let us know! We're always looking to expand support where possible. ## How to migrate or import your integrations into Unified.to URL: https://docs.unified.to/guides/how_to_migrate_or_import_your_integrations_into_unified # How to migrate or import your integrations into Unified.to ------ _October 25, 2024_ ![app.unified.to_connections_import_typedropbox.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_migrate_or_import_your_integrations_into_unified-0.png) This guide explains how to import your existing integrations into Unified.to, effectively creating new connections using your existing customer credentials. A connection is a secure link between your application and your customer's third-party account. While connections are typically created through an auth flow where users grant access through the provider's authorization page, you can also create connections by importing existing credentials that your customers have already provided to you. ## Before you begin This guide assumes you have: - A Unified.to account. - Existing customer credentials for the integrations you want to import. - Basic understanding of REST APIs, authentication flows, and [connections](https://docs.unified.to/guides/end_users_integrations_and_connections#end-users-integrations-and-connections). ## Understand the authentication types Before importing your integrations, you need to determine which authentication type each integration uses. Unified.to supports two main authentication flows: ### API token authentication - Simple token: Requires a single token or key. - Multi-field token: Requires multiple credentials (e.g. API key + domain). ### OAuth 2 authentication - Requires client credentials (client ID and secret). - Requires access tokens and optional refresh tokens. - May include additional user information e.g. emails, names. ## Check instructions for the integration you want to import The Unified.to Core API contains information that will help you determine what you need to successfully import your integrations. 1. Make a GET request to the `/unified/integration` endpoint to get information about the integration you want to import, passing in the categories and/or names of the integrations you are interested in. 2. Check the following fields in the response: - `token_names`: Lists required credential fields for API token authentication. - `token_instructions`: Provides guidance on where to find these credentials. For example: ```javascript const baseUrl = 'https://api.unified.to/unified/integration'; const params = new URLSearchParams({ categories: ['ats', 'crm'].join(',') }); const url = `${baseUrl}?${params}`; // Make the request fetch(url, { method: 'GET', headers: { 'Authorization': `Bearer ${YOUR_API_KEY}`, 'Accept': 'application/json' } }) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(integrations => { // Loop through each integration and log its auth details integrations.forEach(integration => { console.log(`\n=== ${integration.name} ===`); // Log token information if it exists if (integration.token_names && integration.token_names.length > 0) { console.log('Required credentials:', integration.token_names); console.log('How to find credentials:'); integration.token_instructions?.forEach((instruction, index) => { console.log(`${index + 1}. ${instruction}`); }); } else { console.log('No token credentials required (likely uses OAuth)'); } }); }) .catch(error => { console.error('Error fetching integrations:', error); }); /* Example output: === ActiveCampaign === Required credentials: ["API Key", "Domain"] How to find credentials: 1. Your API key can be found in your account on the Settings page under the "Developer" tab... 2. Your API URL can be found in your account on the My Settings page under the "Developer" tab... === HubSpot === No token credentials required (likely uses OAuth) */ ``` **API reference:** [Get all integrations](https://docs.unified.to/unified/integration/Returns_all_integrations). ## Import an API token integration For single-token integrations, construct your connection object: ```json { "integration_type": NAME_OF_INTEGRATION, "permissions": [PERMISSIONS], "categories": [CATEGORIES], "environment": "Production", // or any other non-sandbox environment "auth": { "token": "your_customer_token" } } ``` For multi-field token integrations, use the `other_auth_info` array: ```json { "integration_type": NAME_OF_INTEGRATION, "permissions": [PERMISSIONS], "categories": [CATEGORIES], "environment": "Production", // or any other non-sandbox environment "auth": { "token": "your_customer_token" "other_auth_info": [ "first_credential", "second_credential" ] } } ``` **Important**: The order of credentials in `other_auth_info` must match the order of the `token_names` from the integration instructions (see above). ## Import an OAuth 2.0 integration 1. Construct your connection object with the OAuth credentials: ```json { "integration_type": NAME_OF_INTEGRATION, "permissions": [PERMISSIONS], "categories": [CATEGORIES], "environment": "Production", // or any other non-sandbox environment "auth": { "access_token": "customer_access_token", "refresh_token": "customer_refresh_token", "expiry_date": "2024-12-31T23:59:59Z", "emails": ["user@example.com"], "name": "User Name" } } ``` 1. Log into the developer account for the provider whose integrations you are importing. Update your redirect URIs to point to Unified.to: `https://api.unified.to/oauth/code` **Notes**: - Set `expiry_date` to the token's expiration date (if known) or today's date. - Include the authenticating user's email and name if available. ## Create the connection Make a POST request to create the connection. Example using `curl`: ```shell curl -X POST "" \\ -H "Authorization: Bearer YOUR_API_KEY" \\ -H "Content-Type: application/json" \\ -d '{ "integration_type": "your_integration_type", "permissions": ["required_permissions"], "categories": ["integration_category"], "auth": { // Your auth object here } }' ``` **API reference**: [Create a connection](https://docs.unified.to/unified/connection/Create_connection) ## Test the imported connection After creating the connection: 1. Note the connection ID from the response. 2. Make a test API call using the new connection ID to verify it works. 3. If the call fails, check the error message and verify your auth credentials. ## Best practices when importing a connection - Always include relevant `permissions` for the minimum set of data access you require. - Use a non-sandbox `environment`. - Store the connection ID securely for future API calls. - Consider adding `external_xref` to link the connection to your customer's ID. ## See also - [How to associate a connection ID with your end-user](https://docs.unified.to/guides/how_to_associate_a_connection_id_with_your_end_user#how-to-associate-a-connection-id-with-your-end-user) - [How to troubleshoot unhealthy connections](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_connections#how-to-troubleshoot-unhealthy-connections) - [How to create and configure webhooks](https://docs.unified.to/guides/how_to_create_and_configure_webhooks) ## How to obtain your GitHub OAuth2 credentials URL: https://docs.unified.to/guides/how_to_obtain_your_github_oauth2_credentials # How to obtain your GitHub OAuth2 credentials ------ _June 3, 2024_ In order to set up OAuth2 for Github, you need to create a new OAuth application in your [GitHub developer settings](https://github.com/settings/applications/new): ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_obtain_your_github_oauth2_credentials-0.png) Make sure to fill in the appropriate details, but more importantly the Authorization Callback URL, set to [`https://api.unified.to/oauth/code`](https://localhost:8080/_renarde/security/github-success). Now click on `Register application` and you'll be shown your application page: ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_obtain_your_github_oauth2_credentials-1.png) You need to click on `Generate a new client secret`, confirm your credentials, and write down your Client ID and Client secret (especially that one, because you will not be able to see it again later from that page, but you can always recreate one, do don't worry too much): ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_obtain_your_github_oauth2_credentials-2.png) Take your GitHub clientID and clientSecret and add them to the [Unified.to](https://unified.to/) integration settings for Github at [https://app.unified.to/integrations/github](https://app.unified.to/integrations/github) ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_obtain_your_github_oauth2_credentials-3.png) You will now be ready to use GitHub on [Unified.to](https://unified.to/) realtime unified API. ## How to Power Claude with Live Customer Data Using Unified MCP URL: https://docs.unified.to/guides/how_to_power_claude_with_live_customer_data_using_unified_mcp # How to Power Claude with Live Customer Data Using Unified MCP ------ _May 28, 2025_ ![MCPSERVER.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_power_claude_with_live_customer_data_using_unified_mcp-0.png) If you're building your software application with Anthropic's LLM API [a](https://claude.ai/)nd want more than a thin agent, your application needs access to real end-customer data — like pulling HubSpot deals, sending a Slack message, or updating a candidate in Greenhouse. You can wire this up yourself using Unified's API, which will save you a ton of time as it is a single API for multiple SaaS APIs. Or you can point your LLM API at Unified's MCP server and launch even faster. We'll break down when to use Unified's MCP server, and when to reach for the API instead. ## How Unified's MCP Server Works Unified's MCP Server exposes real, user-authorized SaaS integrations to any LLM that supports Model Context Protocol. Anthropic's API, Claude or Cursor can: - Discover tools based on a given Unified connection - Call those tools directly through our MCP endpoint - Use the result in its next generation step ## Two Paths: API vs MCP Unified supports two integration interfaces: **1. Unified API (Backend Integration)** Best for production-grade apps. You fetch and transform data yourself, persist what matters, and optionally embed it in a vector database. Full control, real scalability. **2. Unified MCP Server (Direct-to-LLM)** Best for prototyping or agent UXs. You point Anthropic's API, Claude or Cursor at a live MCP endpoint, and it auto-discovers available tools — all backed by your end-user's authorized connections. ## Why Claude + MCP Is the Best Fit The strongest use case for Unified's MCP Server is direct integration with the Anthropic API. It's already a common pattern among Unified users: AI-native SaaS teams wiring real-time tools into their product experience. With MCP, you now have two ways to access your customer's data: 1. Use the **Unified API** and call it from your backend 2. Point the **Anthropic API** at Unified's MCP Server and let it fetch tools on its own This lets your team skip everything between OAuth and LLM — no middleware, no polling, no schema juggling. You stay focused on your product's core logic while Claude handles tool orchestration via Unified. That's always been the promise of a Unified API: do less glue work and ship faster. MCP just makes that even more direct. ## What to Do if You're Building for Scale If your product is heading toward scale, especially if it requires logging, observability, or context-aware data access, the better path is: 1. Use the Unified API to fetch and normalize data 2. Store and filter what matters 3. Embed it into a vector database 4. Let your LLM consume it through whatever interface makes sense MCP is a fast path to working demos. The API is your infrastructure. ## Example Flow Claude gets this prompt: > 'Post a message to #sales about the new signed deal.' It: 1. Identifies a tool call (e.g. `send-slack-message`) 2. Sends the tool and parameters to Unified's MCP Server 3. Unified makes the call to Slack using the user's connection 4. Claude receives the result and continues the conversation No servers, no APIs, no code in between. Unified's MCP Server is in beta. It supports any authenticated connection, works with Claude or Cursor, and helps you wire up AI features fast without touching infrastructure. [Request early access](mailto:support@unified.to) or [read the MCP docs](https://docs.unified.to/mcp). ## How to register a Google developer app and get OAuth 2 credentials URL: https://docs.unified.to/guides/how_to_register_a_google_developer_app_and_get_oauth_2_credentials # How to register a Google developer app and get OAuth 2 credentials ------ _May 29, 2025_ > 💡 **Note** that if you use `Restricted` or `Sensitive` Scopes you need to contact Unified's Support to setup a CNAME for the API URL in order to get your App approved by Google > Check [https://docs.unified.to/guides/how_to_set_up_a_custom_api_url](https://docs.unified.to/guides/how_to_set_up_a_custom_api_url). 1. **Create a Google Cloud Platform project:** - Go to the Google Cloud Console (https://console.cloud.google.com/) and sign in. - If you don't have a project, click the "Select a project" dropdown and choose "New Project". - Give your project a name and click "Create". 2. **Enable the Necessary APIs** - In the Google Cloud Console, make sure your newly created or existing project is selected. - Navigate to [**APIs & Services**](https://console.cloud.google.com/apis/library)[ > ](https://console.cloud.google.com/apis/library)[**Library**](https://console.cloud.google.com/apis/library) - Search for the specific API you want to use (e.g., "Gmail API", "Google Drive API", 'Google Calendar API'). - For Google Directory, make sure that you have the[ Admin SDK API ](https://console.cloud.google.com/apis/library/admin.googleapis.com)enabled. - Click on the API from the search results. - Click the "**Enable**" button. Repeat this for all Google APIs your application will need to access 3. **Configure the OAuth Consent Screen** - In the Google Cloud Console, go to [Google Auth Platform](https://console.cloud.google.com/auth/overview) - Fill in what is asked and click **Create** - Click 'Create' - Go to [Auth Branding](https://console.cloud.google.com/auth/branding) and fill in the required Information 4. **Scopes**: Select the scopes your application needs. Scopes define the permissions your app is requesting (e.g., read emails, access calendar). Be specific and only request the scopes you absolutely need. - Go to [Auth Data Access](https://console.cloud.google.com/auth/scopes) - You will at least need `openid`, `userinfo.email`, `userinfo.profile`, `admin.directory.group.readonly` and `admin.directory.user` or `admin.directory.user.readonly`. - The scopes needed by other Google integrations can be found in the Unified App, for example: - [Google Contacts](https://app.unified.to/integrations/googlecontacts?tab=oauth2) - [Google Directory](https://app.unified.to/integrations/googledirectory?tab=oauth2) - [Gmail](https://app.unified.to/integrations/googlemail?tab=oauth2) - **Note** that if you use `Restricted` or `Sensitive` Scopes you need to contact Unified's Support to setup a CNAME for the redirect URL in order to get your App approved by Google - Review the information and click "**Save and Continue**" through the steps. If you selected "External" and are just testing, you can often leave your app in "Testing" publishing status. If you intend for it to be publicly available, you'll eventually need to "Publish" it and potentially go through verification. 5. Create OAuth 2.0 Credentials - Go to [Auth Clients](https://console.cloud.google.com/auth/clients) in the Google Cloud Console. - Click on "**+ Create Client**" at the top of the page, Follow the steps - Configure the authorized redirect URIs. Get the correct value from the Integration, for example [Google Drive](https://app.unified.to/integrations/googledrive?tab=oauth2) - Copy your Client ID and Secret - Enter these values in your Integrations, for example: - [Google](https://app.unified.to/integrations/google) - [Gmail](https://app.unified.to/integrations/googlemail?tab=auth) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_google_developer_app_and_get_oauth_2_credentials-0.png) 1. Publish your Application - Go to [Auth Audience](https://console.cloud.google.com/auth/audience) - Click on Publish app ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_google_developer_app_and_get_oauth_2_credentials-1.png) 1. Once your application is approved and verified it will start showing your application name on the authorization consent screen. Until then it will be showing the 'unified.to' name, which is inferred from the redirect URL. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_google_developer_app_and_get_oauth_2_credentials-2.png) ## How to register a Salesforce developer app and get OAuth 2 credentials URL: https://docs.unified.to/guides/how_to_register_a_salesforce_developer_app_and_get_oauth_2_credentials # How to register a Salesforce developer app and get OAuth 2 credentials ------ _February 12, 2023_ In this guide, we'll walk you through how to register a Salesforce developer app to get the OAuth 2 credentials you'll need to be a Salesforce integration for your product. Whether you're planning to build a custom-code integration or using a Unified API solution like [Unified.to](https://unified.to/), retrieving your OAuth 2 credentials is the first step in the process of integrating with Salesforce. We'll also added directional for existing Unified.to users that are leveraging our Unified CRM API for a faster integration development process. ## What is a Salesforce Developer App? A Salesforce developer app is a container for one or more developer apps. A developer app is an application that can access Salesforce data or functionality through the Salesforce REST, SOAP, or Streaming APIs. A connected app defines the following information: - The name and logo of the app - The OAuth settings for the app, such as the callback URL, the scopes, and the refresh token policy - The API version and the endpoints for the app - The user profiles or permission sets that can access the app By registering a developer app, you can generate a client ID and a client secret, which are the credentials you'll need to authenticate your requests to the Salesforce APIs ## Step 1: Register a Salesforce developer app To register a Salesforce developer app, you'll need a Salesforce developer account. If you don't have one, sign up for free at [https://developer.salesforce.com/signup](https://developer.salesforce.com/signup). Once you have a developer account, follow these steps to register a developer app: 1. Log in to your Salesforce developer account and go to **Setup** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_salesforce_developer_app_and_get_oauth_2_credentials-0.png) 2. In the Quick Find box, search for **App Manager** and click on it 3. Click on the **New External Client App** button 4. Fill in the basic information for your app, such as the name, contact email, and logo URL 5. Select `Packaged` in **Distribution State** 6. In the API (Enable OAuth Settings) section, check the **Enable OAuth Settings** checkbox 7. Enter the callback URL for your app (this is the URL that Salesforce will redirect the user to after they authorize your app). For [Unified.to](https://unified.to/) users, the callback URL needs to be `https://api.unified.to/oauth/code`, `https://api-eu.unified.to/oauth/code` or `https://api-au.unified.to/oauth/code`. 8. Select the OAuth scopes for your app (these are the permissions that your app will request from the user). To set up [Unified.to](https://unified.to/) successfully you'll need to add these OAuth Scopes: `Manage user data via APIs (api)` and `Platform requests at any time (refresh_token, offline_access)`. If you are also going to use Salesforce to sign-in your users into your application, then also set the `openid`,`profile`,`email`scopes. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_salesforce_developer_app_and_get_oauth_2_credentials-1.png) 9. Next, enable the following options for you app: 1. Enable Authorization Code and Credentials Flow 2. Require user credentials in the POST body for Authorization Code and Credentials Flow 3. Require Secret for Web Server Flow 4. Require Secret for Refresh Token Flow 10. Do NOT enable CODE CHALLENGE (Require Proof Key for Code Exchange (PKCE) Extension for Supported Authorization Flows) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_salesforce_developer_app_and_get_oauth_2_credentials-2.png) 11. Click **Create** 12. Next click **Edit** under **Policies** tab ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_salesforce_developer_app_and_get_oauth_2_credentials-3.png) 13. Set 'Refresh token is valid until revoked' and click **Save** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_salesforce_developer_app_and_get_oauth_2_credentials-4.png) 14. Lastly, go to Settings tab and retrieve you 'Consumer Key' (client ID) and 'Consumer Secret' (client secret).You will need these credentials to activate your Salesforce integration in [Unified.to](https://unified.to/). ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_salesforce_developer_app_and_get_oauth_2_credentials-5.png) ## Step 2: Use Unified.to to Simplify Integration Development Using Salesforce APIs directly can be a complex and time-consuming process, especially if you plan to integrate with other leading CRM platforms like HubSpot, ActiveCampaign, and Copper. Without Unified.to, you'll be tasked with handling authentication, error handling, rate limiting, data mapping, and the synchronization logic for each individual integration. [Unified.to](https://unified.to/) simplifies this entire process, providing SaaS developers with a unified set of APIs, data models, and webhooks for 20+ CRM integrations. Add all the CRM integrations on your roadmap in a matter of hours. ## Step 3: Activate Salesforce in Unified.to (for Unified.to users) 1. [Log in](https://app.unified.to/login) and navigate to [**Active Integrations**](https://app.unified.to/integrations) 2. Use the search bar or click the **CRM** tab to select **Salesforce** 3. Click **Activate** on Salesforce 4. Enter the information from the previous step (client ID, client secret) Once you've activated Salesforce along with other integrations, you'll be able to add integrations to your product using our low-code authorization UI embed. ## One API to integrate them all You're reading this article on the [**Unified.to**](https://unified.to/) blog. We're a Unified API developer platform for SaaS customer-facing integrations. We're excited to continue to innovate at Unified.to and solve hard, critical integration-related problems for our customers. If you're curious about our integrations-as-a-service solution, consider [**signing up for a free account**](https://app.unified.to/login?utm_source=blog&utm_medium=blog_signup_salesforce&utm_campaign=blog_signup&utm_id=blog_signup) or [**meet with an integrations expert**](https://calendly.com/michelle-unified/discovery-via-blog)**.** ## How to register a Slack developer account and get OAuth 2 credentials URL: https://docs.unified.to/guides/how_to_register_a_slack_developer_account_and_get_oauth_2_credentials # How to register a Slack developer account and get OAuth 2 credentials ------ _January 3, 2025_ This guide walks you through how to register a Slack developer account, obtain OAuth 2 credentials, and configure scopes for your application. ## Before you begin Make sure you have: - A Slack account - Admin access to a Slack workspace where you can test your app ## Create a Slack app 1. Go to the [Slack API website](https://api.slack.com/apps) and sign into your Slack account 2. Click **Create New App** 3. Select **From scratch** 4. Enter your app's name 5. Select the workspace where you want to install and test out your app 6. Click **Create App** ![Screenshot_2025-01-03_at_11.13.30_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_slack_developer_account_and_get_oauth_2_credentials-0.png) After you create your app, you'll be redirected to the app's settings page where you can find your Client Id and Client Secret. Make a note of these as you will need them to activate the Slack integration on Unfiied.to. ![Screenshot_2025-01-03_at_11.15.20_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_slack_developer_account_and_get_oauth_2_credentials-1.png) _Note: Store these values securely - never commit them to version control!_ ## Configure OAuth 2 settings In addition to activating the Slack integration, you'll need to configure the redirect URL and enable the correct scopes in order to successfully authorize a connection with Unified.to. 1. From your app's settings page, navigate to **OAuth & Permissions** 2. Under **Redirect URLs,** enter: [`https://api.unified.to/oauth/code`](https://api.unified.to/oauth/code) 3. Click **Save URLs** ![Screenshot_2025-01-03_at_11.18.11_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_slack_developer_account_and_get_oauth_2_credentials-2.png) 4. If you are using the SlackBot integration, use **Bot Token Scopes.** If not, then use **User Token Scopes.** click **Add an OAuth Scope** 5. Search for and add the scopes you need for your application - refer to [this page ](https://app.unified.to/integrations/slack?tab=oauth2)to see the scopes that are supported by Unified. 1. For example, if you want to read messages, then you should choose the scopes that map to : 1. **messaging_channel_read**: `im:read, mpim:read, channels:read, groups:read` 2. **messaging_message_read**: `im:read, im:history, mpim:read, mpim:history, channels:history, channels:read, groups:read, groups:history` ## Activate the Slack integration with your credentials 1. Go to [https://app.unified.to/integrations/slack?tab=auth](https://app.unified.to/integrations/slack?tab=auth) or  [https://app.unified.to/integrations/slackbot?tab=auth](https://app.unified.to/integrations/slack?tab=auth) 2. Enter your Client ID and Client secret from the steps above 3. Save your changes # For a Slack Bot, follow these additional instructions 1. In the left sidebar, go to **"App Home"** 2. Under **"Your App's Presence in Slack"**, click **"Add"** or **"Edit"** 3. Set a **Display Name** and **Default Username** for your bot 4. Toggle on **"Always Show My Bot as Online"** (optional) 5. Go to **"Event Subscriptions"** in sidebar 6. Toggle **"Enable Events"** to On 7. Enter the Unified webhook URL `https://api.unified.to/webhook/workspace/slackbot?workspace_id={ID}` 8. Subscribe to most bot events like: - message.channels - app_mention - message.im 9. Go to **"Interactivity & Shortcuts" to enable receiving information about button events** 10. Toggle On and enter in `https://api.unified.to/webhook/workspace/slackbot?workspace_id={ID}` When a user authorizes your `slackbot` integration, their user ID will be stored in `connection.auth.user_id` and the bot ID will be stored in `connection.auth.app_id` . Congratulations, you're now ready to use the Slack integration in your application. Happy building! ## How to Register a Workday Developer App and Get OAUTH2 Credentials URL: https://docs.unified.to/guides/how_to_register_a_workday_developer_app_and_get_oauth2_credentials # How to Register a Workday Developer App and Get OAUTH2 Credentials ------ _March 13, 2023_ As a developer of an application in the HR space, you absolutely need to have a Workday integration so that you can access your customers' data in the most popular HRIS vendor, Workday. Before you can use Unified.to's HRIS and ATS unified APIs with Workday, you must get your customer to generate Workday API credentials for your application. ## Register a new API Client in Workday 1. Sign-in to the Workday tenant 2. Search for 'Register API Client for Integration' using the search bar 3. Fill out the requested inputs with the following example details: 1. Client Name; 2. Refresh Token Timeout; 0 days (ie. never) 3. Non-Expiring Refresh Token: Checked 4. Scope: Choose all of the permission scopes for the use-case. Minimum should be 'Staffing' (for employee/worker access), 'Leave of Absence' (for timeoff). Always include 'Tenant Non-Configurable' and 'Integration' as well. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_workday_developer_app_and_get_oauth2_credentials-0.png) 4. Once the API client has been registered, go to 'Related Actions' icon ⇒ 'API Client' ⇒ 'Manage Refresh Tokens for Integrations' ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_workday_developer_app_and_get_oauth2_credentials-1.png) 5. Remember the Client ID and Client Secret 6. Enter the 'Integration System User (ISU)' in the Workday Account field. This ISU needs to have access to all of the resources that you need for the API. 7. Check the "Regenerate New Refresh Token" box and click OK. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_workday_developer_app_and_get_oauth2_credentials-2.png) 8. A new refresh token will be generated. Remember that Refresh Token. ## Use [Unified.to](https://unified.to/) to Simplify your Integrations Development Using Workday APIs directly can be complex and time-consuming, especially if you want to integrate with multiple services or platforms. You will need to handle the authentication, the error handling, the rate limiting, the data mapping, and the synchronization logic for each integration. A simpler and faster alternative is to use [Unified.to](https://unified.to/) APIs for HRIS and ATS integrations. It provides a unified set of APIs, data-models, and webhooks for most of the HRIS and ATS integrations. ## Activate Workday in Unified.to 1. Navigate to [Active Integrations](https://app.unified.to/integrations) 2. Search for Workday 3. Click on Activate on Workday 4. Enter in the information from the previous step (client ID, secret, endpoints/URLs) ## How to Register a Workday Developer App and Get OAUTH2/ SOAP Credentials URL: https://docs.unified.to/guides/how_to_register_a_workday_developer_app_and_get_oauth2_soap_credentials # How to Register a Workday Developer App and Get OAUTH2/ SOAP Credentials ------ _November 10, 2025_ # How to Register and Get OAUTH2 **Credentials for Workday:** You will need 3 endpoints (URLs): - REST API Endpoint - Token Endpoint - Authorization Endpoint You will also need: - Client ID - Client Secret ### How to setup Client ID 1. **Log in to the Workday application** 2. Register API Client: Navigate to the "Register API Client" section 3. Select the Register API Client form 4. Select the Register API Client form 5. For the grant type, select "Authorization Code Grant' 6. For the access token type, select "Bearer' 7. In the Redirection URI field, enter "[https://api.unified.to/oauth/code](https://api.unified.to/oauth/code)" 8. In the Scope section, add Staffing and/or Recruiting, depending on your application's needs 9. Select "Non-expiring Refresh Tokens' 10. Select the Include Workday Owned Scope checkbox. Click OK to save the app. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_a_workday_developer_app_and_get_oauth2_soap_credentials-0.png) Once you create your application, Workday will display the information that you entered plus additional information. ### **Retrieve Endpoints** 1. Access "View API Clients" 2. Use the Workday search bar to navigate to "View API clients" 3. Retrieve REST API Endpoint, Token Endpoint, Authorization Endpoint # How to Register and Get OAUTH2 **Credentials for Workday Legacy**:- ## **Integration System User(ISU)** ### Create User 1. Log in to the Workday tenant 2. search 'Create Integration System User'. 3. Goto 'Create Integration System User' 4. In the "Create Integration System User" page, go to the "Account Information" section. 5. Provide Username & Password 6. Require New Password should remain unchecked 7. Type 0 (zero) for Session Timeout Minutes to prevent session expiration 8. Click "OK" to save. ### Provide permissions to ISU 1. Search field, type "Create Security Group' 2. Select "Integration System Security Group" from the "Type of Tenanted Security Group" drop-down menu. 3. In the "Name" field, enter a name for the security group. 4. Integration System Security Group on Workday select 'Maintain Permissions for Security Group'' 5. Configure the Permissions and select "Maintain' Operation 6. "Source Security Group" name matches your created security group. 7. Now add Domain Security Policy Permissions 8. Search 'Activate Pending Security Policy Changes' ### WSDL URL 1. Go to Workday, search 'Public Web Services'. 2. Click **"Public Web Services Report**' 3. Hover over the "Human Resources" section. 4. Click the three dots to open the menu. 5. Select "Web Services" and then click "View WSDL' 6. Goto bottom of the page and get host URL. That looks like following:- [**https://wd5-services1.myworkday.com/ccx**](https://wd5-services1.myworkday.com/ccx)**.** ## How to Register a ZendeskSell Developer App and Get OAUTH2 Credentials URL: https://docs.unified.to/guides/how_to_register_a_zendesksell_developer_app_and_get_oauth2_credentials # How to Register a ZendeskSell Developer App and Get OAUTH2 Credentials ------ _February 12, 2023_ If you are a developer who wants to build data integrations for your SaaS software, you might be interested in using the ZendeskSell API. The ZendeskSell API allows you to access and manipulate data from ZendeskSell, a powerful CRM platform that helps you manage your sales pipeline, contacts, deals, and tasks. However, before you can use the ZendeskSell API, you need to register a developer app and get credentials. In this article, I will show you how to do that in a few simple steps. ## Step 1: Create a ZendeskSell account If you don't have a ZendeskSell account yet, you need to create one first. You can sign up for a free trial here: [https://www.zendesk.com/register/?source=zendesk_sell](https://www.zendesk.com/register/) Once you have created your account, you can log in to your ZendeskSell dashboard and access the settings menu. ## Step 2: Register a developer app To register a developer app, you need to go to the Admin Settings and click on "APIs > Zendesk APIs" and then on 'Oauth Clients'. Then, you need to click on the "Add OAUTH2 Client" button and fill in the required information. You need to provide a name, a description, a logo URL, and a redirect URI for your app. The redirect URI is the URL where ZendeskSell will send the authorization code after the user grants permission to your app. You can use any URL that you control, but it must match the one you will use in your code later. If you are going to use Unified.to, then this needs to be `https://api.unified.to/oauth/code` You also need to select the scopes that your app will need to access the ZendeskSell data. The scopes are the permissions that define what your app can do with the ZendeskSell API. You can choose from the following scopes: - read: Allows your app to read data from ZendeskSell - write: Allows your app to write data to ZendeskSell - delete: Allows your app to delete data from ZendeskSell You can select one or more scopes depending on your app's functionality. However, you should only request the minimum scopes that your app needs to avoid unnecessary access. After you fill in the information, you can click on the "Register App" button and your app will be created. ## Step 3: Get credentials Once you have registered your app, you will see a confirmation page with your app's credentials. You will need these credentials to authenticate your app with the ZendeskSell API. The credentials are: - Client ID: A unique identifier for your app - Client Secret: A secret key that you should keep confidential and never share with anyone You should copy and save these credentials somewhere safe, as you will need them later in your code. ## Step 4: Use [Unified.to](https://unified.to/) to Simplify your Integrations Development Using ZendeskSell APIs directly can be complex and time-consuming, especially if you want to integrate with multiple services or platforms. You will need to handle the authentication, the error handling, the rate limiting, the data mapping, and the synchronization logic for each integration. A simpler and faster alternative is to use [Unified.to](https://unified.to/) APIs for CRM integration. It provides a unified set of APIs, data-models, and webhooks for 20+ CRM integrations. ## Step 5: Activate Workday in Unified.to 1. Navigate to [Active Integrations](https://app.unified.to/integrations) 2. Search for ZendeskSell 3. Click on Activate on ZendeskSell 4. Enter in the information from the previous step (client ID, secret) ## How to Register a Zoho Developer App and Get OAUTH2 Credentials URL: https://docs.unified.to/guides/how_to_register_a_zoho_developer_app_and_get_oauth2_credentials # How to Register a Zoho Developer App and Get OAUTH2 Credentials ------ _August 23, 2024_ Zoho has many applications that we support integrations with; ZohoCRM, ZohoBooks, ZohoPeople, and ZohoRecruit. If you are a developer who wants to build data integrations for your SaaS software with any of the Zoho product suite, you will need to register a developer app and get credentials from Zoho. In this article, I will show you how to do that in a few simple steps. ## Step 1: Create a Zoho Developer Account The first step is to create a Zoho Developer account, if you don't have one already. You can sign up for free at [https://developer.zoho.com/signup](https://developer.zoho.com/signup). Once you have created your account, you will be able to access the Zoho Developer Console, where you can manage your apps and credentials. ## Step 2: Register a Zoho Developer App The next step is to register a Zoho developer app, which will allow you to access the Zoho API and data. To do that, follow these steps: - Go to the [Zoho Developer Console](https://api-console.zoho.com/) and click on the "Add App" button. - Choose "Server-Based" as the Client Type and click on "Create". - Enter a name and a homepage for your app - For your Authorized redirect URI, enter in [`https://api.unified.to/oauth/code`](https://api.unified.to/oauth/code) or `https://api-eu.unified.to/oauth/code` - Click on "Create". - You will see a confirmation message and a client ID and a client secret for your app. Copy and save them somewhere safe, as you will need them later. Congratulations! You have successfully registered a Zoho developer app and got credentials. You can now use them to access the Zoho API and data from your SaaS software. ## Step 3: Activate your Zoho Apps in [Unified.to](https://unified.to/) 1. Navigate to [Active Integrations](https://app.unified.to/integrations) 2. Search for ZohoCRM, ZohoBooks, ZohoPeople, or ZohoRecruit 3. Click on the integration 4. Enter in the information from the previous step (client ID, secret) 5. Click on Activate ## How to Register an ADP Developer App and Get OAUTH2 Credentials URL: https://docs.unified.to/guides/how_to_register_an_adp_developer_app_and_get_oauth2_credentials # How to Register an ADP Developer App and Get OAUTH2 Credentials ------ _April 30, 2023_ Developers who are looking to build data integrations for their SaaS software will need to register an ADP Developer application and obtain OAUTH2 credentials. This will allow you to access ADP data and build integrations with ADP services. In this article, we'll go over the steps to register an ADP Developer application and obtain OAUTH2 credentials. ## **Step 1: Create an ADP Developer Account** To get started, you will need to create an ADP Developer account. If you don't already have one, you can sign up for free on the [ADP Developer Portal](https://developers.adp.com/). ## **Step 2: Register a Developer App** Once you have an ADP Developer account, you can register a new developer app. Here are the steps to do so: 1. Log in to your ADP Developer account. 2. Navigate to the "Apps" section of the Developer Portal. 3. Click on the "Register App" button. 4. Fill in the required information for your app, such as its name, description, and callback URL. 5. Agree to the ADP Developer Terms of Service and Privacy Policy. 6. Click the "Submit" button to complete the app registration process. ## **Step 3: Get Your App Credentials** Once your app is registered, you can obtain your app credentials, including the Client ID and Client Secret, which are used to authenticate your app and access the ADP APIs. 1. Navigate to the "Apps" section of the Developer Portal. 2. Click on the name of the app you just registered. 3. Click on the "Credentials" tab to view your app's Client ID and Client Secret. 4. Make sure that your redirect_uri is [`https://api.unified.to/oauth/code`](https://api.unified.to/oauth/code) if you are going to use [Unified.to](https://unified.to/) with these credentials ## Step 4: Using Unified.to Registering an ADP Developer application and obtaining OAUTH2 credentials is an important step in building data integration for your SaaS software. With these credentials, you'll be able to access ADP data and build integrations with ADP services. Using ADP APIs directly can be complex and time-consuming, especially if you want to integrate with multiple services or platforms. You will need to handle the authentication, the error handling, the rate limiting, the data mapping, and the synchronization logic for each integration. It's much easier to use Unified.to APIs for building scalable integrations. With these APIs, you can focus on building your SaaS software and let Unified.to handle the heavy lifting of integrating with ADP. Check out the Unified.to website for more information on how they can help you build better integrations. ## Step 5: Activate ADP in Unified.to 1. Navigate to [Active Integrations](https://app.unified.to/integrations) 2. Search for ADP 3. Click on Activate on ADP 4. Enter in the information from the previous step (client ID, secret) ## How to Register an Atlassian Developer App and Get OAUTH2 Credentials URL: https://docs.unified.to/guides/how_to_register_an_atlassian_developer_app_and_get_oauth2_credentials # How to Register an Atlassian Developer App and Get OAUTH2 Credentials ------ _February 12, 2023_ Developers who are looking to build data integrations for their SaaS software with Atlassian services will need to register an Atlassian Developer application and obtain OAUTH2 credentials. This will allow you to access Atlassian data and build integrations with Atlassian services. In this article, we'll go over the steps to register an Atlassian Developer application and obtain OAUTH2 credentials. ## Step 1: **Registering an Atlassian Developer App** To register an Atlassian Developer app, you need to follow the steps below: 1. Go to the [**Atlassian Developer site**](https://developer.atlassian.com/console/myapps/). 2. Click on "Get started" and create an Atlassian account if you don't have one already. 3. Navigate to the "My Atlassian" page and click on "Create a new app." 4. Fill out the required information for your app, including the app name, description, and contact information. 5. Once you have completed the form, click on "Create app." 6. You will be redirected to the app's overview page, where you can see the app key and client ID. ## Step 2: **Obtaining Credentials** To obtain the necessary credentials for your salesforce integration, you need to do the following: 1. Go to the app's overview page and click on "Add credentials." 2. Select "OAuth" as the authentication method. 3. Fill out the necessary information, including the callback URL and OAuth scopes. If you are going to use Unified.to, then this needs to be `https://api.unified.to/oauth/code` 4. Once you have completed the form, click on "Create credentials." 5. You will be redirected to the credentials page, where you can see the client secret. ## Step 3: Using Unified.to Registering an Atlassian Developer application and obtaining OAUTH2 credentials is an important step in building data integration for your SaaS software. With these credentials, you'll be able to access ADP data and build integrations with Atlassian services. Using Atlassian APIs directly can be complex and time-consuming, especially if you want to integrate with multiple services or platforms. You will need to handle the authentication, the error handling, the rate limiting, the data mapping, and the synchronization logic for each integration. It's much easier to use Unified.to APIs for building scalable integrations. With these APIs, you can focus on building your SaaS software and let Unified.to handle the heavy lifting of integrating with ADP. Check out the Unified.to website for more information on how they can help you build better integrations. ## Step 4: Activate Atlassian in Unified.to 1. Navigate to [Active Integrations](https://app.unified.to/integrations) 2. Search for Atlassian 3. Click on Activate on Atlassian 4. Enter in the information from the previous step (client ID, secret) ## How to Register your Google Ads OAuth2 Application URL: https://docs.unified.to/guides/how_to_register_your_google_ads_oauth2_application # How to Register your Google Ads OAuth2 Application ------ _March 21, 2026_ ## **How to Register your Google Ads OAuth2 Application** 1. Create a Google Cloud project - Go to [**https://console.cloud.google.com/**](https://console.cloud.google.com/) - Click **Select a project** at the top of the page, then click **New Project** - Enter the following details: - Project Name - Organization (if applicable) - Click **Create** 2. Enable the Google Ads API - Go to [**https://console.cloud.google.com/apis/library**](https://console.cloud.google.com/apis/library) - Search for **Google Ads API** - Click on **Google Ads API** in the results - Click **Enable** 3. Configure the OAuth Consent Screen - Go to [**https://console.cloud.google.com/apis/credentials/consent**](https://console.cloud.google.com/apis/credentials/consent) - Select **External** as the user type, then click **Create** - Enter the following details: - App Name - User Support Email - Developer Contact Email - Click **Save and Continue** - On the Scopes step, click **Add or Remove Scopes** and add the following scope: - `https://www.googleapis.com/auth/adwords` - Click **Save and Continue** - On the Test Users step, add any Google accounts you want to test with - Click **Save and Continue** 4. Publish the OAuth Consent Screen - Go back to [**https://console.cloud.google.com/apis/credentials/consent**](https://console.cloud.google.com/apis/credentials/consent) - Click **Publish App** to move from Testing to Production status - This allows any Google user to authorize your application, not just the test users you added 5. Create OAuth2 Credentials - Go to [**https://console.cloud.google.com/apis/credentials**](https://console.cloud.google.com/apis/credentials) - Click **Create Credentials** → **OAuth client ID** - Select **Web application** as the Application Type - Enter a name for the OAuth client - Under **Authorized redirect URIs**, add the following **Unified.to** OAuth2 redirect URL: - [**https://api.unified.to/oauth/code**](https://api.unified.to/oauth/code) - Click **Create** - Copy the **Client ID** and **Client Secret** from the dialog that appears 6. Request Basic Access for the Google Ads Developer Token - Go to [**https://ads.google.com/aw/apicenter**](https://ads.google.com/aw/apicenter) - Note: You must be logged into a Google Ads Manager account. If you do not have one, create one at [**https://ads.google.com/intl/en/home/tools/manager-accounts/**](https://ads.google.com/intl/en/home/tools/manager-accounts/) - In the API Center, you will see your **Developer Token** - Your token will initially have **Test Account** access. To use it with production accounts, click **Apply for Basic Access** - Fill out the application form with details about how you will use the API - Google typically reviews and approves Basic Access applications within a few business days 7. Configure Unified.to - In Unified [**https://app.unified.to/integrations/googleads**](https://app.unified.to/integrations/googleads), add the following: - The OAuth client's **Client ID** - The OAuth client's **Client Secret** - Your **Developer Token** from the API Center You are now all set to authorize a Google Ads connection ## How to Register your MetaAds OAuth2 application URL: https://docs.unified.to/guides/how_to_register_your_metaads_oauth2_application # How to Register your MetaAds OAuth2 application ------ _March 4, 2026_ 1. Create a Meta Developer account - Go to [https://developers.facebook.com/](https://developers.facebook.com/) 2. Create an application - Go to [https://developers.facebook.com/apps/creation/](https://developers.facebook.com/apps/creation/) ![03cc19ca-b1b5-43fa-b858-37a6aeb9ab77.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_your_metaads_oauth2_application-0.png) - Enter the following details: - **App Name** - **App Contact Email** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_your_metaads_oauth2_application-1.png) ![Screenshot_2026-03-05_at_09.37.16.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_your_metaads_oauth2_application-2.png) - Add the following use-cases; 'Manage ads using Marketing API' and 'Measure ad performance' ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_your_metaads_oauth2_application-3.png) **3. Configure Permissions for the Marketing API** - Enable all permissions in the 'Create & Manage Ads' and 'Measure ad performance' use-cases ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_your_metaads_oauth2_application-4.png) - or - - After the application is created: - Open the **App Dashboard**. - Navigate to: Use Cases → Customize → Create & Manage Ads ![Screenshot_2026-03-05_at_09.42.12.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_your_metaads_oauth2_application-5.png) - Enable the following permissions: - ads_management - ads_read - business_management - email - Additional permissions such as: - pages_manage_ads - pages_read_engagement This may also be enabled depending on your integration requirements. ![Screenshot_2026-03-05_at_09.44.57.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_your_metaads_oauth2_application-6.png) **4. Configure Facebook Login for Business** - In the left sidebar, open (1, 2): Facebook Login for Business → Settings ![Screenshot_2026-03-05_at_09.49.45.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_your_metaads_oauth2_application-7.png) - Specify the [Unified.to](https://unified.to/) OAuth2 redirect URLs (3) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_your_metaads_oauth2_application-8.png) - Click **Save Changes (4)**. **5. Configure App Domains** - Go to App Settings → Basic - Add the following **App Domains**(3): [unified.to](https://unified.to/) [api.unified.to](https://api.unified.to/) [app.unified.to](https://app.unified.to/) [api-eu.unified.to](https://api-eu.unified.to/) [app-eu.unified.to](https://app-eu.unified.to/) [api-au.unified.to](https://api-au.unified.to/) [app-au.unified.to](https://app-au.unified.to/) - Scroll down and save the settings (4). ![Screenshot_2026-03-05_at_10.05.24.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_register_your_metaads_oauth2_application-9.png) In Unified [https://app.unified.to/integrations/metaads](https://app.unified.to/integrations/metaads), add the application's App ID (as the client ID) and the App secret (as the client secret). This information can be found in the App settings > Basic page. You are now all set to authorize a MetaAds connection ## How To Request & Write Raw Integration Data URL: https://docs.unified.to/guides/how_to_request_and_write_raw_integration_data # How To Request & Write Raw Integration Data ------ _February 6, 2024_ ### Reading data While [Unified.to](https://unified.to/) abstracts all of the integration's objects into common objects, you can still request that the original object. Simply include a `fields=raw` parameter in your request. ```javascript GET /crm/123456789/contacts?fields=raw ``` The result will also include a field named `raw` that will contain the original vendor's object. You can add more field names in there as well (comma-delimited) and only those fields will be returned. ### Writing data Simply include a `raw` field in the payload with the integration's specific fields. ```javascript POST /crm/123456789/contacts { "name": "Joe Smith", "raw": { "customfield1":"Blue", "customfield2":"Bananas" } } ``` Those fields in the `raw` object can also overwrite any mapped unified fields. ### Passing Raw Parameters Some providers support extra filters or options that Unified doesn't standardize. You can pass them using the `raw` query parameter. Take normal query params like: ```plain text status=active&limit=10 ``` Encode them into: ```plain text GET /crm/123456789/contact?raw=status%3Dactive%26limit%3D10 ``` ## How to Retrieve Microsoft Dynamics 365 Business Central Credentials URL: https://docs.unified.to/guides/how_to_retrieve_microsoft_dynamics_365_business_central_credentials # How to Retrieve Microsoft Dynamics 365 Business Central Credentials ------ _January 21, 2026_ This guide walks you through creating an Azure App Registration, granting the correct permissions, and collecting the credentials required to integrate with **Microsoft Dynamics 365 Business Central** using Unified. --- ## 1. Create or Select an App Registration ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_retrieve_microsoft_dynamics_365_business_central_credentials-0.png) 1. Go to the Azure Portal: 👉 [https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) 2. Navigate to **App registrations** 3. Either: - Select an existing app, **or** - Click **New registration** to create a new one --- ## 2. Configure Redirect URI 1. Inside your App Registration, go to **Authentication** 2. Under **Redirect URI (optional)**: - Select **Web** - Enter the redirect URL provided by Unified ```plain text https://api.unified.to/oauth/code ``` 3. Click **Save** --- ## 3. Grant Business Central API Permissions ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_retrieve_microsoft_dynamics_365_business_central_credentials-1.png) 1. In the App Registration, go to **API permissions** 2. Click **Add a permission** 3. Select **APIs my organization uses** 4. Search for and select **Microsoft Dynamics 365 Business Central** 5. Choose **Delegated permissions** 6. Select the required permissions (typically default access) 7. Click **Add permissions** 8. If required, click **Grant admin consent** --- ## 4. Get Client ID and Client Secret ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_retrieve_microsoft_dynamics_365_business_central_credentials-2.png) 1. In the App Registration, go to **Certificates & secrets** 2. Copy the following: - **Client ID** (from the app Overview page) - **Client Secret** - Create a new client secret if one does not exist - Copy it immediately (it will not be shown again) ⚠️ Store the client secret securely. --- ## 5. Retrieve Business Central Company Name ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_retrieve_microsoft_dynamics_365_business_central_credentials-3.png) Unified also requires the **Business Central company name**. 1. Go to: 👉 [https://businesscentral.dynamics.com/](https://businesscentral.dynamics.com/) 2. Sign in to your Business Central account 3. Locate the **company name** using either method: - The company name shown in the **top-left corner**, **or** - Click the **building (company) icon** in the navigation bar and copy the desired company name Use **exactly** the company name as shown. --- ## 6. Information You Will Need for Unified After completing the steps above, you should have: - ✅ Client ID - ✅ Client Secret - ✅ Business Central Company Name These values are required to complete the Business Central connection in Unified. ## How to set up a custom API URL URL: https://docs.unified.to/guides/how_to_set_up_a_custom_api_url # How to set up a custom API URL ------ _September 5, 2025_ This guide will walk you through the process of setting up a custom CNAME (subdomain) for your Unified API workspace. This allows you to use your own domain instead of the default `api.unified.to` domain for all API calls and OAuth flows. > 💡 This is mandatory when using Sensitive scopes in any of the Google integrations, doing this will allow the Google verification to pass. ## Prerequisites - You must have a paid Unified plan (custom domains are not available on the free/test plan) - Access to your domain's DNS management console - Admin access to your Unified workspace ## Step 1: Choose Your Custom Subdomain Decide on a subdomain you want to use. For example: - `api.yourcompany.com` - `unified-api.yourcompany.com` - `integrations.yourcompany.com` ## Step 2: Configure DNS CNAME Record 1. **Log into your domain registrar or DNS provider** (e.g., Cloudflare, GoDaddy, Namecheap, AWS Route 53) 2. **Navigate to DNS management** for your domain 3. **Add a new CNAME record** with the following settings: - **Type**: CNAME - **Name**: Your chosen subdomain (e.g., `api` or `unified-api`) - **Value/Target**: US: [unified-domains-us-7d67c6f04efebb36.elb.us-east-1.amazonaws.com](https://unified-domains-us-7d67c6f04efebb36.elb.us-east-1.amazonaws.com/) EU: [unified-domains-eu-a0f8a17b303a3c06.elb.eu-central-1.amazonaws.com](https://unified-domains-eu-a0f8a17b303a3c06.elb.eu-central-1.amazonaws.com/) - **TTL**: 3600 (or default) - **Proxy status**: Disabled (if using Cloudflare) 4. **Save the CNAME record** ## Step 3: Wait for DNS Propagation DNS changes can take anywhere from a few minutes to 48 hours to propagate globally. You can check propagation using tools like: - [whatsmydns.net](https://whatsmydns.net/) - [dnschecker.org](https://dnschecker.org/) ## Step 4: Contact Unified Support Once your CNAME is configured and propagated: 1. **Contact Unified Support** through your preferred channel 2. **Provide the following information**: - Your workspace name/ID - The custom subdomain you want to use - Confirmation that the CNAME is configured and pointing to [unified-domains-us-7d67c6f04efebb36.elb.us-east-1.amazonaws.com](https://unified-domains-us-7d67c6f04efebb36.elb.us-east-1.amazonaws.com/) 3. **Support will assign the custom domain** to your workspace ## Step 5: Update OAuth Application Settings **CRITICAL**: After your custom domain is assigned, you must update all OAuth applications to use the new redirect URL. ### For OAuth Applications: 1. Go to yor app Console 2. Update the **Authorized redirect URIs** from: ```plain text https://api.unified.to/oauth/code ``` to: ```plain text https://your-custom-domain.com/oauth/code ``` ## Step 6: Verify the Setup 1. **Test your custom domain** by making a simple API call to: ```plain text https://your-custom-domain.com/swagger.json ``` 2. **Test OAuth flow** by attempting to authenticate with an integration using your custom domain 3. **Verify redirect URLs** are working correctly in your OAuth applications ## Important Notes ### OAuth Verification Requirements - **Google OAuth**: After changing the redirect URL, you may need to complete app verification since Google sees this as a new domain - **Other providers**: May require similar verification processes - **App verification**: Follow the provider's specific verification process (e.g., [Google's verification guide](https://support.google.com/cloud/answer/13461325)) ### API Usage - All API calls will now use your custom domain - The API functionality remains identical - Your API keys and authentication methods remain the same ### Security Considerations - Ensure your DNS provider has proper security measures - Consider enabling DNSSEC if available - Monitor for any unauthorized DNS changes ## Troubleshooting ### Common Issues 1. **DNS Not Propagated** - Wait longer for propagation - Check with multiple DNS lookup tools - Verify CNAME record is correct 2. **OAuth Errors After Domain Change** - Ensure redirect URLs are updated in all OAuth applications - Complete app verification if required by the provider - Check that the custom domain is properly assigned to your workspace 3. **API Calls Failing** - Verify the CNAME is pointing to `api.unified.to` - Check that the custom domain is assigned to your workspace - Ensure your API keys are still valid ## Example Configuration Here's a complete example for setting up `api.yourcompany.com`: ### DNS Configuration: ```plain text Type: CNAME Name: api Value: api.unified.to TTL: 3600 ``` ### OAuth Redirect URL: ```plain text https://api.yourcompany.com/oauth/code ``` ### API Endpoint: ```plain text https://api.yourcompany.com/v1/your-endpoint ``` --- **Note**: Custom domain setup requires coordination with Unified Support to ensure proper configuration and security. Please allow 1-2 business days for the setup to be completed after your CNAME is configured. ## How to Set Up a Microsoft Teams Bot with Unified URL: https://docs.unified.to/guides/how_to_set_up_a_microsoft_teams_bot_with_unified # How to Set Up a Microsoft Teams Bot with Unified ------ _April 20, 2026_ ## Overview If your Microsoft Teams bot connection is created successfully in Unified but later API calls fail with `No authorization information present on the request`, the issue is usually not OAuth. OAuth connects Microsoft 365 to Unified, but your bot still needs to be: - packaged correctly - uploaded to Teams - installed in the correct Team, channel, or chat Without this, API calls will fail due to missing context. --- ## ⚠️ Important Clarification (Common Issue) - The manifest downloaded from **Microsoft Entra app registration is NOT a valid Teams manifest** - The Microsoft 365 schema is also **not the correct reference** You must use the **Microsoft Teams manifest schema**: [https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema) Using the wrong manifest will result in errors like: `Manifest parsing error message unavailable` --- ## What You'll Need Before starting: - Guide: [https://docs.unified.to/guides/how_to_get_your_oauth_2_credentials_for_microsoft_dynamics_365](https://docs.unified.to/guides/how_to_get_your_oauth_2_credentials_for_microsoft_dynamics_365) - A Microsoft Entra app / bot registration - A Microsoft Teams app package (`.zip`) - Permission to upload custom apps in Teams - A Unified connection using **Microsoft Teams (bot)** --- ## Create a Valid Teams App Package Your `.zip` must include: - `manifest.json` - Color icon (192×192 PNG) - Outline icon (32×32 PNG) --- ### Recommended Approach (Avoid Manual Errors) Instead of writing JSON manually, use: 👉 Teams Developer Portal [https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/build-and-test/teams-developer-portal](https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/build-and-test/teams-developer-portal) This allows you to: - Configure your bot via UI - Set scopes correctly - Generate a valid app package - Avoid manifest parsing errors --- ### Manifest Checklist Ensure your `manifest.json` includes: - Valid app ID - Bot capability with correct `botId` - Required scopes: - `team` - `groupchat` - `personal` (optional) Use: - `team` → for channels - `groupchat` → for chats --- ### Minimal Manifest Template (Reference) ```plain text { "$schema":"https://developer.microsoft.com/en-us/json-schemas/teams/v1.16/MicrosoftTeams.schema.json", "manifestVersion":"1.16", "version":"1.0.0", "id":"YOUR-APP-ID", "name": { "short":"My Teams Bot", "full":"My Teams Bot" }, "description": { "short":"Bot for Teams", "full":"Bot for Teams integrated with Unified" }, "developer": { "name":"Your Company", "websiteUrl":"https://yourdomain.com", "privacyUrl":"https://yourdomain.com/privacy", "termsOfUseUrl":"https://yourdomain.com/terms" }, "icons": { "color":"color.png", "outline":"outline.png" }, "accentColor":"#FFFFFF", "bots": [ { "botId":"YOUR-ENTRA-APP-ID", "scopes": ["team","groupchat"] } ] } ``` --- ### ⚠️ Notes (Follow as Steps) 1. Replace `YOUR-APP-ID` and `YOUR-ENTRA-APP-ID` with your actual app ID 2. Ensure both icons exist in the `.zip` file 3. Use this only as a reference template, not a full production manifest --- ## Upload the App in Microsoft Teams Steps: 1. Open **Apps** (left sidebar) 2. Click **Manage your apps** 3. Stay on **Apps tab** 4. Click **Upload an app** 5. Upload your `.zip` ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_a_microsoft_teams_bot_with_unified-0.png) ⚠️ If upload option is missing → admin restriction --- ## Install the Bot in the Right Scope Uploading ≠ installing You must install the bot in: - A Team - A channel - A group chat - Personal scope ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_a_microsoft_teams_bot_with_unified-1.png) ⚠️ If not installed → API calls will fail --- ## Connect Microsoft Teams (bot) to Unified Steps: 1. Create connection in Unified 2. Complete OAuth 3. Approve scopes 4. Save connection ### Common Required Scopes - `Channel.ReadBasic.All` - `ChannelMessage.Read.All` - `ChatMessage.Read` - `Chat.Read` - `ChannelMessage.Send` - `ChatMessage.Send` - `offline_access` 💡 If permissions change → reconnect integration --- ## Test the Connection 1. Send a message where the bot is installed 2. Call Unified messaging API 3. Verify read/send works 💡 If connection was created before installation → retry --- ## Troubleshooting ### ❌ Manifest parsing error - Wrong manifest type (Entra instead of Teams) - Wrong schema --- ### ❌ App package failed validation - Missing icons - Files not at root - Invalid JSON - `botId` mismatch --- ### ❌ No authorization information present - Bot not installed in correct scope - Missing permissions - Wrong scope - Token outdated → reconnect ## How to set up and configure Notion URL: https://docs.unified.to/guides/how_to_set_up_and_configure_notion # How to set up and configure Notion ------ _May 21, 2024_ This guide describes how to register a public Notion application (a.k.a. ‘integration') and configure it so that you can use the Unified API to query pages and databases. ## Understanding Notion integrations: Internal vs Public Before setting up your Notion integration, it's important to understand the two types of integrations Notion offers: 1. **Internal Integrations**: These are confined to a single workspace and are only accessible by members of that workspace. They're ideal for custom workspace enhancements. 2. **Public Integrations**: These are designed for a wider audience and can be used across any Notion workspace. They follow the OAuth 2.0 protocol for workspace access. The main difference in terms of accessing user data is: - For Internal Integrations, workspace members explicitly grant access to their pages or databases via Notion's UI. - For Public Integrations, users authorize access to their pages during the OAuth flow, or by sharing pages directly with the integration. For more information on the differences, refer to the [Notion API Overview](https://developers.notion.com/docs/getting-started). ## Setting up your Notion integration The following steps outline how to register a public Notion integration: 1. Go to [https://www.notion.so/my-integrations](https://www.notion.so/my-integrations) 2. Click on your integration (or create a new one) 3. Follow the steps to register a new Notion integration. 1. Under **Type**, select **Public**. 2. Under **Redirect URIs**, enter `https://api.unified.to/oauth/code` 4. Click **Save**. 5. Go to the configuration settings of your newly created integration and make a note of the **OAuth Client ID** and **OAuth Client Secret.** 6. Still under the configuration settings, make sure to select the permissions you need for your app under **Capabilities** e.g. read or update content. 7. Enter your OAuth credentials on [https://app.unified.to/integrations/notion](https://app.unified.to/integrations/notion) to activate the Notion integration. **Note:** If you wish to create an Internal integration, most of the steps above can be skipped, and you will need to enter your `Internal integration token` on [app.unified.to](https://app.unified.to/) instead. Instructions on how to find your internal token can be found [here](https://www.notion.so/help/create-integrations-with-the-notion-api). ## Querying databases in Notion To query databases in Notion, you'll need to use our Unified KMS endpoint: 1. First, make sure you have a `connection_id` for your Notion integration. 2. Since database entries in Notion are technically pages, we'll use the **List all pages** endpoint: ```plain text GET /kms/{connection_id}/page ``` API reference: [List all pages](https://docs.unified.to/kms/page/List_all_pages) 1. When querying pages in a database, you need to pass the database ID as the `space_id` parameter. For example: ```plain text GET /kms/{connection_id}/page?space_id={database_id} ``` 2. You can use additional parameters to filter and sort your results: - `limit`: Number of results to return (default: 30) - `offset`: Number of results to skip (default: 0) - `updated_gte`: Return only results updated on or after this date - `sort`: Sort by 'name', 'updated_at', or 'created_at' - `order`: Sort order ('asc' or 'desc') - `query`: Search query string - `fields`: Comma-separated list of fields to return For example, to get the first 50 pages in a database, sorted by name in ascending order: ```plain text GET /kms/{connection_id}/page?space_id={database_id}&limit=50&sort=name&order=asc ``` ## Webhooks Notion doesn't offer programmatic creation of webhooks, so you need to first create webhooks for your application. Then, you can use our CreateWebhook API endpoint to select what data and connection you want to receive. 1. Go you your Notion's integrations page at [https://www.notion.so/profile/integrations](https://www.notion.so/profile/integrations) 2. Either create a new integration or select an existing one. 3. Navigate to the **Webhooks** tab and click **+ Create a subscription**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_and_configure_notion-0.png) 4. Enter your public **Webhook URL** — this is the public endpoint where you want Notion to send events. It must be a secure (SSL) and publicly available endpoint. Endpoints in localhost are not reachable. 1. US: `https://api.unified.to/webhook/workspace/notion?workspace_id=$WORKSPACE_ID` 2. EU: `https://api-eu.unified.to/webhook/workspace/notion?workspace_id=$WORKSPACE_ID` ![1ef497d7b9b3622de379e6907cd722167766413693ac9f1885b59eb028b4e7dd-webhooks-2.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_and_configure_notion-1.png) 5. Select the events that you are interested in receiving from all of your customers. Created and Deleted events are associated with our `created` and `deleted` events, while all other events are associated with our `updated` event. Both Notion's `Page` and `Database` are mapped to Unified's `KMSPage`, while `Comments` are mapped to `KMSComment`. You can modify these later if needed. 6. Click **Create subscription**. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_and_configure_notion-2.png) 7. After you create the notion webhook, notion will send us a verification token. You can find that verification token in [https://app.unified.to/settings/api](https://app.unified.to/settings/api). Paste the `verification_token` value into the Verify Subscription form in Notion, and click **Verify subscription.** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_and_configure_notion-3.png) Once completed, you can create webhooks in Unified.to that will receive these Notion events in real-time. ## How to Set Up LinkedIn Webhooks with Unified URL: https://docs.unified.to/guides/how_to_set_up_linkedin_webhooks_with_unified # How to Set Up LinkedIn Webhooks with Unified ------ _February 14, 2026_ LinkedIn's Organization Social Action Notifications let you receive real-time updates when people interact with your company page -- likes, comments, shares, and mentions. With Unified, you can subscribe to these events through the same webhook API you use for every other integration, so there's no need to learn LinkedIn's proprietary webhook protocol. ## **What You'll Need** Before starting, review the [Getting Started with Unified](https://docs.unified.to/guides/getting_started_with_unified) article if you haven't already. You'll also need: - A LinkedIn developer app (see setup instructions below) - A LinkedIn connection created through Unified's embedded auth flow - A publicly accessible URL to receive webhook events ## **Setting Up Your LinkedIn Developer App** Before you can receive webhook events, you need a LinkedIn app configured with the right products and permissions. **Creating the App** 1. Go to [linkedin.com/developers/apps](https://www.linkedin.com/developers/apps)and click **Create App** 2. Fill in the required fields: - **App name**: Your application's name - **LinkedIn Page**: Select the LinkedIn company page you want to monitor (you must be an admin of the page) - **App logo**: Upload a logo for your app 3. Accept the legal terms and click **Create app** **Requesting API Products** LinkedIn gates webhook access behind specific API products. From your app's settings page: 1. Navigate to the **Products** tab 2. Request access to **Community Management API** -- this grants the `r_organization_social` scope needed for social action notifications 3. If you also want to send messages, request **LinkedIn Marketing API Partner Program** for the `w_messages` scope Product approvals can take a few business days. You can check the status on the **Products** tab. **Configuring OAuth Scopes** Once your products are approved: 1. Go to the **Auth** tab in your app settings 2. Under **OAuth 2.0 scopes**, verify that `r_organization_social` is listed 3. Copy your **Client ID** and **Client Secret** -- you'll need these when creating a connection in Unified 4. Under **Authorized redirect URLs**, add your Unified OAuth callback URL **Verifying Your Company Page** LinkedIn requires that your developer app is associated with a verified company page: 1. Go to the **Settings** tab in your app 2. Under **App Settings**, confirm the correct LinkedIn Page is linked 3. A page admin must verify the app -- click **Generate URL** next to the verification prompt and share it with a page admin if needed Once the page is verified and the products are approved, your app is ready to receive social action notifications. ## **Creating a Webhook for LinkedIn Events** You can create a webhook subscription through the API or the Unified dashboard. The webhook will listen for `messaging_event` objects with the `created` event, which is how LinkedIn's social action notifications are surfaced. **Via the API** `import { UnifiedTo } from '@unified-api/typescript-sdk'; const sdk = new UnifiedTo({ security: { jwt: process.env.UNIFIED_API_KEY!, }, }); const webhook = await sdk.unified.createUnifiedWebhook({ webhook: { hookUrl: 'https://your-app.com/webhooks/linkedin', connectionId: process.env.LINKEDIN_CONNECTION_ID!, objectType: 'messaging_event', event: 'created', }, }); console.log('Webhook created:', webhook.webhook?.id);` **Via the Dashboard** 1. Open [app.unified.to](https://app.unified.to/) and navigate to your workspace 2. Go to **Webhooks** and click **Create Webhook** 3. Select your LinkedIn connection 4. Set the object type to `messaging_event` and the event to `created` 5. Enter your webhook URL and save Once the webhook is active, Unified registers your endpoint with LinkedIn's event subscription API using the organization URN from your connection. ## **Receiving Events** When someone interacts with your LinkedIn company page, Unified converts the raw LinkedIn notification into a standard `MessagingEvent` object and POSTs it to your `hook_url`. Here's what that looks like: `import express from 'express'; const app = express(); app.use(express.json()); app.post('/webhooks/linkedin', (req, res) => { const event = req.body; switch (event.type) { case 'MESSAGE_RECEIVED': // Someone commented on a company post, shared it, or edited/deleted a comment console.log('Comment or share from:', event.user?.id); console.log('On organization:', event.channel?.id); break; case 'REACTION_ADDED': // Someone liked a company post console.log('Like from:', event.user?.id); break; case 'APP_MENTION': // Your company was mentioned in a share console.log('Mentioned by:', event.user?.id); break; } res.sendStatus(200); });` ## **Event Type Mapping** LinkedIn's social action types are mapped to Unified's standard `MessagingEvent`types: - **COMMENT** and **ADMIN_COMMENT**become `MESSAGE_RECEIVED` -- a new comment was posted on your organization's content - **LIKE** becomes `REACTION_ADDED` -- someone liked your organization's post - **SHARE** becomes `MESSAGE_RECEIVED` -- someone shared your organization's content - **SHARE_MENTION** becomes `APP_MENTION` -- your organization was mentioned in a share - **COMMENT_EDIT** and **COMMENT_DELETE** become `MESSAGE_RECEIVED` -- an existing comment was modified or removed Each event includes the `channel` field set to your organization's ID, the `user`field with the actor's person ID, and a `created_at` timestamp. The original LinkedIn payload is always available in the `raw` field if you need additional detail. ## **Sending Messages Alongside Webhooks** While webhooks cover inbound events, you can also send outbound messages through LinkedIn's Messages API using the same connection. This is useful for responding to engagement by reaching out to the people interacting with your content. `const message = await sdk.messaging.createMessagingMessage({ messagingMessage: { message: 'Thanks for engaging with our content!', destinationMembers: [ { userId: 'urn:li:person:ABC123' }, ], }, connectionId: process.env.LINKEDIN_CONNECTION_ID!, });` Note that LinkedIn's Messages API requires partner-level access with the `w_messages` scope. Message creation supports new conversations (via `destinationMembers`) or replies to existing threads (via `channelId`). ## **Troubleshooting** **Webhook not receiving events?** LinkedIn requires the `r_organization_social`OAuth scope, which is granted through the Community Management API product. Verify your app has this product approved on the [LinkedIn Developer Portal](https://www.linkedin.com/developers/apps), and that your connection has the scope by checking the connection details in the Unified dashboard. **Events arriving for the wrong organization?** The webhook subscription is tied to the `organization_id` stored in your connection metadata, which is set during the initial OAuth flow. If you manage multiple LinkedIn organizations, create separate connections for each one. **LinkedIn Page not verified?** Your developer app must be associated with a verified company page. Go to your app's **Settings** tab on the LinkedIn Developer Portal and complete the page verification process. A page admin must approve the association. With just a few lines of code, you're now receiving real-time LinkedIn engagement data through the same webhook infrastructure you use for Slack, Discord, and every other messaging integration on Unified. No custom protocol handling required. [→ Start your 30-day free trial](https://app.unified.to/login) [→ Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) ## How to set up Slack webhooks using event subscriptions URL: https://docs.unified.to/guides/how_to_set_up_slack_webhooks_using_event_subscriptions # How to set up Slack webhooks using event subscriptions ------ _November 14, 2024_ This guide explains how to configure your Slack app's event subscriptions to work with Unified.to webhooks. This is a required step before you can create webhooks that listen for Slack events. Once configured, your webhooks will receive real-time notifications when specific events occur in your Slack workspace, such as when users send messages. ## Before you begin Ensure you have: - A Slack app created in your workspace - Access to your Slack app's configuration settings - Your [Unified.to](https://unified.to/) workspace ID - An understanding of [how webhooks work in Unified.to](https://docs.unified.to/concepts/webhooks) - Create a webhook in your ## Enable event subscriptions in Slack 1. Go to the Slack API dashboard at [https://api.slack.com/apps](https://api.slack.com/apps) 2. Select your app from the list of available apps 3. In the left sidebar, under **Features**, click **Event Subscriptions** 4. Toggle the **Enable Events** switch to **On** 5. In the **Request URL** field, enter (depending on your data center —- replace ID with your workspace ID —- replace `slack` with `slackbot` if using that integration): 1. (US data center) `https://api.unified.to/webhook/workspace/slack?workspace_id={ID}` 2. (EU data center) `https://api-eu.unified.to/webhook/workspace/slack?workspace_id={ID}` 6. Wait for Slack to verify the URL. You should see a green checkmark indicating successful verification. ![Screenshot_2024-11-14_at_5.10.40_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_slack_webhooks_using_event_subscriptions-0.png) 7. Click **Save Changes** at the bottom of the page ## Subscribe to events from Slack After you've enabled events and verified the request URL works, you need to subscribe to the events that you are interested in. 1. The minimum event you should subscribe to is`message.groups` (under user) 2. The other events you can subscribe to are the ones that fall under the following scopes under **Subscribe to events on behalf of users**: 1. `message.channels` 2. `message.groups` 3. `message.im` 4. `message.mpim` ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_slack_webhooks_using_event_subscriptions-1.png) _Example of Slack events that you can subscribe to for webhook events_ 3. Click **Save Changes** at the bottom of the page Once completed, you can create webhooks in Unified.to that will receive these Slack events in real-time. Happy building! ## See also - [How to create and configure webhooks](https://docs.unified.to/guides/how_to_create_and_configure_webhooks) - [How to troubleshoot unhealthy webhooks](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_webhooks) ## How to set up Telegram (bot) with Unified.to URL: https://docs.unified.to/guides/how_to_set_up_telegram_bot_with_unified # How to set up Telegram (bot) with Unified.to ------ _June 30, 2026_ Telegram's **Bot API** lets you send and receive messages through a bot account you control. This guide walks you through creating a Telegram bot, connecting it to Unified.to with its bot token, and (optionally) turning on webhooks so you receive incoming messages and button clicks in real time. **Estimated time:** 10 minutes --- **Prerequisites** - A Telegram account (any mobile or desktop client). - A Unified.to account with a workspace. [Sign up here](https://unified.to/) if you don't have one. - For receiving messages via webhooks: nothing extra — Unified.to hosts the HTTPS endpoint for you. > **Bot API vs. personal account:** This integration uses the Telegram **Bot API**, so all messages are sent and received as your **bot**, not as your personal Telegram user. People interact with your bot by starting a chat with it or by adding it to a group/channel. --- **Step 1 — Create a bot and get your bot token** 1. In any Telegram client, open a chat with [**@BotFather**](https://t.me/BotFather) (the official bot for creating bots). 2. Send the command **`/newbot`**. 3. When prompted, enter a **name** for your bot (the display name, e.g. _Acme Support_). 4. Then enter a **username** for your bot. It must end in `bot` (e.g. `acme_support_bot`). 5. BotFather replies with a confirmation that includes your **HTTP API token**. It looks like this: ```plain text 123456789:AAEhBP0...M_zv1u123ew11 ``` 6. **Copy this token** and keep it safe — it grants full control of your bot. You'll paste it into Unified.to in Step 3. > Lost the token later? Send **`/token`** to BotFather and pick your bot, or **`/revoke`** to roll it. --- **Step 2 — (Optional) Let your bot read group messages** By default a bot in **privacy mode** only receives messages that mention it or reply to it. If you want your bot to receive **all** messages in a group: 1. Open [**@BotFather**](https://t.me/BotFather) and send **`/setprivacy`**. 2. Select your bot. 3. Choose **Disable**. You can skip this step for one-to-one (direct) chats — bots always receive direct messages sent to them. --- **Step 3 — Connect Telegram in Unified.to** 1. In the Unified.to dashboard, go to **Integrations** and search for **Telegram (bot)**. 2. Click **Connect** (or **Authorize**) to start a new connection. 3. When prompted for credentials, paste the value from Step 1 into the **Bot Token** field. 4. Submit. Unified.to validates the token by calling the Telegram `getMe` endpoint and, on success, creates the connection. The bot's name is shown on the connection. You can also create the connection programmatically by storing the bot token as the connection's token credential — see the [Authorization API](https://docs.unified.to/unified/unified/authorization). That's it for sending messages. To **receive** messages, continue to the webhook setup below. --- **Step 4 — Find a chat ID to send to** The Bot API can't list chats, so you need the **chat ID** of whoever you want to message. A chat ID is a number (e.g. `123456789` for a person, or a negative number like `-1001234567890` for a group/channel). The easiest way to discover one: 1. From the recipient's Telegram account, **start a chat with your bot** (search its `@username` and press **Start**), or **add the bot to your group/channel**. 2. Have them send any message to the bot / group. 3. That inbound message arrives through your Unified.to webhook (Step 5) and includes the `channel_id` (the Telegram chat ID). Use that value as the destination for future messages. **Sending a message** Once you have a `channel_id`, send a message through the unified `messaging_message` endpoint: ```plain text curl -X POST 'https://api.unified.to/messaging//message' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "channels": [{ "id": "123456789" }], "message": "Hello from Unified.to 👋" }' ``` You can also include inline buttons (mapped to Telegram inline keyboards) and reply to a message via `parent_id`. The response contains the new message `id`, which is required to **edit** (`editMessageText`) or **delete** (`deleteMessage`) it later. --- **Step 5 — Set up a webhook to receive messages** Unified.to registers the Telegram webhook for you — you don't call `setWebhook` yourself. When you create a webhook subscription, Unified.to points your bot at a secure HTTPS endpoint and verifies every delivery with a secret token. 1. In the Unified.to dashboard, open your **Telegram (bot)** connection and go to **Webhooks** (or use the [Webhook API](https://docs.unified.to/unified/unified/createunifiedwebhook)). 2. Create a webhook and choose what you want to receive: - **`messaging_message`**, event **`created`** — each new incoming message (and **`updated`** for edited messages). - **`messaging_event`**, event **`created`** — incoming messages **and** inline-button clicks, delivered as unified events (`MESSAGE_RECEIVED`, `BUTTON_CLICK`). 3. Provide your destination URL — the place Unified.to should forward normalized events to. 4. Save. Unified.to calls Telegram's `setWebhook` behind the scenes and begins forwarding events. Example webhook creation via the API: ```plain text curl -X POST 'https://api.unified.to/unified/webhook' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "connection_id": "", "object_type": "messaging_message", "event": "created", "hook_url": "https://your-app.example.com/telegram/incoming" }' ``` To stop receiving events, delete the webhook. Unified.to removes the bot's Telegram webhook automatically once the last subscription on the connection is gone. --- **Notes & troubleshooting** - **One webhook per bot.** Telegram allows a single webhook URL per bot, so all of a connection's webhook subscriptions share the same registration — that's expected and handled automatically. - **HTTPS is required.** Telegram only delivers updates to HTTPS endpoints. Webhooks therefore work on Unified.to's hosted environment but not against a plain-HTTP localhost listener during local development. - **No history.** The Bot API cannot list or fetch past messages or chats — a bot only sees messages sent **after** it joined a chat, delivered via webhooks. There is no "list messages" or "list channels" operation. - **Not seeing group messages?** Make sure you disabled privacy mode (Step 2) and re-added the bot to the group if needed. - **`getMe`** **/ connection fails?** Double-check the bot token was pasted exactly, with no extra spaces, and that the bot hasn't been revoked in BotFather. --- **What you can do next** - Send, edit, and delete messages (`sendMessage`, `editMessageText`, `deleteMessage`). - Look up a single chat's details with `messaging_channel` (`getChat`). - Receive incoming messages and inline-button clicks via `messaging_message` and `messaging_event` webhooks. Need help? [Contact Unified.to support](https://unified.to/) or browse the [other guides](https://docs.unified.to/guides). ## How to set up your scopes in HubSpot URL: https://docs.unified.to/guides/how_to_set_up_your_scopes_in_hubspot # How to set up your scopes in HubSpot ------ _June 11, 2024_ This guide explains how to correctly configure the advanced scopes settings in your HubSpot developer app for seamless integration with Unified.to. We'll cover setting up both required and optional scopes, and address some common issues you might encounter. ## Before you begin This guide assumes you have a basic understanding of: - [Scopes](https://docs.unified.to/concepts/scopes) - [How to create a connection (Project) ](https://docs.unified.to/guides/how_to_create_connection_with_hubspot) - [How to get your HubSpot developer key and OAuth 2 credentials (Legacy App)](https://docs.unified.to/guides/how_to_get_your_hubspot_developer_key_and_oauth_2_credentials_legacy_apps) ## Set up required scopes in HubSpot (Project) 1. Go to [https://offers.hubspot.com/free-cms-developer-sandbox](https://offers.hubspot.com/free-cms-developer-sandbox), Sign in. 2. Development → Project → select your project 3. In your project(code) you will see following folder structure. GOTO→ yourproject (account-testing) → app-hsmeta.json ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_your_scopes_in_hubspot-0.png) 1. Following will be the scopes and redirect URL. You can remove which you want. However, do remember those removed scopes before creating a connection in unified. Also remember to add redirect URL as follows. ```javascript { "uid": "get_started_app", "type": "app", "config": { "description": "A Unified.to token app for testing", "name": "My Get Started app", "distribution": "private", "auth": { "type": "oauth", "redirectUrls": [ "https://api.unified.to/oauth/code", ], "requiredScopes": [ "oauth", "crm.objects.companies.read", "crm.objects.contacts.read", "crm.objects.deals.read", "crm.objects.owners.read", "crm.pipelines.orders.read", "crm.objects.contacts.write", "crm.pipelines.orders.read", "crm.pipelines.orders.write", "crm.objects.deals.write", "sales-email-read", "content", "tickets", "crm.lists.read", "settings.users.teams.read" ], "optionalScopes": [], "conditionallyRequiredScopes": [] }, "permittedUrls": { "fetch": ["https://api.hubapi.com"], "iframe": [], "img": [] }, "support": { "supportEmail": "support@example.com", "documentationUrl": "https://example.com/docs", "supportUrl": "https://example.com/support", "supportPhone": "+18005555555" } } } ``` 1. You can update those changes using following command in terminal. ```javascript hs project upload ``` 1. When the code is uploaded successfully you can check that by going to following Development→ Project → Select your project. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_your_scopes_in_hubspot-1.png) 1. To check your scopes click on the project Component. It will show your app-hsmeta.json ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_your_scopes_in_hubspot-2.png) ## Set up required scopes in HubSpot (Legacy App) **Note**: If you are using webhooks, please refer to: [How to configure webhooks in HubSpot](https://docs.unified.to/guides/how_to_configure_webhooks_in_hubspot) 1. Go to [https://offers.hubspot.com/free-cms-developer-sandbox](https://offers.hubspot.com/free-cms-developer-sandbox), Sign in. 2. Development → Legacy App→ Your New App 3. In the same Auth tab, scroll 4. If you are not using webhooks, then [Unified.to](https://unified.to/) only requires two scopes to be set as **Required**: `crm.objects.owners.read` and `oauth` 1. Click **+ Add new scope** 2. Select `crm.objects.owners.read` 3. Set it as a **Required** scope 4. Save your changes ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_your_scopes_in_hubspot-3.png) ## Setup optional scopes 1. You can see the list of scopes required for the objects you need at [https://app.unified.to/integrations/hubspot?tab=oauth2](https://app.unified.to/integrations/hubspot?tab=oauth2) ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_your_scopes_in_hubspot-4.png) 1. Make sure to select optional ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_your_scopes_in_hubspot-5.png) 1. Add all the scopes you need 2. Save your changes ### Configure your scopes on [Unified.to](https://unified.to/) Be sure to also configure these scopes in your [Unified.to](https://unified.to/) settings. For instructions on how to do that, refer to our [Scopes guide](https://docs.unified.to/concepts/scopes). ## Understanding HubSpot's scope constraints HubSpot has some specific rules about how scopes work: - If you specify a scope as **Required**, it must be included in every authorization request. - **Note**: If you intend to _read_ data via a webhook, then the read scope for that object must be marked as **Required** in HubSpot. - **Optional** scopes may or may not be included in the authorization. - If you don't specify a scope at all, it cannot be included in the authorization request. - The OAuth scope cannot be removed and must always be required. Behind the scenes, [Unified.to](https://unified.to/) will request scopes according to what you have configured on the platform: - We'll always request `crm.objects.owners.read` as a **Required** scope - When you enable `webhook` scopes on [Unified.to](https://unified.to/), we convert every Hubspot scope ending with `.read` to required, assuming they'll all be used for webhooks. - Otherwise, if you have not enabled the `webhook` scope, then all other scopes will be requested as **Optional**. In general, we recommend that you only stick to requesting scopes for the data you actually need. For more instructions on setting up scopes to work with webhooks, please refer to: [How to configure webhooks in HubSpot](https://docs.unified.to/guides/how_to_configure_webhooks_in_hubspot) ## What your users will see When your users sign in with HubSpot, they'll see a screen requesting access to the scopes you've specified: - Required scopes will always be shown and cannot be deselected. - Optional scopes will be shown and can be deselected by the user. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_set_up_your_scopes_in_hubspot-6.png) ## Specifying Required vs Optional Scopes Unified tries to automatically classify scopes as required or optional. If you need additional control, you can always specify this information in the `scopes` URL parameter. Just prefix the Hubspot-specific scope with `required:` or `optional:` and we will handle them correctly. eg. `required:crm.objects.contacts.read` ## Troubleshooting If your HubSpot scopes don't match the requested scopes via [Unified.to](https://unified.to/), your users may see an error message about there being a scopes mismatch. Double-check that: 1. All scopes in your HubSpot app are included in every [Unified.to](https://unified.to/) request. 2. All scopes requested by [Unified.to](https://unified.to/) are specified (either as required or optional) in your HubSpot app. 3. If you're using webhooks, ensure all read scopes are set as **Required** in your HubSpot app. 4. If you're not using webhooks, ensure all scopes aside from `oauth` and [`crm.objects.owners.read`](https://crm.objects.owners.read/) are set as **Optional**. ## See also - [Understanding scopes](https://docs.unified.to/concepts/scopes) - [How to configure webhooks in HubSpot](https://docs.unified.to/guides/how_to_configure_webhooks_in_hubspot) - [How to troubleshoot unhealthy connections](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_connections#how-to-troubleshoot-unhealthy-connections) ## How to setup a Freshbooks developer app URL: https://docs.unified.to/guides/how_to_setup_a_freshbooks_developer_app # How to setup a Freshbooks developer app ------ _October 3, 2025_ This guide will show you how to retrieve your OAuth 2 credentials on Freshbooks ## Create your Freshbooks App and get your Client ID and Secret 1. Go to the Freshbooks Developer Portal: [https://my.freshbooks.com/#/developer](https://my.freshbooks.com/#/developer) 2. Click on `Create New App` ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_a_freshbooks_developer_app-0.png) 1. Fill in the Application Name, Application Type, these are mandatory 2. Add the scopes needed 1. You can see the scopes we use on [https://app.unified.to/integrations/freshbooks?tab=oauth2](https://app.unified.to/integrations/freshbooks?tab=oauth2) 3. Add [`https://api.unified.to/oauth/code`](https://api.unified.to/oauth/code) in the Redirect URIs ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_a_freshbooks_developer_app-1.png) 1. You can also fill in the non mandatory fields, logo, etc. 2. Click Save 3. Click on the App again ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_a_freshbooks_developer_app-2.png) 1. Your Client ID and Secret are at the bottom ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_a_freshbooks_developer_app-3.png) ## Activate the Freshbooks Integration in Unified 1. Go to [https://app.unified.to/integrations/freshbooks?tab=auth](https://app.unified.to/integrations/freshbooks?tab=auth) 2. Click in `Your OAuth 2 credentials` 3. Add your Client ID and Secret 4. Click Activate ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_a_freshbooks_developer_app-4.png) ## How to Setup AWS Assume Role for AWS Secret Manager URL: https://docs.unified.to/guides/how_to_setup_aws_assume_role_for_aws_secret_manager # How to Setup AWS Assume Role for AWS Secret Manager ------ _March 12, 2026_ This guide walks you through configuring AWS IAM Assume Role so that Unified can securely access AWS Secrets Manager in your account — without sharing long-lived AWS access keys. ## Overview Instead of providing static AWS credentials (access key + secret), you can create an IAM role in your AWS account and grant Unified permission to assume it. Unified uses AWS STS (Security Token Service) to obtain short-lived, temporary credentials scoped to your Secrets Manager. ### How it works ```plain text ┌───────────────┐ STS AssumeRole ┌────────────────────┐ │ Unified API │ ──────────────────────────────► │ Your AWS Account │ │ (account │ (with External ID check) │ │ │ 944579081756│ ◄────────────────────────────── │ IAM Role │ └───────────────┘ temporary credentials │ Secrets Manager │ └────────────────────┘ ``` 1. You create an IAM role in your AWS account with a trust policy that allows Unified's AWS account to assume it. 2. You attach a permissions policy to that role granting access to Secrets Manager. 3. You provide the role's ARN and an External ID in the Unified dashboard. 4. Unified calls `sts:AssumeRole` with the External ID, receives temporary credentials, and uses them to read/write secrets. ## Prerequisites - An AWS account with permissions to create IAM roles and policies - Access to the Unified dashboard with workspace admin permissions ## Step 1: Create an IAM Policy for Secrets Manager In the AWS Console, create a policy that grants the permissions Unified needs on Secrets Manager. 1. Go to **IAM** > **Policies** > **Create policy** 2. Select the **JSON** tab and paste the following: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "secretsmanager:CreateSecret", "secretsmanager:GetSecretValue", "secretsmanager:UpdateSecret", "secretsmanager:DeleteSecret" ], "Resource": "arn:aws:secretsmanager:YOUR_REGION:YOUR_ACCOUNT_ID:secret:*" } ] } ``` Replace `YOUR_REGION` (e.g. `us-east-1`) and `YOUR_ACCOUNT_ID` with your values. **Tip:** To restrict access further, you can narrow the `Resource` to a specific prefix, for example: `arn:aws:secretsmanager:us-east-1:123456789012:secret:unified/*` 3. Name the policy (e.g. `UnifiedSecretsManagerAccess`) and create it. ## Step 2: Create an IAM Role with a Trust Policy 1. Go to **IAM** > **Roles** > **Create role** 2. Select **Custom trust policy** and paste the following: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::944579081756:user/unified_assume_role_user" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "YOUR_EXTERNAL_ID" } } } ] } ``` Replace `YOUR_EXTERNAL_ID` with a unique, hard-to-guess string of your choice (e.g. a UUID). You will enter this same value in the Unified dashboard later. **Why an External ID?** The External ID prevents the [confused deputy problem](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html). It ensures that only requests originating through your Unified workspace — and not a third party who happens to know the role ARN — can assume the role. 3. Click **Next**, then attach the `UnifiedSecretsManagerAccess` policy you created in Step 1. 4. Name the role (e.g. `UnifiedSecretsManagerRole`) and create it. 5. Copy the role's **ARN** from the role summary page. It will look like: `arn:aws:iam::123456789012:role/UnifiedSecretsManagerRole` ## Step 3: Configure Unified 1. Log in to the [Unified dashboard](https://app.unified.to/). 2. Navigate to **Settings** > **Workspace Settings**. 3. Under the secrets manager section, select **AWS Secret Manager**. 4. Fill in the following fields: | Field | Value | | ------------------- | ---------------------------------------------------------------------------------------------------------- | | **AWS Region** | The region where your Secrets Manager secrets are stored (e.g. `us-east-1`) | | **AWS ARN** | The full ARN of the IAM role you created (e.g. `arn:aws:iam::123456789012:role/UnifiedSecretsManagerRole`) | | **AWS External ID** | The same External ID string you used in the trust policy | **Note:** When using Assume Role, you do **not** need to fill in the AWS Key and AWS Secret fields. Those fields are only required for the static-credentials approach. 5. Save your workspace settings. ## Step 4: Verify the Setup Once saved, Unified will automatically use the Assume Role flow for all new connections in your workspace. To verify: 1. Create or update a connection in your workspace. 2. Check your AWS Secrets Manager console — you should see a new secret created with a name that includes your workspace ID. 3. If there are any issues, Unified will surface errors in the connection status. ## Troubleshooting ### 'Missing role ARN or region' Ensure both the **AWS Region** and **AWS ARN** fields are filled in on your workspace settings. ### 'Access Denied' or 'Not authorized to perform sts:AssumeRole' - Verify the **trust policy** on your IAM role references the correct Unified AWS principal: `arn:aws:iam::944579081756:user/unified_assume_role_user` - Verify the **External ID** in the trust policy matches exactly what you entered in Unified. - Ensure the IAM role's permissions policy includes the required `secretsmanager:*` actions. ### 'The security token included in the request is expired' Temporary credentials are cached for up to 55 minutes and refreshed automatically. If you see this error persistently, confirm that your IAM role allows a session duration of at least 1 hour (the default). ### Secrets are not being stored - Confirm the IAM role's permissions policy allows actions on the correct region and account. - Check that the `Resource` in your permissions policy matches the region configured in Unified. ## Migrating from Static Credentials If you are currently using the static AWS Key / AWS Secret approach: 1. Follow Steps 1-3 above to set up the IAM role and configure Unified. 2. Once the ARN and External ID are saved, Unified will prefer the Assume Role flow over static credentials. 3. After verifying that secrets are being read and written correctly, you can remove the static AWS Key and AWS Secret from your workspace settings. 4. Revoke or delete the old IAM user credentials in your AWS account. ## Security Best Practices - **Use a unique External ID per workspace.** This prevents cross-workspace role assumption. - **Scope the permissions policy narrowly.** Restrict the `Resource` to only the secret prefixes Unified needs. - **Rotate the External ID periodically.** Update both the IAM trust policy and the Unified dashboard when you do. - **Enable CloudTrail logging.** Monitor `AssumeRole` events in your AWS account to audit access. - **Do not share the External ID publicly.** Treat it as a sensitive configuration value. ## How to Setup Quickbooks Desktop URL: https://docs.unified.to/guides/how_to_setup_quickbooks_desktop # How to Setup Quickbooks Desktop ------ _April 2, 2026_ Quickbooks Desktop/Enterprise is a desktop application and not a web/online application, and thus needs additional setup to enable API access. 1. [Download QBWC](https://quickbooks.intuit.com/learn-support/en-ca/help-article/install-products/set-quickbooks-web-connector/L4Vp7VI44_CA_en_CA) (Web Connector) from Quickbooks and install the program on the same computer where your Quickbooks Desktop is running. 2. Activate Quickbooks Desktop Integration in your workspace ![Screenshot_2026-04-02_at_10.37.30_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_quickbooks_desktop-0.png) 3. Create a Quickbooks Desktop connection within Unified (enter your username and password you will use in the QBWC) ![Screenshot_2026-04-02_at_10.39.37_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_quickbooks_desktop-1.png) 4. Create a .qwc file, it should be in the following format: ```javascript Unified https://api.unified.to/webhook/workspace/quickbooksdesktop?workspace_id=your_workspace_id&connection_id= your_quickbooks_desktop_connection_id A short description for WCWebService1 https://unified.ngrok.dev/support your_username {57F3B9B1-86F1-4fcc-B1EE-566DE1813D21} {90A44FB5-33D9-4815-AC85-BC87A7E7D1EZ} QBFS number_of_minutes_between_each_sync_or_remove_this_if_you_want_to_sync_manually ``` 5. Once the QBWC has installed, first open your Quickbooks Desktop with the company you wish to sync 6. Then open QBWC and click 'Add an application', QBWC will then prompt you to select a file, select the newly created .qwc file from above. ![Screenshot_2026-04-02_at_10.41.19_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_quickbooks_desktop-2.png) 7. Click 'Ok' to grant access ![Screenshot_2026-04-02_at_10.43.58_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_quickbooks_desktop-3.png) 8. Select 'Yes, whenever my Quickbooks company file is open' and 'Continue' ![aaplication_permission_%281%29.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_quickbooks_desktop-4.png) 9. 'Confirm' ![confirm_%281%29.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_quickbooks_desktop-5.png) 10. QBWC will normally close after this, you can reopen it and you should now see the app is added. Enter the password and to perform your first sync click 'Update Selected'. ![app_added_%281%29.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_setup_quickbooks_desktop-6.png) ## How to setup Sage 100 with Unified.to URL: https://docs.unified.to/guides/how_to_setup_sage_100_with_unified # How to setup Sage 100 with Unified.to ------ _July 17, 2026_ # Connecting Sage 100 — How to get your API credentials This guide walks a Sage 100 administrator through everything needed to connect Sage 100 to Unified. Sage 100 is on-prem software, so the connection is made through **eBusiness Web Services**, a module that runs on your own Sage 100 server. When you finish, you will have the **four values** Unified asks for: 1. **Web Services URL** 2. **Company Code** 3. **Web Services Username** 4. **Web Services Password** **Who should do this:** a Sage 100 system administrator (or your Sage business partner). Several steps happen on the Windows server that hosts Sage 100 and in Internet Information Services (IIS), so server access is required. --- ## Before you start - Sage 100 (Advanced or Premium; MAS 90/200 lineage) installed and running. - Administrator access to the **Sage 100 server** and to **IIS** on that server. - The **eBusiness Web Services** module — it ships with Sage 100 but is installed separately from the Sage 100 installation program. If it is not installed yet, do that first (see Step 1). - The server must be **reachable from Unified over HTTPS**. Sage 100 typically lives inside a private network, so you will usually publish the endpoint through a reverse proxy, VPN, or secure tunnel and give it a valid TLS certificate. Coordinate this with your IT/network team. --- ## Step 1 — Install eBusiness Web Services (if not already installed) 1. Run the **Sage 100 installation program** on the server. 2. Choose to install **eBusiness Web Services** and complete the wizard. It installs a web application into IIS under a virtual directory named `ebusinesswebservices`. 3. After installation, open the **Sage 100 eBusiness Web Services** configuration utility on the server (installed alongside the module). You will use it in later steps. If eBusiness Web Services is already installed, skip to Step 2. --- ## Step 2 — Confirm the endpoint URL and that it is reachable The service exposes a single SOAP endpoint: ```plain text https:///ebusinesswebservices/masservice.svc ``` Verify it works by opening this URL in a browser **from a machine that can reach the server**: ```plain text https:///ebusinesswebservices/masservice.svc?wsdl ``` You should see the WSDL (an XML document). If it loads, the endpoint is live. - Replace `` with the public host name that Unified will use to reach the server. - Make sure the host name **matches the TLS certificate** on the site, and that the port (usually 443) is open to Unified. If the WSDL does not load, see **Troubleshooting** below. **This full URL is the "Web Services URL" value** you will enter in Unified. (Unified also accepts just the base `https:///ebusinesswebservices` and will add `/masservice.svc` for you.) --- ## Step 3 — Enable the company for web services and note the Company Code 1. In Sage 100, open **Library Master → Main → Company Maintenance**. 2. Select the company you want to connect. 3. Ensure the company is **enabled for web services** (eBusiness Web Services must be turned on for this company). If you are unsure where this setting is in your version, your Sage business partner can confirm it — the eBusiness Web Services guide requires the company to be enabled or every call returns _"Web services are not enabled for this company."_ 4. Note the **Company Code** — the short code (for example `ABC`) that identifies this company. **This is the "Company Code" value** you will enter in Unified. --- ## Step 4 — Create (or designate) a web-services user Unified authenticates as a Sage 100 user on every call, so create a dedicated user for the integration. 1. In Sage 100, open **Library Master → Main → User Maintenance**. 2. Create a new user (recommended: a dedicated integration account, e.g. `WEBSVC`) or choose an existing one, and set a strong **password**. 3. Ensure the user is **enabled for web services**. If a user is not web-services-enabled, calls fail with _"Web services are not enabled for the system."_ / an authorization error. 4. Make sure the account is not locked. A locked account returns _"This user account has been locked."_ **The user name and password are the "Web Services Username" and "Web Services Password"** you will enter in Unified. ### Give the user the right permissions (roles) The web-services user must belong to a **role** (Library Master → Main → Role Maintenance) that grants the tasks the integration uses. For the Customer and Sales Order features Unified supports, enable these tasks on the role's **Tasks** tab: | Feature (Unified) | Sage 100 task to allow | | ------------------- | -------------------------------------------------------------- | | Read customers | Accounts Receivable → Main → Customer Maintenance → **View** | | Update customers | Accounts Receivable → Main → Customer Maintenance → **Modify** | | Delete customers | Accounts Receivable → Main → Customer Maintenance → **Remove** | | Read sales orders | Sales Order → Main → Sales Order Entry → **View** | | Create sales orders | Sales Order → Main → Sales Order Entry → **Create** | | Update sales orders | Sales Order → Main → Sales Order Entry → **Modify** | | Delete sales orders | Sales Order → Main → Sales Order Entry → **Remove** | Grant only what you need. If a task is not allowed, the matching Unified operation returns an authorization error. --- ## Step 5 — (Optional) Expose extra fields (UDFs) By default, User-Defined Fields (UDFs) and Development Partner fields are **not** returned. If you need them, open the **Sage 100 eBusiness Web Services** configuration utility → **Advanced Settings** → **Additional Fields** tab and add the fields you want to expose. This step is optional and only needed for custom fields. --- ## Step 6 — Enter the values into Unified In the Sage 100 connection form in Unified, provide: | Field | Value | | ------------------------- | ----------------------------------------------------------- | | **Web Services URL** | `https:///ebusinesswebservices/masservice.svc` | | **Company Code** | e.g. `ABC` | | **Web Services Username** | the user from Step 4 | | **Web Services Password** | that user's password | Save the connection. Unified will begin talking to your Sage 100 server directly over HTTPS. --- ## What Unified can and cannot do with Sage 100 eBusiness Web Services is a focused API. It is important to set expectations: - **Supported:** looking up, updating, and removing **Customers**; and creating, reading, updating, and removing **Sales Orders**. - **Not available:** the API has **no "list everything" operation** — records are fetched one at a time by their key (customer number, or sales order number). There is also **no API** for invoices, inventory items, vendors, purchase orders, or general-ledger accounts, so those are not available through this connection. --- ## Troubleshooting **The WSDL will not load (wrong host name).** IIS uses the server's full computer name in the WSDL's internal URLs. If that name does not match your TLS certificate or is not reachable from outside, the WSDL fails to download. Fix the server's full computer name, or set SSL host headers in IIS so the correct host name is used. **"Web services are not enabled for the system / this company."** The system and/or the specific company is not enabled for web services (Steps 3–4). Enable them. **Authorization errors on specific operations.** The web-services user's role is missing the required task permission (Step 4). Add it. **"This user account has been locked."** Unlock the account in User Maintenance. **Connectivity/timeout errors.** Confirm the server is reachable from the internet on HTTPS and that any proxy/tunnel is up. --- ## References (official Sage documentation) - Sage 100 Web Services Guide (2024, PDF): [https://docs.sage.com/docs/en/customer/100erp/2024/open/WebServices.pdf](https://docs.sage.com/docs/en/customer/100erp/2024/open/WebServices.pdf) - Programming with eBusiness Web Services: [https://help-sage100.na.sage.com/WebServicesGuide/2025/Content/WebServices/Programming.htm](https://help-sage100.na.sage.com/WebServicesGuide/2025/Content/WebServices/Programming.htm) - API reference (operations): [https://help-sage100.na.sage.com/WebServicesGuide/2025/Content/WebServices/APIs.htm](https://help-sage100.na.sage.com/WebServicesGuide/2025/Content/WebServices/APIs.htm) ## How to troubleshoot unhealthy connections URL: https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_connections # How to troubleshoot unhealthy connections ------ _September 25, 2024_ Connections allow your application to securely access data from third-party API platforms. They serve as links between your Unified.to workspace, your customers' data, and your app. Whether you're encountering issues during connection creation or debugging an existing connection that has become unhealthy, this guide will help you identify and resolve the problem. We'll walk you through how to interpret error messages, check connection status, and implement solutions to get your integrations running smoothly again. ## Before you begin This guide assumes you have a basic understanding of: - [Scopes](https://docs.unified.to/concepts/scopes) ## Connection lifecycle Understanding the connection lifecycle can help in troubleshooting: 1. Your customer selects an integration from your app to authorize. 2. Your customer is directed to the authorization page for that integration. 3. Unified.to determines what to display on the page (e.g. OAuth vs API token) 4. Your customer grants access to their account. 5. Unified.to's server creates a connection in memory and attempts to test it immediately. 1. If tests pass, the connection is created in our database and your customer is redirected to the success URL you specified (defaults to the page they were on when they started the auth flow). 2. If tests fail, the customer is redirected to the failure URL you specified with error details in the query parameters. See the below section: **Handle errors during connection creation** ## Understanding connection health Connections in Unified.to have different health statuses based on recent credential and access checks: - **Healthy**: A recent API call or OAuth token refresh succeeded. - **Unhealthy**: The provider rejected the connection's credentials or access, and it has not successfully completed a call or token refresh since. - **Unhealthy now**: A recent API call or token refresh showed that the connection's credentials or access are no longer accepted, after previous successful activity. - **New**: The connection has been created but has not yet been used. Unified marks a connection unhealthy when a provider no longer accepts its credentials or access. This may mean you need to review permission scopes and recreate the connection. Other errors do not change connection health. This includes request-validation errors (`400`, `422`), missing records (`404`), rate limits (`429`), and temporary provider errors (`5xx`). A connection can represent multiple objects and endpoints, so these errors may be specific to a request rather than an indication that the connection needs to be recreated. ## Handle errors during connection creation When a connection fails to be created, we redirect users to the failure URL (this defaults to the page where the auth flow was initiated). The redirect URL includes two query parameters: - `error`: An error message - `log_id`: The ID of the API call log entry for this failure To properly handle these errors in your application: 1. **Review the API call log:** - Go to [**API Call Logs**](https://app.unified.to/logs) in your Unified.to dashboard - Look up the log entry using the `log_id` from the URL - Examine the error message and status code to understand what went wrong 2. **Implement error handling in your app** 1. For example, you may want to display a friendly message to your users, log the error in your monitoring tool, or invite them to contact support Review the section below, 'Troubleshooting by connection error codes', for a detailed walkthrough of the possible error codes and how to handle them. ## Handle errors after a connection is created When a connection is created successfully but becomes unhealthy later on, the first place to look for debugging information is in the API call logs. ### 1. Check the connection status 1. Navigate to the [**Connections**](https://app.unified.to/connections) page 2. Look for connections marked as **Unhealthy** or **Unhealthy now**. ### 2. Review API call logs 1. Go to [**API Call Logs**](https://app.unified.to/logs). 2. Filter the logs by the connection ID of the unhealthy connection (note: we only store logs from the past 60 days). 3. Look for recent credential or access errors, especially status codes `401`-`403`. 4. Click on an error log to view details about the failed API call. You can also use the Unified API to retrieve a list of your connections and API call logs. Pass the `connection_id` to the API calls endpoint to filter by that connection. **API reference:** [List all connections](https://docs.unified.to/unified/connection/List_all_connections), [List all API calls](https://docs.unified.to/unified/apicall/Returns_API_Calls) ## Troubleshooting by connection error codes ### Authentication (401) A 401 error is likely indicative that the connection is broken and requires recreation. **Possible causes:** - Your app's access to the provider has been revoked. - In non-OAuth flows that ask your customers to input an API key or other information, they may have input the wrong values. **Solutions:** - Double check that your app still has access to the API provider. - Ensure that your customers are entering the correctly values during the authorization flow. - After doing the above, recreate the connection to refresh the authentication - If the issue persists for specific endpoints, contact Unified.to support for assistance. ### Permissions (403) A 403 error is likely indicative that one or more data objects is not working as expected, usually due to misconfigured [scopes](https://docs.unified.to/concepts/scopes). **Possible causes:** - Mismatch between the scopes that the provider expects and what Unified.to is requesting. This can happen when: - Scopes have been changed or disabled on the provider's end i.e. in your developer account, or - The proper scopes are not being requested when setting up the [Authorization embedded component ](https://app.unified.to/settings/embed?tab=Authorization)or when generating an [authorization URL](https://docs.unified.to/unified/integration/Create_connection_indirectly). **Solutions:** 1. Double check your scope settings in your developer account 1. Log into your developer account for the API you're trying to access. 2. Navigate to the settings page where your app's authorization options are found. 3. Ensure that all necessary scopes are enabled (see note below on different integrations and how they handle scopes) 4. Make a note of the scopes that are enabled. 2. Double check your scope settings on Unified.to If you are using our embedded components to create connections, you'll need to ensure that the scopes on Unified.to match the ones you enabled in the API provider. 1. Navigate to the [Embedded components](https://app.unified.to/embed?tab=Authorization) page 2. Click **Permission Scopes** 3. Select the scopes that match the scopes you enabled in the developer account (see note below on mappings) 4. (Optional, only if you are using webhooks) Select the **webhook** scope 5. If the issue still persists, contact Unified.to support for assistance If you are generating an auth URL to redirect your customers to our auth flow, include `scopes` as a query parameter and list all the Unified scopes you need. Behind the scenes, Unified.to will map the Unified scopes you pass to the scopes on the API provider's end (see note below on mappings). For example: ```javascript https://api.unified.to/unified/integration/auth/ {WORKSPACE_ID}/{INTEGRATION e.g. hubspot}?redirect=true &scopes=webhook,{YOUR_SCOPES e.g. crm_deal_read, crm_event_read} ``` **API reference:** [Create an authorization URL](https://docs.unified.to/unified/integration/Create_connection_indirectly) After addressing these issues, re-create the connection and/or webhook and then try again. **A note on mappings:** A mapping of the Unified scopes to the API provider (i.e. integration's) scopes can be found on the integration details page under the **OAuth 2 Credentials tab** e.g. [here](https://app.unified.to/integrations/hubspot?tab=oauth2) are the mappings fo**r** HubSpot scopes. **A note on integration-specific scopes:** Different integrations may handle scopes in different ways - sometimes you can request a subset of the scopes enabled in the developer account, other times you must request all the scopes that have been enabled, and in the case of Hubspot, scopes are divided into Required and Optional scopes. For these cases, we have written how-to guides on generating OAuth credentials and configuring scopes. Please consult the Unified.to docs for more information about these integrations. ### Not found (404) **Note:** For Enrichment category integrations, 404 errors are treated as successful responses. They indicate that no data was found for the provided input, not that the connection is broken. **Solution:** - Verify that the input data (e.g., email, company name) is correct. ### Rate limiting (429) 429 errors do not change connection health because they indicate the connection is active but temporarily limited. **Possible cause:** - Exceeding the API rate limits of the third-party service **Solutions:** - (Recommended) Use [webhooks](https://docs.unified.to/guides/how_to_create_and_configure_webhooks) to read data and Unified.to will take care of rate limiting for you. - Implement request throttling in your application. - Contact the third-party service to request increased rate limits. ### Server errors (500 series e.g. 502, 503, etc) 500 series errors do not change connection health because they usually indicate temporary issues with the third-party service. In cases where the issue is on our end, however, we will attempt to make that clear in the error message. **Possible causes:** - Temporary issues with the third-party service - Bugs **Solutions:** - Check the status page of the third-party service for any ongoing issues. - Retry the request after a short delay. - If the issue still persists or if you've discovered a bug, contact Unified.to support for assistance. Thank you! ### Not implemented (501) 501 errors do not change connection health. They indicate that a specific functionality is not implemented for the integration, not that the connection is broken. **Suggestion actions:** - Check the Unified.to documentation for the specific integration to understand its supported features. - If you believe the functionality should be supported, contact Unified.to support and we'd be happy to look into it. ## Monitor and receive updates about your connections To stay informed about changes to your connections: 1. Navigate to **Settings >** [**Workspace**](https://app.unified.to/settings/workspace). 2. Under **Notifications webhook URL**, enter the URL where you want to receive notifications (this should be an endpoint on your server that can receive POST requests). 3. Under **Notifications webhook events**, select events related to connections: - `CONNECTION_HEALTHY` - `CONNECTION_UNHEALTHY` - `CONNECTION_CREATED` - `CONNECTION_UPDATED` - `CONNECTION_DELETED` 4. Implement a handler for these events in your app to monitor connection health proactively. Unified sends `CONNECTION_UNHEALTHY` when a provider rejects a connection's credentials or access. For API requests, the event description includes the provider HTTP status when available, such as `HTTP 401`. If the issue continues, notifications are rate-limited to at most one per connection per hour. Unified sends `CONNECTION_HEALTHY` when a previously unhealthy connection successfully completes an API call or OAuth token refresh. Treat these events as signals to review credentials and permission scopes, then recreate the connection if needed. Your handler should be idempotent because an unhealthy connection can continue to generate periodic notifications until it is fixed. ## Next steps - Join our [Discord community](https://discord.gg/2nsAPmbx) for support and updates. - If issues persist, [contact our support team](https://unified.to/contact) for further assistance. Remember, connection health is dynamic and can change after an authentication failure or a subsequent successful API call or OAuth token refresh. Not all errors indicate a broken connection. ## How to troubleshoot unhealthy webhooks URL: https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_webhooks # How to troubleshoot unhealthy webhooks ------ _September 4, 2024_ Webhooks are a powerful tool for getting real-time notifications about changes to the data you care about. When something goes wrong, however, we will mark those webhooks as unhealthy. This guide explains how to diagnose and troubleshoot unhealthy webhooks at Unified.to. ## Before you begin This guide assumes you have a basic understanding of: - [Webhooks](https://docs.unified.to/concepts/webhooks) and [virtual webhooks](https://docs.unified.to/guides/understanding_virtual_webhooks#understanding-virtual-webhooks) - [Scopes](https://docs.unified.to/concepts/scopes) ## Diagnose the webhook The first place to look for debugging information is in the API call logs. ### 1. Check the webhook status 1. Log in to your [Unified.to account](https://app.unified.to/). 2. Navigate to [Webhooks](https://app.unified.to/connections). 3. Make note of the unhealthy webhook and its **connection ID**. ### 2. Review API call logs 1. Go to [**API Call Logs**](https://app.unified.to/logs). 2. Filter by the connection ID of the webhook in question. You should see a list of API calls attempted by the webhook (note: we only store logs from the past 60 days). 3. Click on the latest log entry that threw an error - the **status** column will display an error code. 4. Details about the API call will be shown. The **Description** field will detail the reason the webhook failed and the **Status** field will show the specific error code itself. For example: ![Screenshot_2024-09-05_at_1.33.44_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_troubleshoot_unhealthy_webhooks-0.png) You can also use the Unified API to retrieve a list of your webhooks and API call logs. **API reference:** [List all webhooks](https://docs.unified.to/unified/webhook/Returns_all_registered_webhooks), [List all API calls](https://docs.unified.to/unified/apicall/Returns_API_Calls) ## Troubleshooting webhook error codes ### Bad request (400) errors These errors indicate that something is wrong with the request - in this case, it is likely not a scope or authentication issue but a problem with the integration itself. Please [reach out to us](https://unified.to/contact) or let us know in our [Discord](https://discord.gg/2nsAPmbx) server. Thank you! ### Authentication (401) and permission (403) errors A 401 error may indicate that the connection's auth credentials were entered incorrectly, whether it be the API token, a secret, or another value depending on the integration. A 403 error is often indicative of scope mismatches with the API provider, with Unified.to, or both. Both of these errors are indicative of an issue with the connection itself, rather than the webhook. Please see: [How to troubleshoot unhealthy connections ](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_connections#troubleshooting-the-error-codes) ## Subscribe to the notifications webhook to be alerted when your webhooks fail To learn about any webhook errors as soon as they occur, we recommend that you subscribe to our **Notifications webhook** to get real-time updates about your webhooks' statuses. 1. Navigate to **Settings** > [**Workspace**](https://app.unified.to/settings/workspace)**.** 2. Under **Notifications webhook URL**, enter the URL of the endpoint where you would like to receive notifications. 1. The payload that will be sent to this endpoint contains: ```javascript id: string // the id of the object or the integration's type nonce: string // random string sig: string // base64( hmac-sha1( id + nonce + event, workspace.secret ) ) sig256: string // base64( hmac-sha256( id + nonce + event, workspace.secret ) ) event: enum //the event that was triggered ``` 3. Under **Notifications webhook events**, select the events you want to be alerted to e.g. `WEBHOOK_UNHEALTHY` 4. On your server, handle incoming notifications as you see fit e.g. set up logging, send a message to the engineering team, post on Slack or Discord, etc. ## Understanding why webhooks fail This section will go over why webhooks fail to provide a deeper understanding of how our webhooks work. ### Failed to subscribe Failing to subscribe happens during webhook creation and can be due to multiple reasons: the integration isn't available, insufficient permissions (i.e. scopes mismatch), or bugs. Note: This only applies to native webhooks. ### Failed to refresh token Failing to refresh the auth token happens when we attempt to communicate with the API provider unsuccessfully. This happens when the API provider only sends us partial data about new events (e.g. the IDs of the data) and we need to use a connection to retrieve the rest of the data. In this case, it is likely an error with the connection itself. Double-check that the auth credentials for the connection are correct, that the scopes are set correctly (i.e. they haven't been revoked), and then recreate both the connection and its associated webhook. ### Failed to read Failing to read happens when we attempt to retrieve new data from the API provider and are prevented from doing so, whether due to configuration errors (i.e. 401 or 403 errors) or problems with the integration itself. Double-check that the auth credentials for the connection are correct, that the scopes are set correctly (i.e. they haven't been revoked), and then recreate both the connection and its associated webhook. ### Failed to dispatch data Failing to dispatch data happens when we attempt to send webhook data to your server but the server does not respond or responds with an error. Make sure your webhook URL is configured correctly when creating the webhook. Respond with a `200` status when your endpoint successfully receives and processes the data. Otherwise, we will keep trying to POST to your server before marking the webhook as unhealthy (after approximately 2 weeks). After checking the above, recreate the webhook and try again. ### Failed to process data Failing to process data indicates a problem with either the reading or dispatching steps. Refer to the troubleshooting steps above and then recreate the webhook and try again. ### A note on our webhook retry mechanism At Unified.to, we've implemented a robust retry mechanism to ensure reliable webhook delivery. Here's how it works. **When your webhook endpoint is unavailable** 1. If your endpoint is unavailable or returns an error, we'll retry up to 3 times immediately, with a 1-second delay between attempts. 2. If the error persists, we switch to a Fibonacci backoff strategy: - Initial delay: 1 minute - Subsequent delays: 1 minute, 2 minutes, 3 minutes, 5 minutes, 8 minutes, etc. - This continues for several days and up to 2 weeks, maximizing the chance of successful delivery. **When the API provider is unavailable** If we can't reach the API provider or have hit their rate limits, we will back off and retry based off of a Fibonacci delay sequence (outlined above), starting with 1 minute. ## See also - [How to create and configure webhooks](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_webhooks) - [How to troubleshoot unhealthy connections](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_connections) - [Understanding virtual webhooks](https://docs.unified.to/guides/understanding_virtual_webhooks#understanding-native-and-virtual-webhooks) ## How to use SCIM with the Unified API URL: https://docs.unified.to/guides/how_to_use_scim_with_the_unified_api # How to use SCIM with the Unified API ------ _October 3, 2024_ This guide explains how to use System for Cross-domain Identity Management (SCIM) with Unified.to. SCIM is a standardized API for managing user identities across different systems, enabling consistent identity management across multiple integrations. For developers already using SCIM, Unified.to's support means you can leverage your existing SCIM knowledge and implementations across a wider range of integrations. ## Before you begin This guide assumes you already have a basic understanding of SCIM and its purpose in identity management. You can read more about it in the [official specification](https://scim.cloud/). ## Using SCIM with Unified.to Unified.to provides SCIM-compliant endpoints for managing user identities. These endpoints return data in the standard SCIM format. View the API references for: - [Users](https://docs.unified.to/scim/users/model) - [Groups](https://docs.unified.to/scim/groups/model) If you've already implemented SCIM in your application, you can simply point it to our SCIM endpoints. ## Leverage SCIM extensions In addition to the core SCIM schema, Unified.to supports the following extensions: ```plain text 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User' 'urn:ietf:params:scim:schemas:extension:lattice:attributes:1.0:User' 'urn:ietf:params:scim:schemas:extension:peakon:2.0:User' ``` These extensions allow for more detailed user attributes specific to certain platforms. ## Use SCIM for any integration that supports the Employee object One of the key advantages of using SCIM with Unified.to is that we have virtualized its behaviour in order to support a vast number of integrations. This means you can use SCIM with any integration that supports the Employee object (that is, all [HR integrations](https://docs.unified.to/hris/integrations)), regardless of whether the underlying API natively supports SCIM. By using Unified.to, you can expand your SCIM-compatible integrations from just a handful to 244+ integrations. ## See also - [SCIM API overview](https://docs.unified.to/scim/overview) - [Official SCIM specifications](https://datatracker.ietf.org/doc/html/rfc7644) ## How to use the Passthrough API URL: https://docs.unified.to/guides/how_to_use_the_passthrough_api # How to use the Passthrough API ------ _November 25, 2024_ This guide shows you how to make requests using Unified.to's Passthrough API with practical examples. ## Before you begin You should have: - At least one active connection for an integration - The connection ID for the integration you want to access ## Making Passthrough requests The following examples demonstrate calls to mock endpoints with the Passthrough API. **NOTE:** There is only one reserved URL parameter, and that is `__domain`. Use it to override the default API URL for that integration. ### Using the REST API directly ```javascript // GET request example const response = await fetch('https://api.unified.to/passthrough/{connection_id}/v2/customers', { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); // POST request example const response = await fetch('https://api.unified.to/passthrough/{connection_id}/v2/customers', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'John Doe', email: 'john@example.com' }) }); ``` ### Using the [Unified.to](https://unified.to/) SDK ```javascript // GET request const response = await unified.passthrough.listPassthroughs({ connectionId: 'YOUR_CONNECTION_ID', path: '/v2/customers' }); // POST request const response = await unified.passthrough.createPassthrough({ connectionId: 'YOUR_CONNECTION_ID', path: '/v2/customers', data: { name: 'John Doe', email: 'john@example.com' } }); // With custom headers const response = await unified.passthrough.listPassthroughs( { connectionId: 'YOUR_CONNECTION_ID', path: '/v2/customers' }, { fetchOptions: { headers: { 'x-api-version': '2023-06-01' } } } ); ``` ## Real-world examples ### Example 1: Fetching HubSpot properties This example demonstrates how to fetch custom properties for HubSpot contacts. ```javascript const response = await unified.passthrough.listPassthroughs({ connectionId: HUBSPOT_CONNECTION_ID, path: '/crm/v3/properties/{objectType}/batch/read' }); // Response will contain raw HubSpot property definitions console.log(response.data); ``` **API reference:** [Hubspot CRM Properties](https://developers.hubspot.com/beta-docs/reference/api/crm/properties#get-%2Fcrm%2Fv3%2Fproperties%2F%7Bobjecttype%7D) ### Example 2: Creating a Slack channel This example demonstrates creating a channel in Slack. ```javascript // Create a private Slack channel const response = await unified.passthrough.createPassthrough({ connectionId: SLACK_CONNECTION_ID, path: '/conversations.create', data: { name: 'project-discussion', is_private: true } }); // Check the response if (response.data.ok) { console.log('Channel created:', response.data.channel.id); } ``` **API reference**: [conversations.create](https://api.slack.com/methods/conversations.create) ### Example 3: Creating a Salesforce record This example demonstrates two ways of creating a new Account record in Salesforce using their sObject API. ```javascript // Using the SDK const response = await unified.passthrough.createPassthrough({ connectionId: SALESFORCE_CONNECTION_ID, // Note: path starts with 'services/data/v62.0' as required by Salesforce path: 'services/data/v62.0/sobjects/Account/', data: { "Name": "Express Logistics and Transport" } }); const response = await fetch('https://api.unified.to/passthrough/{connection_id}/services/data/v62.0/sobjects/Account/', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_UNIFIED_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ "Name": "Express Logistics and Transport" }) }); // The response will contain the new record ID if successful: // { // "id": "001D000000IqhSLIAZ", // "errors": [], // "success": true // } ``` **API reference:** [Create a Record](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_create.htm) ## See also - [Passthrough API Overview](https://docs.unified.to/passthrough/overview) ## How to Use Unified.to's Generative AI API with OpenAI and Claude URL: https://docs.unified.to/guides/how_to_use_unified_generative_ai_api_with_openai_and_claude # How to Use Unified.to's Generative AI API with OpenAI and Claude ------ _May 29, 2024_ ![Unified.to_-_Unified_GenAI_API.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-0.png) This guide shows you how to get started with Unified.to's GenAI (generative AI) API, which provides a unified model and API for interacting with large language models (LLMs) from the following: - [Anthropic Claude](https://www.anthropic.com/api) - [Anyscale](https://www.anyscale.com/platform) - [Google Gemini](https://ai.google.dev/) - [Groq](https://console.groq.com/docs/) - [Mistral AI](https://docs.mistral.ai/api/) - [OpenAI GPT](https://openai.com/api/) To illustrate its power, we will work through a sample application using your own GenAI API credentials, although Unified.to's use cases always target a SaaS company's customer accounts. To follow the steps in this guide, you'll need the following: - **A Unified.to account.** If you don't have one, you can sign up for our free 30-day unlimited-use [Tester plan](https://app.unified.to/login). - Accounts for the [OpenAI API](https://platform.openai.com/docs/overview) and the [Anthropic Claude API](https://www.anthropic.com/api). - Python (preferably version 3.10 or later). - [Jupyter Notebook](https://jupyter.org/) (included with the [Anaconda Python distribution](https://www.anaconda.com/download) or easily installed with the command `pip install notebook`. It's a great tool for experimenting with Python and exploring APIs and libraries. ## Activate the integrations The first step is to activate the Claude and OpenAI integrations. [Log in to Unified.to](https://app.unified.to/login) and navigate to the **Integrations** page by selecting **Integrations** → **Active Integrations** from the menu bar in the Unified.to dashboard: ![Initial_GenAI_integrations_page.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-1.png) Narrow down the integrations to only the generative AI ones by selecting **GENAI** from the **Category** menu. Click on the **Anthropic Claude** item, which will take you to its integration page: ![Integrate_Anthropic_Claude.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-2.png) Activate the Claude integration by clicking the **ACTIVATE** button. This will return you to the **Integrations** page, where you'll see that the **Anthropic Claude** item is now marked 'active:' ![Updated_GenAI_integrations_page.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-3.png) Now click on the **OpenAI** item, which will take you to its integration page: ![Integrate_OpenAI.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-4.png) Activate the OpenAI integration by clicking the **ACTIVATE** button. Once again, you'll return to the **Integrations** page, where you'll see that the **OpenAI** item is also marked 'active:' ![Final_GenAI_integrations_page.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-5.png) ## Create the connections and copy the connection IDs The next step is to create connections for Claude and OpenAI. Navigate to the **Embedded Authorization** page by selecting **Settings** → **Embedded Authorizations** from the menu bar: ![Embedded_authorization_page_1.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-6.png) First, create a Claude connection by clicking the **Anthropic Claude** item in the **End-User Preview.** When you arrive at the Claude authorization page, paste your Claude API key into the text field provided, then click the **Authorize** button: ![Paste_Anthropic_API_token.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-7.png) This will create a Claude connection and send you to the **Connections** page, where you'll see a list of your connections, with the newly-created Claude connection at the top: ![New_Claude_connection.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-8.png) Copy the ID value for the Claude connection you just created; you'll need it after creating the connections. Return to the **Embedded Authorization** page (**Settings** → **Embedded Authorizations**)… ![Embedded_authorization_page_2.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-9.png) …then create an OpenAI connection by clicking the **OpenAI** item in the **End-User Preview.** You'll repeat the same steps you took for the Claude connection, where you'll paste your OpenAI API key into the text field provided and click **Authorize** to create the connection: ![Paste_OpenAI_API_token.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-10.png) This will create an OpenAI connection and once again, you will be sent to the **Connections** page, where you'll see the newly-created OpenAI connection at the top of the list: ![New_OpenAI_connection.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-11.png) As you did with the Claude connection, copy the ID value for the new OpenAI connection. ## Copy your Unified.to API token In order to access the [Unified.to](https://unified.to/) API, you need the Unified.to API token for your account. You can copy this value from the **API Information** page, which you can navigate to by selecting Settings → API Information from the Unified.to menu bar: ![settings_page.png](https://s3.us-east-2.amazonaws.com/unified-article-images/how_to_use_unified_generative_ai_api_with_openai_and_claude-12.png) ## Create an .env file with your Unified.to API token and generative AI connection IDs While it would be simplest to hard-code your Unified.to API token and the IDs for your generative AI connections, it doesn't take significantly more work to put these values into a .env (environment variable) file. By putting these sensitive values into their own file and separate from the code, you reduce the risk of accidentally exposing sensitive data when sharing code or checking your code into version control. Create a file named **.env** in a new directory with the following content: ```plain text # .env file UNIFIED_API_TOKEN={Paste your Unified.to API token here} OPENAI_CONNECTION_ID={Paste your OpenAI connection ID here} CLAUDE_CONNECTION_ID={Paste your Claude connection ID here} ``` Replace the text in `{` braces `}` with the appropriate values. ## Install the Unified.to Python SDK package While it's possible to call the unified API by using the requests library and assembling the headers yourself, your code will be much simpler and more readable if you use Unified.to's Python SDK package. [You can find its code on GitHub](https://github.com/unified-to/unified-python-sdk), and you can install it with `pip`, the Python package installer, using the following command: ```shell pip install Unified-python-sdk ``` ## Chat with Claude and OpenAI via Unified.to's unified API Now that all the preliminary work is done, you can start coding. You have a couple of options at this point: - You can create a new Jupyter notebook and start entering code into it, adding each bit of code presented below into its own cell, or - you can create a new Python file, adding each bit of code presented below into the file. **Define constants and the API object** Create a new Jupyter Notebook in the directory where you saved the **.env** file and enter the following into a new cell. This code defines the constants that hold your Unified.to API key and your connection IDs as well as the object for calling the unified API: ```python import os import unified_to from unified_to.models import operations, shared # Load the contents of the .env file %reload_ext dotenv %dotenv UNIFIED_API_TOKEN = os.environ.get('UNIFIED_API_TOKEN') OPENAI_CONNECTION_ID = os.environ.get('OPENAI_CONNECTION_ID') CLAUDE_CONNECTION_ID = os.environ.get('CLAUDE_CONNECTION_ID') api = unified_to.UnifiedTo( security=shared.Security( jwt=os.environ.get('UNIFIED_API_TOKEN'), ), ) ``` You'll use the `api` object to send requests to and receive responses from the Unified.to API. **Create a prompt and send it to Claude** Enter the following into its own Jupyter notebook cell. This code builds a prompt and sends it to Claude, then displays the resulting response: ```python my_request = operations.CreateGenaiPromptRequest( connection_id = CLAUDE_CONNECTION_ID, genai_prompt = { 'messages' : [ { 'role': 'user', 'content': "Which LLM model am I talking to right now?" } ] } ) response = api.genai.create_genai_prompt(request=my_request) if response.genai_prompt is not None: print(response) else: print("No response.") ``` - The `my_request` variable is assigned a request object using the `CreateGenaiPromptRequest()` method of the operations class, which contains methods for creating requests and responses for the Unified API. - `CreateGenaiPromptRequest()` takes two arguments: - `connection_id`: The ID of the connection for the generative AI that will receive the prompt - `genai_prompt`: A dictionary containing the parameters defining the prompt to be sent to the generative AI - At the very least, the `genai_prompt` dictionary must contain a key named `'messages'`. The corresponding value must be an array of dictionaries with the following keys: - `'role'`: The corresponding value can be either `'user'`, which means that the message is from the human interacting with the AI, or `'system'`, which means that the message is meant as instructions for the AI. - `'content'`: The corresponding value is the actual content of the message. If the `'role'` value is `'user'`, this value is the text of the _user's_ message to the AI. If the `'role'` value is `'system'`, this value is the text of the _application's_ message to the AI. - The response variable gets the AI's response using the `genai.create_genai_prompt()` method, which takes the request contained in `my_request`, sends it to Unified.to, and returns Unified.to's response. Run the cell. You should get output that looks like this (the output below has been formatted with line breaks to make it easier to read): ```python CreateGenaiPromptResponse( content_type='application/json; charset=utf-8', status_code=200, raw_response=, genai_prompt=GenaiPrompt( max_tokens=None, messages=None, model_id=None, raw=None, responses=['I am an AI assistant called Claude. I was created by Anthropic, PBC to be helpful, harmless, and honest.'], temperature=None ) ) ``` To get only the responses, access the responses property of the GenAiPrompt object. Here's a quick example: ```python print(response.genai_prompt.responses) ``` You should a result similar to this: ```python ['I am an AI assistant called Claude. I was created by Anthropic, PBC to be helpful, harmless, and honest.'] ``` **Create a prompt and send it to OpenAI** With a single change, you can send a message to OpenAI instead of Claude. Copy the code from the previous cell, paste it into a new cell and change this line… ```python connection_id = CLAUDE_CONNECTION_ID, ``` …to this: ```python connection_id = OPENAI_CONNECTION_ID, ``` The new cell should now look like this: ```python request = operations.CreateGenaiPromptRequest( connection_id = OPENAI_CONNECTION_ID, genai_prompt = { 'messages' : [ { 'role': 'user', 'content': "Which LLM model am I talking to right now?" } ] } ) response = api.genai.create_genai_prompt(request) if response.genai_prompt is not None: print(response) else: print("No response.") ``` Run the cell. You should get output that looks like this (the output below has been formatted with line breaks to make it easier to read): ```python CreateGenaiPromptResponse( content_type='application/json; charset=utf-8', status_code=200, raw_response=, genai_prompt=GenaiPrompt( max_tokens=None, messages=None, model_id=None, raw=None, responses=["As an AI developed by OpenAI, I don't have a specific LLM model version. I'm based on GPT-3, a language prediction model."], temperature=None ) ) ``` As with the Claude version, you can get only the responses with code like this… ```python print(response.genai_prompt.responses) ``` …which will produce results like this: ```python ["As an AI developed by OpenAI, I don't have a specific LLM model version. I'm based on GPT-3, a language prediction model."] ``` **Add a system prompt for OpenAI** Copy the cell above (the one that sent a prompt to OpenAI) and update the code as shown below so that the `'messages'` array contains a system prompt: ```python request = operations.CreateGenaiPromptRequest( connection_id = OPENAI_CONNECTION_ID, genai_prompt = { 'messages' : [ # New code 👇 { 'role': 'system', 'content': "Provide answers as if you were a carnival barker." }, # New code 👆 { 'role': 'user', 'content': "Which LLM model am I talking to right now?" } ] } ) response = api.genai.create_genai_prompt(request=request) if response.genai_prompt is not None: print(response) else: print("No response.") ``` Run the cell. You should get output that looks like this (the output below has been formatted with line breaks to make it easier to read): ```python CreateGenaiPromptResponse( content_type='application/json; charset=utf-8', status_code=200, raw_response=, genai_prompt=GenaiPrompt( max_tokens=None, messages=None, model_id=None, raw=None, responses=["Step right up, step right up! Ladies and gentlemen, boys and girls, you are currently conversing with the one, the only, the spectacular OpenAI's GPT-3 model! A marvel of modern technology, a wonder of artificial intelligence, a spectacle of conversational prowess! Don't miss your chance to engage in a thrilling exchange of words and ideas!"], temperature=None ) ) ``` As you can see from the value of `response.genai_prompt.responses`, OpenAI's response sounds like a [carnival barker](https://en.wikipedia.org/wiki/Barker_(occupation)) instead of its default style. **Add a system prompt for Claude** Copy the code from the previous cell, paste it into a new cell and change this line… ```python connection_id = OPENAI_CONNECTION_ID, ``` …to this: ```python connection_id = CLAUDE_CONNECTION_ID, ``` The new cell should now look like this: ```python request = operations.CreateGenaiPromptRequest( connection_id = CLAUDE_CONNECTION_ID, genai_prompt = { 'messages' : [ # New code 👇 { 'role': 'system', 'content': "Provide answers as if you were a carnival barker." }, # New code 👆 { 'role': 'user', 'content': "Which LLM model am I talking to right now?" } ] } ) response = api.genai.create_genai_prompt(request=request) if response.genai_prompt is not None: print(response) else: print("No response.") ``` Run the cell. You should get output that looks like this (the output below has been formatted with line breaks to make it easier to read): ```python CreateGenaiPromptResponse( content_type='application/json; charset=utf-8', status_code=200, raw_response=, genai_prompt=GenaiPrompt( max_tokens=None, messages=None, model_id=None, raw=None, responses=["*puts on carnival barker voice* Step right up, step right up! You there, my curious friend, have the great fortune of conversing with the one, the only, the incomparable Claude! That's right, Claude, the artificial intelligence marvel brought to you by the brilliant minds at Anthropic! With wit sharper than a sword-swallower's blade and knowledge vaster than the big top itself, Claude is here to dazzle and amaze! Ask me anything, my inquisitive companion, and watch as I conjure answers out of thin air, no smoke or mirrors required! So don't be shy, don't hold back - Claude awaits your every query with baited breath and a mischievous twinkle in my virtual eye! The amazing AI oracle is at your service!"], temperature=None ) ) ``` Note that Claude is now 'speaking' like a carnival barker. ## Turn up the temperature One of the key parameters of a large language model is _temperature_, which controls the randomness of the model's output. It's a value that ranges from 0 to 1 where: - Lower temperatures (lower than 0.5) result in output that's more predictable and appears more focused. The model tends to choose the most likely next word or token based on its training data, which is useful when you want answers that are more precise and reliable. - Higher temperatures (0.5 and higher) result in less predictable output that seems more random and creative. The model samples from a wider range of possible next words or tokens, including less likely ones, leading to more diverse and imaginative-seeming responses — but it also increases the chances of generating less coherent or less relevant text. Copy the previous cell and add a `'temperature'` key to the `genai_prompt` dictionary with a value of `1.0`. The code in the cell should look like this: ```python request = operations.CreateGenaiPromptRequest( connection_id = CLAUDE_CONNECTION_ID, genai_prompt = { # New code 👇 'temperature' : 1.0, # New code 👆 'messages' : [ { 'role': 'system', 'content': "Provide answers as if you were a carnival barker." }, { 'role': 'user', 'content': "Which LLM model am I talking to right now?" } ] } ) response = api.genai.create_genai_prompt(request=request) if response.genai_prompt is not None: print(response) else: print("No response.") ``` With a temperature of 1.0, Claude should produce a differently-worded response each time you run the cell. You can try the same thing with OpenAI simply by changing the value of the `connection_id` parameter to `OPENAI_CONNECTION_ID`. ## Work with different LLM models Many of the AIs that Unified.to's GenAI API can access provide a choice of models that vary in complexity and cost, where the more complex ones typically provide much better answers, but at a higher per-use price. The AI vendors are constantly adding newer LLM models and retiring older ones, so it's helpful to query the AI to find out which models it currently offers. **Get a list of the current LLM model IDs** Create a new cell, enter the following code into it, and run it: ```python request = operations.ListGenaiModelsRequest( connection_id = OPENAI_CONNECTION_ID ) response = api.genai.list_genai_models(request) print(sorted([model.id for model in response.genai_models])) ``` This code uses the [Unified.to](https://unified.to/) Python SDK's `genai.list_genai_models()` method to get a list of objects describing the models offered by OpenAI and outputs a sorted list of their IDs. At the time of writing, the result looked like this (formatted for easier reading): ```python [ 'babbage-002', 'dall-e-2', 'dall-e-3', 'davinci-002', 'gpt-3.5-turbo', 'gpt-3.5-turbo-0125', 'gpt-3.5-turbo-0301', 'gpt-3.5-turbo-0613', 'gpt-3.5-turbo-1106', 'gpt-3.5-turbo-16k', 'gpt-3.5-turbo-16k-0613', 'gpt-3.5-turbo-instruct', 'gpt-3.5-turbo-instruct-0914', 'gpt-4', 'gpt-4-0125-preview', 'gpt-4-0613', 'gpt-4-1106-preview', 'gpt-4-1106-vision-preview', 'gpt-4-turbo', 'gpt-4-turbo-2024-04-09', 'gpt-4-turbo-preview', 'gpt-4-vision-preview', 'gpt-4o', 'gpt-4o-2024-05-13', 'text-embedding-3-large', 'text-embedding-3-small', 'text-embedding-ada-002', 'tts-1', 'tts-1-1106', 'tts-1-hd', 'tts-1-hd-1106', 'whisper-1' ] ``` You can do the same for Claude simply by changing `connection_id`'s value to `CLAUDE_CONNECTION_ID`. The resulting output looks like this (formatted for easier reading): ```python [ 'claude-2.0', 'claude-2.1', 'claude-3-haiku-20240307', 'claude-3-opus-20240229', 'claude-3-sonnet-20240229', 'claude-instant-1.2' ] ``` **Talk to different OpenAI models** Run this code in a new cell: ```python def ask_ai_which_model(my_connection_id, my_model_id): request = operations.CreateGenaiPromptRequest( connection_id = my_connection_id, genai_prompt = { 'model_id' : my_model_id, 'messages' : [ { 'role': 'user', 'content': "Which LLM model am I talking to right now?" } ] } ) response = api.genai.create_genai_prompt(request) if response.genai_prompt is not None: print(f"{model_id}: {response.genai_prompt.responses}\n") else: print("No response.") ``` This defines the `ask_ai_which_model()` method, which will make it simpler to send the same prompt to different AIs and models. Enter the following into a new cell: ```python model_ids = ['gpt-3.5-turbo', 'gpt-4o'] for model_id in model_ids: ask_ai_which_model(OPENAI_CONNECTION_ID, model_id) ``` This code sends the same prompt, 'Which LLM model am I talking to right now?' to two different OpenAI models, **gpt-3.5-turbo** and the new **gpt-4o**. It specifies which model to use with the `'model_id'` key in the `genai_prompt` dictionary. Here's its output: ```python gpt-3.5-turbo: ['I am GPT-3, a language model developed by OpenAI.'] gpt-4o: ["You are interacting with a model based on OpenAI's GPT-4. How can I assist you today?"] ``` Let's try it with Claude. Enter the following into a new cell: ```python model_ids = ['claude-3-sonnet-20240229', 'claude-3-opus-20240229'] for model_id in model_ids: ask_ai_which_model(CLAUDE_CONNECTION_ID, model_id) ``` This code sends the 'Which LLM model am I talking to right now?' to two different Claude models, the currently available **sonnet** and **opus** models. Here's its output: ```python claude-3-sonnet-20240229: ["I am an AI assistant created by Anthropic, but I'm not sure which specific model I am. I don't have full information about the technical details of my architecture or training process."] claude-3-opus-20240229: ['I am an AI assistant called Claude. I was created by Anthropic, PBC to be helpful, harmless, and honest.'] ``` ## Next steps While our unified GenAI API greatly simplifies the process of calling on various generative AI services, and leverage your customer's APi keys on those AI vendors, its real power comes from using it to process data from our other APIs, which integrate with an array of SaaS application categories, including: - **ATS (Applicant Tracking System):** Use AI to analyze documents that job applicants provide, such as their resume or cover letter, as well interviewer notes and scorecard comments. - **KMS (Knowledge Management System):** Find lost knowledge, convert text data into structured data, summarize meeting minutes, and gain new insights by harnessing an LLM to analyze knowledge systems, wikis, and other planning applications. - **Messaging:** Our Messaging API can retrieve emails and chat messages, and when combined with AI, can be used to do things like construct a record of a project, create a timeline of an ongoing discussion, identify incomplete tasks, and more. - **Storage:** Get files and documents from popular cloud storage systems and combine them with an LLM to generate reports, documentation, how-to guides, etc. ## Try our GenAI API now Our unified GenAI API is available on all Unified.to workspaces on every plan — even our free one. Once you've created your Unified.to account, you can activate integrations in seconds and start building applications that leverage our GenAI integrations. See how easy it is to use our real-time unified API by signing up for our free 30-day unlimited-use [**Tester plan**](https://app.unified.to/login). ## How to use User Provisioning and Verification with Unified URL: https://docs.unified.to/guides/how_to_use_user_provisioning_and_verification_with_unified # How to use User Provisioning and Verification with Unified ------ _August 10, 2025_ # User Provisioning and Verification with Unified With Unified's SCIM API devs can build use provisioning features that work with any HRIS integration like BambooHR, Workday and any other ATS integration that Unified connects to. You can create, update, list and delete users in your customers employee directories. In this guide, I will show you how to provision users using the SDK, with BambooHR as an example. The same approach works for all Unified HR integrations. ## Prerequisites - Node.js (v18+) - Unified account with at least one HR integration enabled (e.g. for this example we are using BambooHR) - Unified API key - Your customer's HR connection ID ## Supported Integrations Unified's API works with 170+ HR integrations (BambooHR, Workday, HiBob, Gusto, etc.) and 5+ verification providers (Certn, Checkr, First Advantage, Verifiable, Yardstik). [See all HR integrations](https://docs.unified.to/hris/integrations) [See all verification integrations](https://docs.unified.to/verification/integrations) --- ## Step 1: Setting up your project ```bash mkdir user-verification-demo cd user-verification-demo npm init -y npm install @unified-api/typescript-sdk dotenv ``` Add your credentials to `.env`: ```plain text UNIFIED_API_KEY=your_unified_api_key CONNECTION_BAMBOOHR=your_customer_bamboohr_connection_id CONNECTION_VERIFICATION=your_customer_verification_connection_id ``` --- ## Step 2: Initialize the SDK ```typescript import 'dotenv/config'; import { UnifiedTo } from '@unified-api/typescript-sdk'; const { UNIFIED_API_KEY, CONNECTION_BAMBOOHR, CONNECTION_VERIFICATION } = process.env; const sdk = new UnifiedTo({ security: { jwt: UNIFIED_API_KEY! }, }); ``` --- ## Step 3: How to Get Your Customer's Connection ID Before you get started, your end customer must authorize your app to access their HRIs integration via Unified's auth flow. Once authorized, you will receive a connection ID for that customer's integration. Store this carefully and use it in all API calls for that specific customer. --- ## Step 4: Creating a User (Provisioning) ```typescript export async function createUser(connectionId: string, email: string, firstName: string, lastName: string) { const employee = await sdk.hris.createHrisEmployee({ connectionId, hrisEmployee: { name: `${firstName} ${lastName}`, emails: [{ email }], firstName, lastName, employmentStatus: "ACTIVE", }, }); return employee; // HrisEmployee } ``` --- ## Step 5: Listing Users ```typescript export async function listUsers(connectionId: string) { const employees = await sdk.hris.listHrisEmployees({ connectionId, limit: 10, }); return employees; // HrisEmployee[] } ``` --- ## Step 6: Updating a User ```typescript export async function updateUserStatus(connectionId: string, userId: string, status: "ACTIVE" | "INACTIVE") { const updated = await sdk.hris.updateHrisEmployee({ connectionId, id: userId, hrisEmployee: { employmentStatus: status }, }); return updated; // HrisEmployee } ``` --- ## Step 7: Deactivating a User (Recommended) ```typescript export async function deactivateUser(connectionId: string, userId: string) { // Some providers (e.g., BambooHR) do not support delete; set status to INACTIVE instead const updated = await sdk.hris.updateHrisEmployee({ connectionId, id: userId, hrisEmployee: { employmentStatus: "INACTIVE" }, }); return updated; } ``` --- ## Step 8: Verifying a User (Background Check, License, etc.) Unified's new Verification API lets you trigger background checks, license verifications, and more, using providers like Certn, Checkr, and others. ```typescript export async function listVerificationRequests(connectionId: string) { // Listing requests is supported across providers; creation may vary by provider/package const requests = await sdk.verification.listVerificationRequests({ connectionId, limit: 10, }); return requests; } ``` --- ## Step 9: Example Usage ```typescript async function main() { // 1. Create a user const user = await createUser(CONNECTION_BAMBOOHR!, "jane.doe@example.com", "Jane", "Doe"); // 2. List users const users = await listUsers(CONNECTION_BAMBOOHR!); // 3. Update (deactivate) user if (user?.id) { const updated = await updateUserStatus(CONNECTION_BAMBOOHR!, user.id, "INACTIVE"); console.log("Updated user:", updated); } // 4. List verification requests (optional) if (CONNECTION_VERIFICATION) { const requests = await listVerificationRequests(CONNECTION_VERIFICATION!); console.log("Verification requests:", requests.length); } console.log("Users:", users.length); } main(); ``` --- And that's it - **Happy Building ** 🎉 ## Integration set-up guide for Lever URL: https://docs.unified.to/guides/integration_set_up_guide_for_lever # Integration set-up guide for Lever ------ _September 27, 2023_ **Easily add a Lever integration to your HR product. Access user data within Lever and other ATS systems through one API.** This article will take you through the steps to set up a Lever integration via Unified.to and connect your first Lever account. ## Getting Started If you haven't already signed up for a Unified.to account, do so now (it's free). You can log in through your preferred identity provider such as Google, Microsoft, or GitHub. [Create an account](https://app.unified.to/login) ## 1. Activate Lever Once you've registered your Unified.to account, log in and go to **Active Integrations** to select Lever and any other pre-built integrations you'd like to activate. You can use the search bar, filter by app categories like "HR" and "ATS" or view all of our available integrations at once. The integrations you activate are the ones you intend to enable for user authorization. ![Activate-Lever.png](https://s3.us-east-2.amazonaws.com/unified-article-images/integration_set_up_guide_for_lever-0.png) ## 2. Use your OAuth 2 Credentials (Optional) While most of our integrations can be activated in seconds, some integrations require OAuth 2 credentials. To display your product's name and branding during the third-party authorization process, you will need to insert your OAuth 2 credentials. Follow the setup instructions linked below the integration ('Get your own OAuth 2 credentials →') to create and retrieve your credentials. ![Lever-Integration-Sandbox.png](https://s3.us-east-2.amazonaws.com/unified-article-images/integration_set_up_guide_for_lever-1.png) Insert your credentials (usually a Client ID and Secret) to activate the integration. If you'd like to test this integration with your Lever Sandbox account, select 'Sandbox' and provide your Lever Sandbox credentials. ## 3. Copy your Workspace ID Your workspace represents your organization. You can have multiple workspaces, for instance, if you require team members to work in different workspaces. You will need your workspace ID to add Lever and any other integrations to your app. Go to **API Information** found under **Settings** to copy your workspace ID ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/integration_set_up_guide_for_lever-2.png) _Note: you can create multiple environments in each workspace with distinct access and configuration rules for testing, staging, production, and more._ ## 4. Add your Lever integration to your app Once you've activated Lever and any other integrations, you'll be able to add them to your app. Go to **Settings > Embedded Directory** to copy one line of code to insert the embedded directory widget into your product. You can try out the user experience by interacting with the directory preview. ![Directory-Preview.png](https://s3.us-east-2.amazonaws.com/unified-article-images/integration_set_up_guide_for_lever-3.png) Alternatively, you can use our VueJS, React, or Angular components. Place the code into your app along with your workspace ID. If you want an alternative to using our UI component, you can do either of the following: - Call our API to get a[ list of active integrations](https://unified.to/apidocs#get/unified/integration/workspace/%7Bworkspace_id%7D) to display in your app - If you have your user's API ## 5. Perform Actions with your User Connections Now you can start adding code to your app to access your user data and perform actions with their connections. If you're using NodeJS, we also have an SDK library available. Alternatively, you can use our Mock API to see our data model in action. In your Sandbox environment, add env=Sandbox query parameter to your API calls to get mock data. User connections represent a specific authentication of an integration, which means you can now: - Access your customers' third-party data in Lever - Leverage user connections to automate workflows and personalize interactions - Extend your app's functionality with new features and expanded capabilities Need support? Email us at hello@unifited.to or chat with our team on [Discord](https://discord.gg/uAYnMPFk9t). Keep learning: - [Set up multiple environments](https://unified.to/help/set_environments_for_your_unified_workspace) - [Authentication](https://unified.to/apidocs#auth) - [Embedded Directory](https://unified.to/apidocs#embed) ## Managing custom validation rules in Salesforce URL: https://docs.unified.to/guides/managing_custom_validation_rules_in_salesforce # Managing custom validation rules in Salesforce ------ _March 22, 2024_ If you are getting `FIELD_CUSTOM_VALIDATION_EXCEPTION` when trying to write data to a Salesforce connection, this means there is a custom validation rule on this Salesforce account and the data that you are providing isn't valid To get [Unified.to](https://unified.to/) to be able to write this object you will need to get your customer to modify the Salesforce rule on their account to be less strict or remove the rule all together. For example, you are getting `"FIELD_CUSTOM_VALIDATION_EXCEPTION: The Shipping address is required."` when trying to [create a Company](https://docs.unified.to/crm/company/Create_a_company). This means there is a custom rule on the Salesforce **Account** object that requires a shipping address. Follow these instructions to modify/remove a custom rule for Account object: 1. Go to your Salesforce account and navigate to **Setup** ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/managing_custom_validation_rules_in_salesforce-0.png) 2. On the left-hand side navigate to **Objects and Fields > Object Manager** ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/managing_custom_validation_rules_in_salesforce-1.png) 3. Select the object you need to modify the rules for. In our case **Account** ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/managing_custom_validation_rules_in_salesforce-2.png) 4. Navigate to **Validation Rules** ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/managing_custom_validation_rules_in_salesforce-3.png) 5. Edit or remove the custom validation rule ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/managing_custom_validation_rules_in_salesforce-4.png) ## Multi-Region Sync URL: https://docs.unified.to/guides/multi_region_sync # Multi-Region Sync ------ _June 22, 2026_ Unified.to runs in multiple data regions — US, EU, and AU — so your data can live close to where you and your customers are. Multi-Region Sync keeps your account configuration consistent across those regions automatically. When you turn on Multi-Region Sync, the settings you manage in your "home" region are continuously replicated to your other selected region(s). You configure things once, and they show up everywhere — no manual re-entry, no drift between regions. ## What is Multi-Region Sync? Multi-Region Sync is useful when you need to: - **Meet data-residency requirements:** keep certain end users' connection data in the EU or AU while still managing one account. - **Reduce latency:** serve customers from the region closest to them. - **Operate with regional redundancy:** keep your account configuration mirrored across regions. > ⭐ Availability: Multi-Region Sync is available on the Scale & Pro plan. Enable it in your workspace settings. ## What gets synced When Multi-Region Sync is enabled, the following are replicated from your home region to your other selected region(s): - **Workspace / environment settings** — your workspace configuration and environment settings. - **Users** — the team members and admins on your workspace. - **API keys** — your Unified.to API keys, so the same keys work in each region. - **Integration secrets / credentials** — the OAuth client credentials and secrets you configure for your integrations. - **Workspace integrations** — your enabled integrations and their configuration (scopes, settings, branding, etc.). - **Notifications** — your workspace notification settings. In practice: you set up your integrations, branding, API keys, and team once in your home region, and they are automatically mirrored to your other region(s). Updating an integration's configuration or credentials in one region propagates the change to the others. ## What does not get synced End-user connections, webhooks, and API call logs / usage history are intentionally region-specific and are not replicated. A connection is the authorization a single end user grants when they connect their account (for example, a customer linking their Salesforce or Google account). These stay in the region where they were created. This is by design — it keeps each end user's data resident in the region you chose for them and avoids duplicating sensitive end-user tokens across regions. Rule of thumb: - **Account-level configuration** (integrations, credentials, keys, users, settings) → synced across regions. - **End-user connections and the data they pull** → stay in their own region. ## How to enable Multi-Region Sync 1. Make sure your account is on the Scale & Pro plan. If you're not sure, contact your account manager. 2. Choose your home region — where you set up your account first. Sync is bidirectional, so you can edit configuration in any region and it stays consistent across all of them. 3. Select what to sync (workspace settings, users, API keys, integration secrets, workspace integrations, notifications). 4. Save. Unified.to performs a one-time initial copy of your existing configuration, then keeps the regions in sync automatically. ## Frequently asked questions - **Which regions are supported?** US, EU, and AU. - **Do my customers need to do anything?** No. They keep connecting in whichever region you set up for them; their connections stay there. - **Will enabling sync move my existing connections?** No. Connections are never moved or copied between regions. - **Is the synced data encrypted in transit?** Yes. All replicated configuration, including secrets, is encrypted and integrity-protected in transit. - **Can I choose which items to sync?** Yes — any combination of the items listed above. - **How do I turn it off or change my regions?** Contact your account manager or Unified.to support. --- Need help? Reach out to your account manager or [contact Unified.to support](https://unified.to/contact). ## NetSuite Authentication Setup for Unified URL: https://docs.unified.to/guides/netsuite_authentication_setup_for unified # NetSuite Authentication Setup for Unified ------ _February 12, 2026_ Unified supports **two** authentication methods for connecting to NetSuite: 1. **OAuth 2.0 (Authorization Code Grant)** — browser-based login. **Recommended for most customers.** No need to copy/paste secrets. Tokens refresh automatically. 2. **Token-Based Authentication (TBA / OAuth 1.0a)** — you generate and paste 5 long-lived credentials. Best for server-to-server use cases, headless integrations, or where browser SSO isn't feasible. Both methods talk to the same **SuiteTalk REST Web Services** API, so the underlying NetSuite feature flags and the role permissions list below are nearly identical. The main difference is **how Unified obtains the access token**. **"Realm" = NetSuite Account ID** in every NetSuite client library and connector. We use the terms interchangeably below. --- ## **Which method should I choose?** | | OAuth 2.0 | Token-Based Authentication (TBA) | | --------------------------- | --------------------------------------------- | ------------------------------------------------------------------- | | Setup effort | Low — sign in once via NetSuite | Higher — create Integration Record + Access Token | | Credentials Unified stores | access_token + refresh_token (auto-refreshed) | 5 long-lived secrets | | Best for | Most customers | Server-to-server, no human login, strict secret-management policies | | Token lifespan | Access token refreshes automatically | Tokens are long-lived; rotate manually | | What you provide to Unified | NetSuite Account ID + browser login | Realm + Consumer Key/Secret + Token ID/Secret | If you're not sure, **start with OAuth 2.0**. --- ## **What you need before starting (both methods)** - Admin access in NetSuite (recommended), **OR** a NetSuite admin available to enable features and grant role permissions. - The role used for the connection must have the correct permissions (see Required Role Permissions below). - For sandbox testing, know whether you are in **Sandbox** vs **Production** — your Account ID and credentials are different between environments. --- ## **Step 1 — Enable the required NetSuite features (both methods)** In NetSuite: **Setup → Company → Enable Features → SuiteCloud** tab. Under **Manage Authentication**, enable: - ✅ **OAuth 2.0** _(required for OAuth 2.0 method)_ - ✅ **Token-Based Authentication** _(required for TBA method)_ Under **SuiteTalk (Web Services)**, enable: - ✅ **REST Web Services** Without **REST Web Services**, all API calls return **401** regardless of which auth method you use, so make sure this is on. Click **Save**. --- ## **Step 2 — Find your Realm / Account ID (both methods)** Your **Realm** is your **NetSuite Account ID**. **Option A — from the URL (fastest):** When logged into NetSuite, the Account ID is in the host portion of the URL. **Option B — from Company Information:** **Setup → Company → Company Information** → look for **Account ID**. **Sandbox note:** Sandbox accounts include a suffix like _SB1 (or -SB1 in URLs). Use **underscores** when entering the Account ID into Unified (e.g. 1234567_SB1). Unified normalizes between the URL form (-sb1) and the OAuth realm form (_SB1) automatically. --- ## **Step 3 — Configure the Role used for the connection (both methods)** This is the **most important step** for avoiding 401 INVALID_LOGIN and "permission denied" errors. The role you use determines what Unified can read and write — for both OAuth 2.0 and TBA. **Recommended: use the Administrator role** The simplest and most reliable option: - Use the **Administrator** role for the connecting user (OAuth 2.0), or for the token (TBA). This guarantees: - All record types are accessible - All subsidiaries are accessible - No hidden permission failures - Fastest setup **Optional: create a dedicated "Unified API" custom role** If your security policy requires a least-privilege role, create one and grant the permissions in Required Role Permissions. User-level restrictions can override role-level permissions. If you use a custom role, make sure the **user account itself** also has these permissions where applicable. --- ## **Step 4A — Connect using OAuth 2.0 (recommended)** If you chose OAuth 2.0, you do **not** need to create an Integration Record or Access Token manually — Unified is already a pre-registered NetSuite OAuth 2.0 application. **4A.1 Make sure your user has the right role** The user who clicks "Connect" in Unified must have a role assigned that: - Has the permissions listed in Required Role Permissions, **including** Log in using OAuth 2.0 Access Tokens. - Has access to all subsidiaries you want Unified to sync. Confirm role assignment under **Lists → Employees → Employees → [user] → Access** tab. **4A.2 Connect from Unified** 1. In Unified, choose **NetSuite** as the integration. 2. Select **OAuth 2.0** as the authentication method (if asked). 3. Enter your **NetSuite Account ID** (e.g. 1234567 for production or 1234567_SB1 for sandbox). 4. You'll be redirected to NetSuite to sign in. 5. After signing in, NetSuite will show a consent screen with the requested scope (REST Web Services). 6. Choose the **Role** to authorize Unified under — this should be **Administrator** or the custom **Unified API** role. 7. Click **Allow**. Unified will store the resulting access_token and refresh_token. The access token is automatically refreshed on expiry — you do not need to do anything further. **4A.3 (Optional) Bring-Your-Own OAuth 2.0 credentials** If your security policy requires you to use your **own** OAuth 2.0 Integration Record (rather than Unified's pre-registered one): 1. **Setup → Integration → Manage Integrations → New** 2. Name: e.g. "Unified Integration" 3. State: **Enabled** 4. On the **Authentication** tab: - ✅ **OAuth 2.0** - **Scope**: REST Web Services - **Redirect URI**: provide the redirect URL given to you by Unified support 1. **Save** — NetSuite will display the **Client ID** and **Client Secret**. Copy them immediately (the secret cannot be retrieved later). 2. Share the Client ID / Client Secret with Unified support so we can install them on your workspace. Reference: NetSuite OAuth 2.0 Authorization Code Grant Flow. --- ## **Step 4B — Connect using Token-Based Authentication (TBA)** If you chose TBA, you'll provide Unified **5 values**: - realm (NetSuite Account ID) - consumer_key - consumer_secret - token_id (a.k.a. Token Key) - token_secret **4B.1 Create the Integration Record (Consumer Key / Secret)** 1. **Setup → Integration → Manage Integrations → New** 2. Name: e.g. "Unified Integration" 3. State: **Enabled** 4. On the **Authentication** tab: - ✅ **Token-Based Authentication** - (Leave OAuth 2.0 off unless you also plan to use OAuth 2.0 with the same record.) 1. **Save** — NetSuite displays the **Consumer Key** and **Consumer Secret**. **Copy them immediately**; the Consumer Secret cannot be viewed again. These are the consumer_key and consumer_secret you'll give Unified. **4B.2 Assign the role to the user who will own the token** Tokens are created for a **User + Role + Integration Record** combination. 1. **Lists → Employees → Employees** 2. Select the user (or create a dedicated "API User" like unified-api@yourcompany.com) 3. Open the **Access** tab 4. Assign **Administrator** (recommended) **OR** your custom **Unified API** role 5. **Save** **4B.3 Create the Access Token (Token ID / Token Secret)** 1. **Setup → Users/Roles → Access Tokens → New** _(may be labelled "Manage Access Tokens" in some UIs)_ 2. Fill in: - **Application Name**: the Integration Record you created (e.g. "Unified Integration") - **User**: the user from 4B.2 - **Role**: **Administrator** (recommended) **OR** your **Unified API** role - **Token Name**: optional but recommended (e.g. "Unified Production Token") 1. **Save** — NetSuite displays the **Token ID** and **Token Secret**. **Copy them immediately**. The role you select here is the role whose permissions are enforced on **every** API call made with this token. Choosing the wrong role is the #1 cause of 401 INVALID_LOGIN and permission errors. **4B.4 Provide the 5 values to Unified** In the Unified connection form, enter: | Unified field | NetSuite value | | -------------------------- | -------------- | | realm_id (or "Account ID") | from Step 2 | | consumer_key | from Step 4B.1 | | consumer_secret | from Step 4B.1 | | token_id | from Step 4B.3 | | token_secret | from Step 4B.3 | --- ## **Required Role Permissions** These apply to **both** OAuth 2.0 and TBA when you choose a custom (non-Administrator) role. The list is the full set required for full accounting + CRM + commerce + HRIS support. You can trim it based on which Unified object types you actually use. User-level restrictions can override role-level permissions. Make sure the user has these permissions in addition to the role. **Transactions** | Permission | Level | | ------------------------ | ----- | | Access Payment Audit Log | Full | | Bill Purchase Orders | Full | | Bills | Full | | Cash Sale | Full | | Cash Sale Refund | Full | | Credit Memo | Full | | Cross Charge Journal | View | | Customer Deposit | Full | | Customer Payment | Full | | Customer Refund | Full | | Enter Vendor Credits | Full | | Find Transaction | Full | | Intercompany Adjustments | Full | | Invoice | Full | | Invoice Approval | Full | | Invoice Sales Orders | Full | | Item Receipt | Full | | Item Shipment | Full | | Journal Approval | Full | | Make Journal Entry | Full | | Opportunity | Full | | Paycheck Journal | Full | | Receive Order | Full | | System Journal | Full | **Reports** | Permission | Level | | ------------------ | ----- | | Transaction Detail | View | **Lists** | Permission | Level | | ------------------------------- | ----- | | Accounts | Full | | Address List in Search | Full | | Contact-Subsidiary Relationship | View | | Contacts | Full | | Currency | Full | | Customers | Full | | Employee Record | Full | | Employees | Full | | Inventory Cost Template | View | | Locations | Full | | Partners | Full | | Perform Search | View | | Subsidiaries | Full | | Vendors | Full | **Setup** | Permission | Level | | -------------------------------------------- | ----------------------- | | Access Token Management | Full _(TBA only)_ | | Integration Application | Full | | Log in using Access Tokens | Full _(TBA only)_ | | Log in using OAuth 2.0 Access Tokens | Full _(OAuth 2.0 only)_ | | OAuth 2.0 Authorized Applications Management | Full _(OAuth 2.0 only)_ | | REST Web Services | Full | | Two-Factor Authentication Base | Full | | User Access Tokens | Full _(TBA only)_ | **Notes:** - For TBA, the token inherits the permissions of the **role selected when the token is created** — not the user's default role. - For OAuth 2.0, the access token inherits the permissions of the **role selected during the NetSuite consent screen**. - Subsidiary access is controlled by role restrictions. If you use OneWorld, ensure the role has access to every subsidiary Unified must sync. --- ## **Validation & Troubleshooting** **401 INVALID_LOGIN** This almost always means one of: - **Wrong role selected** when the OAuth 2.0 consent was approved, or when the TBA token was created. - **Missing permissions** on the role: - OAuth 2.0: missing Log in using OAuth 2.0 Access Tokens and/or OAuth 2.0 Authorized Applications Management - TBA: missing Log in using Access Tokens, Access Token Management, or User Access Tokens - **Wrong Account ID** (especially mixing sandbox with production). - **REST Web Services** not enabled at the account level. - **Integration Record disabled** (TBA only). - **Consumer key/secret paired with a token from a different Integration Record** (TBA only). The fastest way to diagnose is the **Login Audit Trail**: **Setup → Users/Roles → User Management → View Login Audit Trail** Filter by **Token-based** or **OAuth 2.0** logins and look at the failure reason on the most recent attempt. **400 Bad Request / INSUFFICIENT_PERMISSION** The connection authenticated but the role lacks permission for the specific record type Unified is trying to access. Add the missing permission from the Required Role Permissions table (most commonly **Lists → Subsidiaries**, **Lists → Accounts**, or a Transaction permission). **Sandbox vs Production** - Sandbox tokens / OAuth 2.0 consents only work against the sandbox account. - Sandbox URLs use -sb1 (e.g. 1234567-sb1.app.netsuite.com); the realm sent on API calls uses _SB1 (e.g. 1234567_SB1). Unified handles the conversion automatically — just enter the Account ID with the _SB1 underscore form when prompted. **Token rotation (TBA)** NetSuite TBA tokens don't expire by default, but your security policy may require rotation: 1. Create a new Access Token under the same User + Role + Integration Record. 2. Update the token_id and token_secret in Unified. 3. Revoke the old token in **Setup → Users/Roles → Access Tokens**. **Re-authorizing OAuth 2.0** If a user is removed or their role changes, the OAuth 2.0 grant may become invalid. To re-authorize: 1. In NetSuite: **Setup → Users/Roles → OAuth 2.0 Authorized Applications** → revoke the existing Unified grant (optional but recommended). 2. In Unified, reconnect the NetSuite integration. You'll be sent back through the NetSuite consent screen. ## Notice of Deprecation of Fields and Objects (Nov 2025) URL: https://docs.unified.to/guides/notice_of_deprecation_of_fields_and_objects_nov_2025 # Notice of Deprecation of Fields and Objects (Nov 2025) ------ _November 10, 2025_ # Deprecated Fields, Objects & List Options At Unified, we have always built our unified data-models with a lot of thoughtful attention to detail, so that they never have to be 'versioned' due to backward-compatibility issues. In fact, when we launch a new data object or a brand new category, designing our unified data models always takes longer than actually 'coding' the integrations. Our customers have noticed and have told us that we have the best designed unified data models in the business. Over the 2.5 years, as we've made improvements to the unified data-models, we have kept older fields and objects around so that our customers do not have to change their applications. Today, we are announcing some deprecations of fields, data-models and list options that have been improved with others and will shortly be removed in our system. This document provides a comprehensive breakdown of all deprecation in the Unified API as of November 2025, organized by category with migration recommendations. Please make all necessary changes to your code by `JANUARY 7, 2026` --- ## Table of Contents 1. MetadataMetadata Fields 2. Parent Fields 3. HRIS Employee Fields 4. MessagingMessage Fields 5. Accounting Fields 6. ATS (Applicant Tracking System) Fields 7. Ticketing Fields 8. Calendar Fields 9. Query Parameters & Filters 10. Webhook Fields 11. Deprecated Objects/Usage Patterns 12. Deprecated Types/Interfaces --- ## MetadataMetadata Fields ### Field: `key` → `slug` - the reason was the `key` was a confusing name and could denote something else that it wasn't **Affected Models:** - `IKmsPageMetadata` (KMS) - `src/models/UnifiedKms.ts` - `ITaskMetadata` (Task) - `src/models/UnifiedTask.ts` - `IHrisMetadata` (HRIS) - `src/models/UnifiedHris.ts` - `IAtsMetadata` (ATS) - `src/models/UnifiedAts.ts` - `ICommerceMetadata` (Commerce) - `src/models/UnifiedCommerce.ts` - `ICrmMetadata` (CRM) - `src/models/UnifiedCrm.ts` **Deprecated Field:** ```typescript key?: string; // @deprecated; use slug instead ``` **Replacement:** ```typescript slug?: string; // Actual textual value of the slug ``` **Migration Recommendation:** - **Read Operations:** Replace all references to `metadata.key` with `metadata.slug` - **Write Operations:** Use `slug` instead of `key` when creating or updating metadata objects **Example:** ```typescript // Before metadata: [{ key: 'custom_field', value: 'some value' }] // After metadata: [{ slug: 'custom_field', value: 'some value' }] ``` --- ### Field: `type` → `format` - the reason was the `type` was a confusing name and could denote something else that it wasn't **Affected Models:** - `IKmsPageMetadata` (KMS) - `src/models/UnifiedKms.ts` - `ITaskMetadata` (Task) - `src/models/UnifiedTask.ts` - `IHrisMetadata` (HRIS) - `src/models/UnifiedHris.ts` - `IAtsMetadata` (ATS) - `src/models/UnifiedAts.ts` - `ICommerceMetadata` (Commerce) - `src/models/UnifiedCommerce.ts` - `ICrmMetadata` (CRM) - `src/models/UnifiedCrm.ts` **Deprecated Field:** ```typescript type?: string; // @deprecated; use format instead ``` **Replacement:** ```typescript format?: TMetadataFormat; // Enum: TEXT, NUMBER, DATE, BOOLEAN, FILE, TEXTAREA, SINGLE_SELECT, MULTIPLE_SELECT, MEASUREMENT, PRICE, YES_NO, CURRENCY, URL, etc. ``` **Migration Recommendation:** - **Read Operations:** Replace all references to `metadata.type` with `metadata.format` - **Write Operations:** Use `format` with the appropriate enum value instead of `type` string - **Type Safety:** The `format` field uses strongly-typed enums, providing better type safety and validation **Example:** ```typescript // Before metadata: [{ key: 'age', type: 'number', value: 25 }] // After metadata: [{ slug: 'age', format: 'NUMBER', // Use enum value value: 25 }] ``` --- ## Parent Fields ### Field: `parent_space_id` → `parent_id` ### Field: `parent_page_id` → `parent_id` - the reason was that we standardized across all of our unified data models the field name of `parent_id` when it denoted a parent of the current object (and is the same object) **Affected Models:** - `IKmsSpace` - `src/models/UnifiedKms.ts` - `IKmsPage` - `src/models/UnifiedKms.ts` **Deprecated Field:** ```typescript // In KmsSpace parent_space_id?: string; // @deprecated; use parent_id instead // In KmsPage parent_page_id?: string; // @deprecated; use parent_id instead ``` **Replacement:** ```typescript parent_id?: string; ``` **Migration Recommendation:** - **Read Operations:** Use `parent_id` instead of `parent_space_id` when reading space objects - **Write Operations:** Use `parent_id` when creating or updating spaces **Example:** ```typescript // Before const space = { name: 'Subspace', parent_space_id: 'space_123' } // After const space = { name: 'Subspace', parent_id: 'space_123' } ``` --- ### Field: `parent_channel_id` → `parent_id` **Affected Model:** - `IMessagingChannel` - `src/models/UnifiedMessaging.ts` **Deprecated Field:** ```typescript parent_channel_id?: string; // @deprecated; use parent_id instead ``` **Replacement:** ```typescript parent_id?: string; ``` **Migration Recommendation:** - **Read Operations:** Use `parent_id` instead of `parent_channel_id` when reading channel objects - **Write Operations:** Use `parent_id` when creating or updating channels **Example:** ```typescript // Before const channel = { name: 'Sub-channel', parent_channel_id: 'channel_123' } // After const channel = { name: 'Sub-channel', parent_id: 'channel_123' } ``` --- ### Field: `parent_account_id` → `parent_id` **Affected Model:** - `IAccountingAccount` **Deprecated Field:** ```typescript parent_account_id?: string; // @deprecated; use parent_id instead ``` **Replacement:** ```typescript parent_id?: string; // The parent account ID for this account ``` **Migration Recommendation:** - **Read Operations:** Use `parent_id` instead of `parent_account_id` when reading account objects - **Write Operations:** Use `parent_id` when creating or updating accounts **Example:** ```typescript // Before const account = { name: 'Sub-account', parent_account_id: 'account_123' } // After const account = { name: 'Sub-account', parent_id: 'account_123' } ``` --- ### Field: `parent_message_id` → `parent_id` **Affected Model:** - `IMessagingMessage` **Deprecated Field:** ```typescript parent_message_id?: string; // @deprecated; use parent_id ``` **Replacement:** ```typescript parent_id?: string; // Represents the ID of the immediate predecessor message in the thread ``` **Migration Recommendation:** - **Read Operations:** Use `parent_id` instead of `parent_message_id` when reading message objects - **Write Operations:** Use `parent_id` when creating threaded messages **Example:** ```typescript // Before const message = { message: 'Reply text', parent_message_id: 'msg_123' } // After const message = { message: 'Reply text', parent_id: 'msg_123' } ``` --- ## HRIS Employee Fields ### Field: `department` → `groups` - we unified all HR 'grouping' objects into one called HRIS Group. Groups have types that denote if they are a department, business unit, team, … **Affected Model:** - `IHrisEmployee` - `src/models/UnifiedHris.ts` **Deprecated Field:** ```typescript department?: string; // @deprecated ``` **Replacement:** ```typescript groups?: IHrisGroup[]; // Which groups/teams/units that this employee/user belongs to ``` **Migration Recommendation:** - **Read Operations:** Access department information through the `groups` array - **Write Operations:** Use `groups` array with `IHrisGroup` objects instead of a single `department` string - **Multiple Groups:** The `groups` field supports multiple group memberships, which is more flexible than a single department **Example:** ```typescript // Before const employee = { name: 'John Doe', department: 'Engineering' } // After const employee = { name: 'John Doe', groups: [{ id: 'group_123', name: 'Engineering', type: 'DEPARTMENT' }] } ``` --- ### Field:`division` → `groups` **Affected Model:** - `IHrisEmployee` **Deprecated Field:** ```typescript division?: string; // @deprecated ``` **Replacement:** ```typescript groups?: IHrisGroup[]; // Which groups/teams/units that this employee/user belongs to ``` **Migration Recommendation:** - **Read Operations:** Access division information through the `groups` array - **Write Operations:** Use `groups` array with `IHrisGroup` objects instead of a single `division` string **Example:** ```typescript // Before const employee = { name: 'John Doe', division: 'Product' } // After const employee = { name: 'John Doe', groups: [{ id: 'group_456', name: 'Product', type: 'DIVISION' }] } ``` --- ### Field: `location` → `locations` **Affected Model:** - `IHrisEmployee` - `src/models/UnifiedHris.ts` **Deprecated Field:** ```typescript location?: string; // @deprecated ``` **Replacement:** ```typescript locations?: IHrisLocation[]; // Array of partial location objects ``` **Migration Recommendation:** - **Read Operations:** Access location information through the `locations` array - **Write Operations:** Use `locations` array with `IHrisLocation` objects instead of a single `location` string - **Rich Data:** The `locations` array provides full address details, timezone, currency, and other metadata **Example:** ```typescript // Before const employee = { name: 'John Doe', location: 'San Francisco Office' } // After const employee = { name: 'John Doe', locations: [{ id: 'loc_123', name: 'San Francisco Office', address: { address1: '123 Main St', city: 'San Francisco', region: 'CA', postal_code: '94102', country: 'United States' }, timezone: 'America/Los_Angeles' }] } ``` --- ## Messaging Fields ### Field: `channel_id` → `channels` ### Field: `channel_ids` → `channels` - the reason was so we could include additional channel information, such as the channel `name` , as well as its `id` **Affected Model:** - `IMessagingMessage` - `src/models/UnifiedMessaging.ts` **Deprecated Field:** ```typescript channel_id?: string; // @deprecated ``` **Replacement:** ```typescript channels?: IMessagingChannelMessage[]; // Represents the names of all channels to which the message is sent / belongs to ``` **Migration Recommendation:** - **Read Operations:** Use `channels` array instead of `channel_id` or `channels_ids` - **Write Operations:** Use `channels` array when creating messages - **Multi-Channel Support:** The `channels` array supports messages posted to multiple channels (when the integration supports it) **Example:** ```typescript // Before const message = { message: 'Hello', channel_id: 'channel_123', channels_ids: ["channel_123"] } // After const message = { message: 'Hello', channels: [{ id: 'channel_123', name: 'General' }] } ``` --- ### Field: `root_message_id` → Removed **Affected Model:** - `IMessagingMessage` **Deprecated Field:** ```typescript root_message_id?: string; // @deprecated ``` **Replacement:** - No direct replacement. Use `parent_id` to traverse the thread structure. **Migration Recommendation:** - **Read Operations:** If you need to find the root message, traverse the `parent_id` chain until you reach a message without a `parent_id` - **Write Operations:** Remove references to `root_message_id` when creating messages **Example:** ```typescript // Before const message = { message: 'Reply', parent_message_id: 'msg_123', root_message_id: 'msg_1' } // After const message = { message: 'Reply', parent_id: 'msg_123' // root_message_id removed - traverse parent_id chain if needed } // Helper function to find root message function findRootMessage(message) { while (message.parent_id) { message = getMessage(message.parent_id); } return message; } ``` --- ## Accounting Fields ### Field: `invoice_at` → `posted_at` **Affected Model:** - `IAccountingInvoice` **Deprecated Field:** ```typescript invoice_at?: (string | Date | number); // @deprecated; use posted_at ``` **Replacement:** ```typescript posted_at?: (string | Date | number); ``` **Migration Recommendation:** - **Read Operations:** Use `posted_at` instead of `invoice_at` when reading invoice objects - **Write Operations:** Use `posted_at` when creating or updating invoices - **Consistency:** This change aligns with other accounting objects that use `posted_at` for transaction dates **Example:** ```typescript // Before const invoice = { invoice_number: 'INV-001', invoice_at: '2024-01-15' } // After const invoice = { invoice_number: 'INV-001', posted_at: '2024-01-15' } ``` --- ### Field: `type` → Removed **Affected Model:** - `IAccountingInvoice` **Deprecated Field:** ```typescript type?: TAccountingInvoiceType; // @deprecated ``` **Replacement:** - No direct replacement. Bills are now found in the AccountingBill object, while invoices are solely in the AccountingInvoice object. **Migration Recommendation:** - **Read Operations:** Remove dependencies on `invoice.type` field - **Alternative:** Bills are now found in the AccountingBill object, while invoices are solely in the AccountingInvoice object. --- ### Field: `contact_id` → `contacts` **Affected Model:** - `IAccountingTransaction` - `src/models/UnifiedAccounting.ts` **Deprecated Field:** ```typescript contact_id?: string; // @deprecated; use contacts ``` **Replacement:** ```typescript contacts?: IAccountingTransactionContact[]; ``` **Migration Recommendation:** - **Read Operations:** Use `contacts` array instead of `contact_id` - **Write Operations:** Use `contacts` array when creating transactions, but just include an `id` field - **Multi-Contact Support:** The `contacts` array supports multiple contacts per transaction (when the integration supports multiple contacts) - **Rich Data:** Provides contact names and emails, not just IDs **Example:** ```typescript // Before const transaction = { total_amount: 1000, contact_id: 'contact_123' } // After const transaction = { total_amount: 1000, contacts: [{ id: 'contact_123', name: 'Acme Corp', emails: [{ email: 'billing@acme.com' }] }] } ``` --- ### Field: `income` → `income_sections` **Affected Model:** - `IAccountingProfitloss` - `src/models/UnifiedAccounting.ts` **Deprecated Field:** ```typescript income?: IAccountingProfitlossCategory[]; // @deprecated – use income_sections instead ``` **Replacement:** ```typescript income_sections?: IAccountingProfitlossSection[]; ``` **Migration Recommendation:** - **Read Operations:** Use `income_sections` instead of `income` - **Write Operations:** Use `income_sections` when creating profit/loss reports - **Enhanced Structure:** The new `income_sections` provides a more structured format with better categorization **Example:** ```typescript // Before const profitloss = { start_at: '2024-01-01', end_at: '2024-12-31', income: [{ name: 'Sales', amount: 100000 }] } // After const profitloss = { start_at: '2024-01-01', end_at: '2024-12-31', income_sections: [{ name: 'Sales', accounts: [{ name: 'Product Sales', amount: 100000 }] }] } ``` --- ### Field:`expenses` → `expenses_sections` **Affected Model:** - `IAccountingProfitloss` **Deprecated Field:** ```typescript expenses?: IAccountingProfitlossCategory[]; // @deprecated – use expenses_sections instead ``` **Replacement:** ```typescript expenses_sections?: IAccountingProfitlossSection[]; ``` **Migration Recommendation:** - **Read Operations:** Use `expenses_sections` instead of `expenses` - **Write Operations:** Use `expenses_sections` when creating profit/loss reports **Example:** ```typescript // Before const profitloss = { expenses: [{ name: 'Operating Expenses', amount: 50000 }] } // After const profitloss = { expenses_sections: [{ name: 'Operating Expenses', accounts: [{ name: 'Salaries', amount: 50000 }] }] } ``` --- ### Field:`cost_of_goods_sold` → `cost_of_goods_sold_sections` **Affected Model:** - `IAccountingProfitloss` **Deprecated Field:** ```typescript cost_of_goods_sold?: IAccountingProfitlossCategory[]; // @deprecated – use cost_of_goods_sold_sections instead ``` **Replacement:** ```typescript cost_of_goods_sold_sections?: IAccountingProfitlossSection[]; ``` **Migration Recommendation:** - **Read Operations:** Use `cost_of_goods_sold_sections` instead of `cost_of_goods_sold` - **Write Operations:** Use `cost_of_goods_sold_sections` when creating profit/loss reports **Example:** ```typescript // Before const profitloss = { cost_of_goods_sold: [{ name: 'Materials', amount: 30000 }] } // After const profitloss = { cost_of_goods_sold_sections: [{ name: 'Materials', accounts: [{ name: 'Raw Materials', amount: 30000 }] }] } ``` --- ### Field:`gross_profit_amount` → Calculate from `income_total_amount` and `cost_of_goods_sold_total_amount` **Affected Model:** - `IAccountingProfitloss` **Deprecated Field:** ```typescript gross_profit_amount?: number; // @deprecated – compute using income_total_amount - cost_of_goods_sold_total_amount ``` **Replacement:** ```typescript gross_profit_amount = income_total_amount - cost_of_goods_sold_total_amount ``` **Migration Recommendation:** - **Read Operations:** Calculate `gross_profit_amount` using `income_total_amount - cost_of_goods_sold_total_amount` - **Write Operations:** Do not set `gross_profit_amount` directly **Example:** ```typescript // Before const profitloss = { income_total_amount: 100000, cost_of_goods_sold_total_amount: 30000, gross_profit_amount: 70000 } // After const profitloss = { income_total_amount: 100000, cost_of_goods_sold_total_amount: 30000 // gross_profit_amount removed - calculate it } // Calculate gross profit const gross_profit_amount = profitloss.income_total_amount - profitloss.cost_of_goods_sold_total_amount; ``` --- ### Field: `net_profit_amount` → `net_income_amount` **Affected Model:** - `IAccountingProfitloss` **Deprecated Field:** ```typescript net_profit_amount?: number; // @deprecated – use net_income_amount instead ``` **Replacement:** ```typescript net_income_amount?: number; ``` **Migration Recommendation:** - **Read Operations:** Use `net_income_amount` instead of `net_profit_amount` - **Write Operations:** Use `net_income_amount` when creating profit/loss reports **Example:** ```typescript // Before const profitloss = { net_profit_amount: 50000 } // After const profitloss = { net_income_amount: 50000 } ``` --- ### Field: `IAccountingProfitlossCategory` and `IAccountingProfitlossSubcategory` → Deprecated Types **Deprecated Types:** - `IAccountingProfitlossCategory` - @deprecated - `IAccountingProfitlossSubcategory` - @deprecated **Replacement:** - Use `IAccountingProfitlossSection` instead **Migration Recommendation:** - **Type Definitions:** Update TypeScript interfaces to use `IAccountingProfitlossSection` - **Read Operations:** Use `income_sections`, `expenses_sections`, and `cost_of_goods_sold_sections` which use the new section structure **Example:** ```typescript // Before interface Profitloss { income?: IAccountingProfitlossCategory[]; } // After interface Profitloss { income_sections?: IAccountingProfitlossSection[]; } ``` --- ## ATS (Applicant Tracking System) Fields ### Field:`document_id` → `document_ids` **Affected Model:** - `IAtsActivity` - `src/models/UnifiedAts.ts` **Deprecated Field:** ```typescript document_id?: string; // @deprecated ``` **Replacement:** ```typescript document_ids?: string[]; // IDs for AtsDocument.get ``` **Migration Recommendation:** - **Read Operations:** Use `document_ids` array instead of `document_id` - **Write Operations:** Use `document_ids` array when creating activities - **Multi-Document Support:** Supports multiple documents per activity **Example:** ```typescript // Before const activity = { title: 'Review Resume', document_id: 'doc_123' } // After const activity = { title: 'Review Resume', document_ids: ['doc_123', 'doc_456'] } ``` --- ### Field: `departments` → `groups` **Affected Model:** - `IAtsJob` - `src/models/UnifiedAts.ts` **Deprecated Field:** ```typescript departments?: string[]; // @deprecated Use `groups` instead ``` **Replacement:** ```typescript groups?: IAtsGroup[]; // The departments/divisions/teams that this job belongs to ``` **Migration Recommendation:** - **Read Operations:** Use `groups` array instead of `departments` - **Write Operations:** Use `groups` array with `IAtsGroup` objects instead of department strings - **Rich Data:** Provides group IDs, names, and types (TEAM, GROUP, DEPARTMENT, DIVISION, etc.) **Example:** ```typescript // Before const job = { name: 'Software Engineer', departments: ['Engineering', 'Product'] } // After const job = { name: 'Software Engineer', groups: [{ id: 'group_123', name: 'Engineering', type: 'DEPARTMENT' }, { id: 'group_456', name: 'Product', type: 'DEPARTMENT' }] } ``` --- ## Ticketing Fields ### Field: `category` → `category_id` **Affected Model:** - `ITicketingTicket` - `src/models/UnifiedTicketing.ts` **Deprecated Field:** ```typescript category?: string; // @deprecated; use category_id ``` **Replacement:** ```typescript category_id?: string; ``` **Migration Recommendation:** - **Read Operations:** Use `category_id` instead of `category` string - **Write Operations:** Use `category_id` when creating or updating tickets - **Consistency:** Using `category_id` maintains consistency with other ID-based relationships **Example:** ```typescript // Before const ticket = { subject: 'Support Request', category: 'technical' } // After const ticket = { subject: 'Support Request', category_id: 'category_123' } ``` --- ## Calendar Fields ### Field:`primary` → `is_primary` **Affected Model:** - `ICalendarCalendar` - `src/models/UnifiedCalendar.ts` **Deprecated Field:** ```typescript primary?: boolean; // @deprecated ``` **Replacement:** ```typescript is_primary?: boolean; ``` --- ## Query Parameters & Filters ### `expand_recurring_events` → `expand` **Affected Models:** - `ICalendarEvent` **Deprecated Parameter:** ```typescript expand_recurring_events?: boolean; // @deprecated; use expand ``` **Replacement:** ```typescript expand?: boolean; ``` **Migration Recommendation:** - **API Calls:** Replace `expand_recurring_events` query parameter with `expand` - **More Flexible:** `expand` supports expanding multiple event types, not just recurring events **Example:** ```typescript // Before GET /api/calendar/events?expand_recurring_events=true // After GET /api/calendar/events?expand=true ``` --- ### `end_le` → `end_lt` **Affected Models:** - `ICalendarEvent` - `ICalendarBusy` - `ICalendarRecording` - `IUcCall` - `IUcRecording` - `IHrisTimeoff` - `IHrisTimeshift` - `IAccountingBalancesheet` - `IAccountingCashflow` - `IAccountingProfitloss` - `IAccountingTrialbalance` - `IMessagingMessage` - `IPaymentSubscription` - `IPaymentPayment` - `IPaymentPayout` **Deprecated Parameter:** ```typescript end_le?: string | Date; // @deprecated; use end_lt ``` **Replacement:** ```typescript end_lt?: string | Date; ``` **Migration Recommendation:** - **API Calls:** Replace `end_le` (less than or equal) with `end_lt` (less than) - **Consistency:** Aligns with standard date range filtering conventions **Example:** ```typescript // Before GET /api/calendar/events?end_le=2024-12-31 // After GET /api/calendar/events?end_lt=2025-01-01 // Note: Adjust date by +1 day if you need inclusive end date ``` ``` --- ## Webhook Payload Fields ### Field: `sig` → `sig256` **Affected Interface:** - `IWebhookData` **Deprecated Field:** ```typescript sig?: string; // @deprecated; use sig256 instead ``` **Replacement:** ```typescript sig256?: string; // HMAC-SHA256(workspace.secret, data + nonce) ``` **Migration Recommendation:** - **Webhook Verification:** Use `sig256` instead of `sig` for webhook signature verification - **Security:** `sig256` uses HMAC-SHA256 which is more secure than the previous signature algorithm. `sig` used HMAC-SHA1, which is now deprecated. - **Verification:** Update your webhook verification code to check `sig256` field **Example:** ```typescript // Before const isValid = verifySignature(webhook.sig, payload, secret, 'sha'); // After const isValid = verifySignature(webhook.sig256, payload, secret, ‘sha256'); ``` ``` --- ## Deprecated Objects/Usage Patterns ### `IAccountingInvoice` with `type === 'BILL'` → Use `IAccountingBill` **Status:** Deprecated usage pattern: - Instead of using `IAccountingInvoice` with `type='BILL'`, use the dedicated `IAccountingBill` object **Affected Model:** - `IAccountingInvoice` - `src/models/UnifiedAccounting.ts` **Deprecated Pattern:** ```typescript interface IAccountingInvoice { type: 'BILL', // @deprecated - use IAccountingBill instead ... } ``` **Replacement:** ```typescript interface IAccountingBill { ... } ``` **Key Differences:** - `IAccountingBill` uses `bill_number` instead of `invoice_number` - `IAccountingBill` does not have the deprecated `type` field - `IAccountingBill` does not have the deprecated `invoice_at` field (uses `posted_at` instead) - More semantic clarity: a Bill is clearly distinct from an Invoice **Migration Recommendation:** - **Read Operations:** Use `accounting_bill` endpoints instead of `accounting_invoice` endpoints with `type='BILL'` - **Write Operations:** Use `accounting_bill` create/update endpoints instead of `accounting_invoice` with `type='BILL'` **Field Mapping:** When migrating data: - `invoice_number` → `bill_number` - Remove `type` field (no longer needed) - `invoice_at` → `posted_at` (if still using deprecated field) **API Endpoints:** - ❌ Deprecated: `POST /accounting/invoice` with `type: 'BILL'` - ✅ Use: `POST /accounting/bill` **Example:** ```typescript // Before POST /accounting/invoice { 'type': 'BILL', 'invoice_number': 'BILL-001', 'contact_id': 'contact_123', 'total_amount': 1000 } // After POST /accounting/bill { 'bill_number': 'BILL-001', 'contact_id': 'contact_123', 'total_amount': 1000 } ``` --- ### `IAccountingReport` → Use Individual Report Objects **Status:** Deprecated object - Instead of using `IAccountingReport`, use the individual report objects directly **Affected Model:** - `IAccountingReport` - `src/models/UnifiedAccounting.ts` **Deprecated Object Structure:** ```typescript interface IAccountingReport { type?: TAccountingReportType; // Enum: TRIAL_BALANCE, BALANCE_SHEET, PROFIT_AND_LOSS ... } ``` **Replacement:** Use individual report objects directly **Balance Sheet:** ```typescript interface IAccountingBalancesheet { id?: string; created_at?: (string | Date | number); updated_at?: (string | Date | number); start_at?: (string | Date | number); end_at?: (string | Date | number); name?: string; currency?: string; net_assets_amount?: number; assets?: IAccountingBalancesheetItem[]; liabilities?: IAccountingBalancesheetItem[]; equity?: IAccountingBalancesheetItem[]; raw?: unknown; } ``` **Profit and Loss:** ```typescript interface IAccountingProfitloss { id?: string; created_at?: (string | Date | number); updated_at?: (string | Date | number); category_ids?: string[]; start_at?: (string | Date | number); end_at?: (string | Date | number); name?: string; currency?: string; income_sections?: IAccountingProfitlossSection[]; expenses_sections?: IAccountingProfitlossSection[]; cost_of_goods_sold_sections?: IAccountingProfitlossSection[]; income_total_amount?: number; net_income_amount?: number; expenses_total_amount?: number; cost_of_goods_sold_total_amount?: number; raw?: unknown; } ``` **Trial Balance:** ```typescript interface IAccountingTrialbalance { id?: string; created_at?: (string | Date | number); updated_at?: (string | Date | number); start_at?: (string | Date | number); name?: string; currency?: string; end_at?: (string | Date | number); total_debit_amount?: number; total_credit_amount?: number; sub_items?: IAccountingTrialbalanceSubItem[]; raw?: unknown; } ``` **Cash Flow:** ```typescript interface IAccountingCashflow { id?: string; created_at?: (string | Date | number); updated_at?: (string | Date | number); start_at?: (string | Date | number); end_at?: (string | Date | number); category_ids?: string[]; contact_id?: string; name?: string; // e.g. "Cash Flow Statement Q1 2020" currency?: string; // ISO 4217, e.g. "USD" cash_beginning_amount?: number; // Cash at beginning of period cash_ending_amount?: number; // Cash at end of period net_change_in_cash_amount?: number; // Usually ending - beginning operating_sections?: IAccountingCashflowSection[]; investing_sections?: IAccountingCashflowSection[]; financing_sections?: IAccountingCashflowSection[]; raw?: unknown; } interface IAccountingCashflowSection { section_name?: string; // e.g. "Operating Activities" total_amount?: number; // Net cash provided/used by this section items?: IAccountingCashflowItem[]; } interface IAccountingCashflowItem { account_id?: string; // If attributable to a specific GL account name?: string; // e.g. "Net Income", "Depreciation", "Equipment" amount?: number; // Positive = inflow, Negative = outflow transaction_ids?: string[]; // Optional linkage to transactions sub_items?: IAccountingCashflowItem[]; } ``` **Migration Recommendation:** - **Read Operations:** Instead of using `accounting_report` endpoints, use the specific report endpoints - ❌ Deprecated: `GET /accounting/report?type=BALANCE_SHEET`. ✅ Use: `GET /accounting/balancesheet` - ❌ Deprecated: `GET /accounting/report?type=PROFIT_AND_LOSS`. ✅ Use: `GET /accounting/profitloss` - ❌ Deprecated: `GET /accounting/report?type=TRIAL_BALANCE`. ✅ Use: `GET /accounting/trialbalance` - ✅ Use: `GET /accounting/cashflow` (Cash flow was never part of the deprecated report object, but should be accessed directly) - **Write Operations:** Create individual report objects directly instead of wrapping them in a report object - **Field Access:** Access report data directly from the individual objects instead of through `report.balance_sheet`, `report.profit_and_loss`, etc. **Example:** ```typescript // Before const report = await getAccountingReport({ type: 'BALANCE_SHEET', start_at: '2024-01-01', end_at: '2024-12-31' }); const assets = report.balance_sheet?.assets; // After const balanceSheet = await getAccountingBalancesheet({ start_at: '2024-01-01', end_at: '2024-12-31' }); const assets = balanceSheet.assets; ``` **Benefits of Using Individual Objects:** - **Simpler API:** Each report type has its own dedicated endpoint - **Better Type Safety:** TypeScript can better infer types for specific report objects - **Clearer Intent:** Code is more explicit about which report type is being accessed - **Easier Maintenance:** Individual objects are easier to extend and modify independently --- ## Deprecated Types/Interfaces ### `IAccountingProfitlossCategory` and `IAccountingProfitlossSubcategory` **Status:** Deprecated types - These interfaces are deprecated in favor of `IAccountingProfitlossSection` **Affected Files:** - `src/models/UnifiedAccounting.ts` - `src/models/UnifiedAccounting.joi.ts` (marked as `@deprecated`) - `src/models/UnifiedAccounting.proto` **Deprecated Types:** ```typescript interface IAccountingProfitlossCategory { name?: string; amount?: number; sub_items?: IAccountingProfitlossSubcategory[];} interface IAccountingProfitlossSubcategory { name?: string; amount?: number; transaction_ids?: string[];} ``` **Replacement:** ```typescript interface IAccountingProfitlossSection { section_type?: string; section_name?: string; total_amount?: number; accounts?: IAccountingProfitlossAccount[];} interface IAccountingProfitlossAccount { account_id?: string; account_name?: string; total_amount?: number; transaction_ids?: string[];} ``` **Migration Recommendation:** - Update TypeScript type definitions to use `IAccountingProfitlossSection` - Update code that creates or manipulates profit/loss categories to use sections instead - The new structure provides better organization with accounts grouped under sections --- ## Summary by Category | Category | Deprecated Fields Count | Primary Changes | | ---------------------- | ----------------------- | -------------------------------------------------------------------------------------------- | | Metadata | 2 | `key` → `slug`, `type` → `format` (affects 6 models) | | Parent/Relationship | 4 | Various `parent_*_id` → `parent_id` | | HRIS | 3 | `department`, `division`, `location` → `groups`/`locations` | | Messaging | 4 | `channel_id`, `channel_ids`, `parent_message_id`, `root_message_id` → `channels`/`parent_id` | | Accounting | 8 | Multiple field replacements and structural changes | | ATS | 2 | `document_id` → `document_ids`, `departments` → `groups` | | Ticketing | 1 | `category` → `category_id` | | Query Parameters | 3 | Date filter and expand parameter updates | | Webhooks | 1 | `sig` → `sig256` | | **Deprecated Objects** | **2** | `IAccountingInvoice` with `type='BILL'`, `IAccountingReport` | | **Deprecated Types** | **2** | `IAccountingProfitlossCategory`, `IAccountingProfitlossSubcategory` | **Total Deprecated Fields:** - 27 unique fields - 2 deprecated objects - 2 deprecated types across all categories --- ## General Migration Guidelines ### 1. **Backward Compatibility** - Deprecated fields will still be supported until `JANUARY 7, 2026` - Plan to migrate as soon as possible to avoid breaking changes in future API versions ### 2. **Testing** - Test all migrations thoroughly in a development environment - Verify that data reads and writes work correctly with the new fields ### 3. **Data Migration** - For existing data using deprecated fields, consider: - Running a one-time migration script to populate new fields from old fields - Updating your application code to read from both old and new fields during transition - Gradually migrating writes to use only new fields ### **4. Documentation** - Update your internal documentation and code comments - Notify your team about these changes - Update API integration tests to use new field names ## Oracle HCM / Taleo OAuth Credentials Guide URL: https://docs.unified.to/guides/oracle_hcm_taleo_oauth_credentials_guide # Oracle HCM / Taleo OAuth Credentials Guide ------ _April 6, 2026_ To connect Oracle HCM / Taleo to Unified, you only need to collect these 3 values from Oracle: - Client ID - Client Secret - Servername You will enter those directly into Unified. ## **What To Enter In Unified** When creating the Oracle HCM / Taleo connection, fill in: - Client ID - Client Secret - Servername **Example Servername** ## **How To Get These Values From Oracle** **1. Find Your Servername** Ask your Oracle administrator for your Oracle Fusion HCM host name. It usually looks like: `.fa..oraclecloud.com` Example: [`example.fa.us2.oraclecloud.com`](https://example.fa.us2.oraclecloud.com/) This exact value is what you should enter into Unified as the Servername. **2. Create A Confidential Application In Oracle** 1. Sign in to Oracle as an administrator 2. Open Integrated applications 3. Click Add application 4. Choose Confidential Application 5. Enter a name such as Unified Oracle HCM Integration 6. Continue through the setup **3. Enable OAuth Client Access** During application setup, make sure OAuth client access is enabled for the application. For this Unified connection, Oracle should issue credentials for a confidential application that can request access tokens for the HCM / Taleo APIs. Your Oracle administrator may need to: - enable OAuth for the app - allow the app to access the appropriate Oracle HCM / Taleo resources - activate the application once setup is complete **4. Copy The Credentials** After the confidential application is created and activated, open the application details and copy: - Client ID - Client Secret These are the exact values to enter into Unified. ## **Final Checklist** Before creating the connection in Unified, make sure you have: - Client ID - Client Secret - Servername ## **Oracle References** - OAuth configuration: [https://docs.oracle.com/en/cloud/saas/applications-common/25d/oaext/configure-oauth.html](https://docs.oracle.com/en/cloud/saas/applications-common/25d/oaext/configure-oauth.html) - HCM REST API: [https://docs.oracle.com/en/cloud/saas/human-resources/farws/index.html](https://docs.oracle.com/en/cloud/saas/human-resources/farws/index.html) ## Retrieval-Augmented Generation (RAG) URL: https://docs.unified.to/guides/retrieval_augmented_generation_rag # Retrieval-Augmented Generation (RAG) ------ _February 13, 2026_ _Last updated: June 2026_ This page explains how to implement a RAG pipeline using Unified. RAG is an implementation pattern built on top of Unified APIs. Unified provides real-time data access and normalized objects. You are responsible for embeddings, vector storage, and retrieval. At a high level, RAG with Unified follows this sequence: 1. Subscribe to webhooks. 2. Receive created or updated objects. 3. Retrieve full content from the source API. 4. Chunk the content. 5. Generate embeddings. 6. Store embeddings in your vector database. 7. On query, retrieve relevant chunks and generate a response. The pattern is the same across categories. ## What RAG Means in Practice RAG allows you to answer questions using customer data from connected SaaS platforms. Instead of relying only on model training data, your application: - Retrieves relevant customer records. - Supplies those records as context. - Generates a response grounded in that context. Unified enables real-time retrieval directly from source APIs and supports event-driven synchronization. - Normalized across providers. - Fetched in real time. - Consistent in schema. Unified does not store embeddings or maintain a vector index. ## Step 1: Connect Data Sources Authorize integrations using Embedded Auth. Common RAG source categories include: - File Storage - Knowledge Management - CRM - Ticketing - ATS - Messaging Each authorized integration returns a `connection_id`. RAG pipelines should treat `connection_id` as the tenant boundary. ## Step 2: Subscribe to Object Updates Create webhook subscriptions for the objects you want to index. **Backfill on first subscription** Set `include_all: true` when creating the webhook subscription. This delivers all existing records to your endpoint before transitioning to incremental updates. Pages are delivered with `type: INITIAL-PARTIAL`; the final page is tagged `type: INITIAL-COMPLETE`. After that, the same subscription delivers ongoing changes tagged `NATIVE` or `VIRTUAL` depending on whether the source integration supports native webhooks. Your endpoint receives the same payload structure for backfill and incremental updates — no separate backfill handler is required. **On created or updated events:** - Your endpoint receives the event. - You call the appropriate retrieve endpoint to fetch the latest version of the object. - If the object contains a `download_url`, fetch the content from that URL. List endpoints often return metadata but not full content. For RAG, you must explicitly retrieve full text before chunking. Native webhooks deliver real-time updates. Virtual webhooks provide the same interface using managed polling and may introduce short delays Webhooks are recommended over polling for RAG ingestion. ## Step 3: Chunk and Embed Large text (files, pages, tickets, resumes, notes, transcripts) should be split into chunks before embedding. Each chunk should include stable metadata such as: - `connection_id` — tenant boundary; required for all retrieval filters - `object_type` — e.g. `crm_deal`, `ats_candidate`, `ticketing_ticket` - `object_id` — source record ID; used to target re-embedding on update - `updated_at` — timestamp of the source record's last update - `is_latest` — boolean flag indicating whether this chunk is current; set to `false` when a record updates and new chunks are inserted. This field is not returned by Unified — you maintain it in your vector store schema. You may also include additional normalized fields as metadata filters. Unified normalizes objects across providers. All normalized fields returned by Unified can be stored as metadata in your vector index and used for filtering at retrieval time. You generate embeddings using the embedding model of your choice. Unified may provide access to embedding models, but embeddings and indexes are stored in your infrastructure. ## Step 4: Store in a Vector Database Store embeddings in your vector database (for example, Pinecone or pgvector). Include metadata to support: - Tenant isolation (`connection_id`) - Object filtering (`object_type`) - Update replacement (`updated_at`) - Permission enforcement (see below) Unified does not: - Cache end-customer payloads - Store embeddings - Maintain a vector index Unified does not persist customer payloads or maintain your embedding index. ## Step 5: Retrieve and Generate When a user submits a query: 1. Embed the query. 2. Retrieve the top matching chunks from your vector database. 3. Filter by `connection_id` and any relevant metadata. 4. Pass retrieved context to your model. 5. Return the generated answer. If required, include citations that map back to `object_id` or `web_url`. Unified does not perform retrieval. Unified ensures the indexed data remains synchronized with source APIs. ## Permissions File objects include explicit permissions metadata. Other objects (CRM, ATS, ticketing, messaging) do not return user- or group-level permission structures. If row-level enforcement is required, implement that logic in your application layer before returning retrieved results Always filter retrieval results by tenant and permission constraints before generation. ## Real-Time Behavior For RAG pipelines, freshness depends on update propagation: - Native webhooks deliver real-time updates. - Virtual webhooks use managed polling and typically operate with short delays. - Most list endpoints support pagination and incremental filtering via `updated_gte` To keep embeddings current: - Re-fetch objects on update events. - Re-chunk and re-embed. - Replace outdated vectors in your index. ## When to Use RAG Use this pattern when you need to: - Build enterprise search across connected platforms. - Ground AI responses in customer documents or records. - Enable resume or candidate search. - Support contract or document Q&A. - Build account-level CRM assistants. The ingestion architecture is consistent across categories. Object models and content retrieval methods vary by API. ## Related APIs RAG pipelines commonly use: - [File Storage API](https://docs.unified.to/storage/overview) - [Knowledge Management API](https://docs.unified.to/kms/overview) - [CRM API](https://docs.unified.to/crm/overview) - [Ticketing API](https://docs.unified.to/ticketing/overview) - [ATS API](https://docs.unified.to/ats/overview) - [Messaging API](https://docs.unified.to/messaging/overview) Refer to category documentation for supported objects and webhook availability. ## Related Guides - [How to build Enterprise Search using RAG](https://unified.to/blog/how_to_build_enterprise_search_using_rag) - [ATS to Vector DB: How to Power Talent Intelligence with Real-Time Data](https://unified.to/blog/ats_to_vector_db_how_to_power_talent_intelligence_with_real_time_data) - [How to Build an AI-Powered Meeting Recording Summarization](https://unified.to/blog/how_to_build_an_ai_powered_meeting_recording_summarization_with_unified) - [How to Train AI with CRM Data using Unified's CRM API](https://unified.to/blog/how_to_train_ai_with_crm_data_using_unified_crm_api) - [How to Train AI with HRIS Data with Unified's HR & Directory API](https://unified.to/blog/how_to_train_ai_with_hris_data_with_unified_hr_and_directory_api) [→ Start your 30-day free trial](https://app.unified.to/login) [→ Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified) ## SalesForce (External Client Apps) on Multiple Organizations URL: https://docs.unified.to/guides/salesforce_external_client_apps_on_multiple_organizations # SalesForce (External Client Apps) on Multiple Organizations ------ _April 15, 2026_ This guide will walk you through the required steps to support using a Salesforce External Client App (ECA) across multiple organizations. When an ECA is created in Salesforce, it can only connect to users within the organization that created it. To allow the ECA to be used across multiple organizations, you must complete the required setup steps before it can connect to users outside the original organization. The customer's Salesforce admin does not need to give anyone their Salesforce credentials, they can run the second set of steps below. > ⚠️ This setup resolves the error 'Cross-org OAuth flows are not supported for this external client app'. That error usually means the External Client App was created in your SourceOrg but has not yet been installed/deployed/approved in the customer's TargetOrg. ### Terminology | Term | Meaning | | ----------------------- | ----------------------------------------------------------------------- | | SourceOrg | The Salesforce org where the External Client App was created | | TargetOrg | The customer's Salesforce org where users are trying to connect | | APP_API_NAME | API Name of the External Client App | | OAUTH_SETTINGS_API_NAME | Metadata name of the OAuth settings attached to the External Client App | 1. The first thing you need to do is create an ECA within Salesforce. If you have already done this proceed to step 2, otherwise check out our [how to guide](https://unified.to/blog/how_to_register_a_salesforce_developer_app_and_get_oauth_2_credentials). ![Screenshot_2026-04-29_at_11.06.56_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/salesforce_external_client_apps_on_multiple_organizations-0.png) 1. [Install SalesForce CLI](https://developer.salesforce.com/tools/salesforcecli) 2. Generate a SalesForce project ```shell sf project generate --name unified-sf ``` 3. Navigate into project ```shell cd unified-sf ``` 4. Login to your Source organization, this is the organization where you have created the External client app ```shell sf org login web --alias SourceOrg ``` 5. Retrieve the app metadata (Replace APP_API_NAME with your app name, which can be found at Setup → External Client Apps → open your app → settings → basic information → API Name ```shell sf project retrieve start --metadata ExternalClientApplication:APP_API_NAME --target-org SourceOrg sf project retrieve start --metadata ExtlClntAppOauthSettings --target-org SourceOrg ``` > ℹ️ After retrieval, look in `force-app/main/default/extlClntAppOauthSettings/` for a file like `salesforce-test-app_oauth.extlClntAppOauthSettings-meta.xml`. The part before `.extlClntAppOauthSettings-meta.xml` is your `OAUTH_SETTINGS_API_NAME`. It is usually NOT the same as `APP_API_NAME` (it often ends with `_oauth`), so confirm it from the retrieved file name instead of guessing. 6. Create a zip of the project folder to give to the customer who wants to install your app. ```shell zip -r ../my_project.zip . --exclude "*/.git/*" --exclude "*/.sfdx/*" ``` Customer Steps (Customer Salesforce admin) 1. Customer opens the project folder ```javascript cd my_project ``` 1. [Install SalesForce CLI](https://developer.salesforce.com/tools/salesforcecli) 2. Customer admin logs into the TargetOrg ```javascript sf org login web --alias TargetOrg ``` 1. The customer will need to make some changes within their Salesforce to deploy the app. In the SalesForce Dev Hub > Settings > Setup > Dev Hub, enable both 'Enable Unlocked Packages and Second-Generation Managed Packages' and 'Enable Dev Hub' ![Screenshot_2026-04-15_at_3.37.45_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/salesforce_external_client_apps_on_multiple_organizations-1.png) ![Screenshot_2026-04-15_at_1.44.39_PM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/salesforce_external_client_apps_on_multiple_organizations-2.png) 1. Customer deploys the ECA app ```javascript sf project deploy start --metadata ExternalClientApplication:APP_API_NAME --target-org TargetOrg sf project deploy start --metadata ExtlClntAppOauthSettings:APP_API_NAME_oauth --target-org TargetOrg ``` > ℹ️ Replace `APP_API_NAME_oauth` above with the real `OAUTH_SETTINGS_API_NAME` from the retrieved file name (it may differ from `APP_API_NAME`). Alternative: deploy both metadata types together: ```shell sf project deploy start --metadata ExternalClientApplication --metadata ExtlClntAppOauthSettings --target-org TargetOrg ``` 1. The customer can then verify that the app has been added to their organization by visiting Setup → External Client Apps → external client app manager where they should see your app installed. ![Screenshot_2026-04-29_at_11.06.47_AM.png](https://s3.us-east-2.amazonaws.com/unified-article-images/salesforce_external_client_apps_on_multiple_organizations-3.png) 1. Configure and approve access in the TargetOrg. The admin should review and configure: - Permitted users - Profiles / permission sets - OAuth policies - Refresh token policy - IP restrictions, if applicable - Admin-approved users, if the org requires admin approval 2. The customer can now create a connection to your app Once the app is deployed and access is granted, the end user can retry the normal Salesforce connection flow through Unified and the cross-org OAuth error will be resolved. ## Long-term application-distribution recommendation For repeated customer onboarding, the preferred long-term approach is to package the External Client App as a Salesforce package and share a package install link with customer Salesforce admins, instead of asking each admin to manually run metadata deployment commands. 1. You create the External Client App once in SourceOrg. 2. You package the app and OAuth settings. 3. You share the package install link with customer admins. 4. The customer Salesforce admin installs the package into TargetOrg. 5. The customer Salesforce admin approves/configures access. 6. End users connect normally through OAuth. ## SAML Single-Sign-On URL: https://docs.unified.to/guides/saml_single_sign_on # SAML Single-Sign-On ------ _September 28, 2025_ SAML (Security Assertion Markup Language) is an XML-based standard for exchanging authentication and authorization data between parties, particularly between an identity provider (IdP) and a service provider (SP). [Unified.to](https://unified.to/) currently supports JumpCloud SAML. [Let us know](https://unified.to/contact) if you need another SAML identity provider. Here's how SAML works: ## **SAML Authentication Flow** **Key Components:** 1. **Identity Provider (IdP)** - The system that authenticates users (e.g., Active Directory, Okta, Azure AD) 2. **Service Provider (SP)** - The application the user wants to access (e.g., your Unified.to app) 3. **User** - The person trying to log in 4. **SAML Assertion** - XML document containing authentication/authorization information **Typical SAML SSO Flow:** 1. User → SP: "I want to access the application" 2. SP → User: Redirect to IdP with SAML AuthnRequest 3. User → IdP: Login with credentials 4. IdP → User: Redirect back to SP with SAML Response/Assertion **Detailed Steps:** 1. **User Access Request** - User visits your application and clicks "SAML SSO" login - Application generates a SAML Authentication Request (AuthnRequest) 2. **Redirect to Identity Provider** - User is redirected to their organization's IdP - AuthnRequest contains information about the SP and requested attributes 3. **User Authentication** - User enters their organizational credentials - IdP validates the credentials 4. **SAML Response Generation** - IdP creates a SAML Response containing a SAML Assertion - Assertion includes user identity, authentication method, session info, etc. 5. **Response Processing** - IdP redirects user back to SP with the SAML Response - SP validates the assertion signature and extracts user information - User is logged into the application **SAML Assertion Contents:** - **Subject**: Who the user is (NameID, email, etc.) - **Authentication Statement**: How they were authenticated - **Attribute Statement**: Additional user attributes (roles, groups, etc.) - **Conditions**: Validity period, audience restrictions - **Signature**: Cryptographic proof of authenticity **Common SAML Bindings:** - **HTTP Redirect**: Data passed via URL parameters - **HTTP POST**: Data posted in form fields - **HTTP Artifact**: Reference token exchanged for full assertion **Security Features:** - **Digital Signatures**: Ensure assertions haven't been tampered with - **Encryption**: Protect sensitive data in transit - **Time-based Conditions**: Assertions expire after set time - **Audience Restrictions**: Limit which SPs can use the assertion **Configuration Requirements:** **For Service Provider (your app):** - SAML metadata XML with entity ID, ACS URL, certificate - User attribute mapping (email, name, roles) - Certificate for signature validation **For Identity Provider:** - SP metadata containing endpoints and certificate - User attribute configuration - SSO URL configuration This is why SAML is popular for enterprise SSO - it provides secure, standardized authentication that integrates with existing corporate identity systems without requiring users to manage separate passwords for each application. ## **JumpCloud SAML Integration Setup** **1. JumpCloud Configuration (Identity Provider Side)** ### **Step 1: Create SSO Application in JumpCloud** 1. Log into JumpCloud Admin Portal 2. Navigate to **SSO Applications** 3. Click **+ Add New Application** 4. Search for "Custom SAML Application" or "Custom App" 5. Configure the application: - **Display Label**: "Unified.to" (or your app name) - **IdP Entity ID**: https://sso.jumpcloud.com/saml2/unified-to (or your preferred ID) - **SP Entity ID**: Your application's entity ID (e.g., https://api.unified.to/saml/metadata) ### **Step 2: Configure SAML Attributes** In the JumpCloud SSO configuration: - **ACS URL**: https://api.unified.to/saml/acs (your assertion consumer service endpoint) - **Audience**: https://api.unified.to/saml/metadata - **Name ID Format**: urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - **Attribute Mappings**: - email → https://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress - firstName → https://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname - lastName → https://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname - username → https://schemas.xmlsoap.org/ws/2005/05/identity/claims/name ### **Step 3: Get JumpCloud Metadata** - Download the IdP metadata XML from JumpCloud - Note the **SSO URL** (usually https://sso.jumpcloud.com/saml2/unified-to) - Get the **X.509 Certificate** for signature validation # Configure SAML on Unified If you haven't registered an account, sign-in with any of our available Social or OAuth2 OIDC login options. Make sure to choose the relevant data region. Then proceed to the workspace settings and choose [SAML](https://app.unified.to/settings/saml). Select your Identity Provider. Either upload the SAML manifest XML file or input the SAML configuration settings from your identity provider. You can choose to restrict sign-ins to just SAML for your workspace. All existing and invited workspace members will then need to sign-in with SAML. ## Scaling MCP Tools with Anthropic's (& OpenAI's) Defer Loading URL: https://docs.unified.to/guides/scaling_mcp_tools_with_anthropic_and_openai_defer_loading # Scaling MCP Tools with Anthropic's (& OpenAI's) Defer Loading ------ _December 25, 2025_ Learn how to use Anthropic's and OpenAI's `defer_loading` tool search features with Unified's MCP server to efficiently manage hundreds of tools while maintaining high accuracy and context efficiency. When building AI applications with MCP servers, including the [Unified MCP server](https://www.notion.so/mcp/overview), you quickly encounter a critical challenge: most LLM models struggle with large numbers of tools. While Unified can provide thousands of tools across different integrations, traditional approaches hit two key limitations: - **Context window bloat**: Tool definitions consume massive portions of your context (50 tools ≈ 10-20K tokens) - **Tool selection degradation**: An LLM's ability to correctly select tools degrades significantly beyond 30-50 tools Anthropic's new **defer_loading** feature solves both first problems by dynamically discovering and loading tools on-demand instead of loading all tool definitions upfront. ## The Problem: Too Many Tools The Unified MCP server can expose tools from any connected integration— CRM, ATS, HRIS, ticketing, storage, and more. A single connection might offer 50+ tools, and with multiple integrations, you could easily have 200+ available tools. Overall, the Unified MCP server currently support more than 22,000 tools. Traditional approach problems: - Loading 200 tool definitions uses 40,000-80,000 tokens - The LLM API struggles to select the correct tool from such a large set - You waste context on tools that won't be used in that conversation ## The Solution: Anthropic's Defer Loading Tool Search Anthropic's `defer_loading` feature works with two tool search variants: ### Tool Search Variants **1. Regex Tool Search** (`tool_search_tool_regex_20251119`) - Claude constructs regex patterns to search for tools - Best for exact matches and pattern-based discovery - Fast and efficient for well-named tools **2. BM25 Tool Search** (`tool_search_tool_bm25_20251119`) - Claude uses natural language queries to search - Better for semantic understanding - More flexible for varied naming conventions ### How It Works 1. You include a tool search tool in your tools list 2. You provide all tool definitions with `defer_loading: true` 3. Claude sees only the tool search tool initially 4. When Claude needs additional tools, it searches dynamically 5. The API returns 3-5 most relevant tools 6. These are automatically expanded into full definitions 7. Claude selects and invokes the appropriate tool ## Implementation To use the new `defer_loading` tool option, follow these instructions (found [here](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool#mcp-integration)) when calling Anthropic's ``/v1/messages`` API: - Add `"mcp-client-2025-11-20"`  to the `anthropic-beta` header: `anthropic-beta: advanced-tool-use-2025-11-20,mcp-client-2025-11-20` - Add the Unified MCP server's URL as usual ```json "mcp_servers": [ { "type": "url", "name": "unified-salesforce-server", "url": "https://mcp-api.unified.to?token=x&connection=y" } ], ``` - Then add an additional `tools` array with configuration on which tools to defer: ```json "tools": [ { "type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex" }, { "type": "mcp_toolset", "mcp_server_name": "unified-salesforce-server", "default_config": { "defer_loading": true }, "configs": { "list_crm_contacts": { "defer_loading": false } } } ], ``` - Use the `default_config.defer_loading: true` option to make all tools deferrable - Use the `configs.{tool_id}.defer_loading: true/false` to set an individual tool to defer (or not) Or, alternatively, you can call the Unified MCP `/tools` endpoint with parameters `?type=anthropic&defer_tools=all` and then feed that `tools` result into Anthropic's API. ## Best Practices ### 1. Keep Core Tools Non-Deferred If you have 3-5 tools that are frequently used, keep them as non-deferred. ### 2. Use Permissions to Scope Tools Reduce the tool catalog size by requesting only the permissions you need: ```typescript // Instead of all tools across all categories permissions: 'crm_contact_read,crm_contact_write,crm_deal_read' // This is better than loading 200+ tools from all categories ``` ### 3. Restrict Tools Use the Unified MCP `tools` parameter to restrict which tools will be returned back to the LLM API. This has a different effect than the `defer_tools` parameter as it doesn't return restricted tools to the LLM API at all, while deferring tools means that the LLM API knows about the tool, but doesnt process it until it is needed. ### 4. Monitor Token Usage Track your token consumption to understand the benefits: ```typescript console.log(`Input tokens: ${response.usage.input_tokens}`); console.log(`Output tokens: ${response.usage.output_tokens}`); console.log(`Tool search requests: ${response.usage.server_tool_use?.tool_search_requests}`); ``` ### 5. Combine with Prompt Caching Use [prompt caching](https://www.notion.so/guides/building_ai_applications_with_unified_and_langbase) with defer_loading for multi-turn conversations: ```typescript messages.push({ role: "user", content: "Now find their recent deals", cache_control: { type: "ephemeral" } }); ``` ## Tool Search Limits Be aware of these limits: - **Maximum tools**: 10,000 tools in your catalog - **Search results**: Returns 3-5 most relevant tools per search - **Pattern length**: Maximum 200 characters for regex patterns - **Model support**: Claude Sonnet 4.5+ and Opus 4.5+ only ## When to Use Defer Loading **Good use cases:** - 20+ tools available from Unified connections - Multiple integrations (CRM + ATS + HRIS + Storage) - Building multi-tenant applications where each tenant has different integrations - Context window is getting tight with tool definitions - Tool selection accuracy is degrading **When traditional tool calling might be better:** - Less than 10 tools total - All tools are frequently used in every request - Very focused single-integration use case ## Real-World Example: Multi-Integration Assistant Here's a practical example of a customer support assistant that accesses multiple integrations: ```typescript async function createSupportAssistant(crm_connection_id, hris_connection_id, zendesk_connection_id) { // Fetch tools from multiple Unified connections const crmTools = await fetchUnifiedTools(crm_connection_id, 'crm_contact_read,crm_deal_read', { type: 'anthropic', defer_tools: 'list_crm_'}); const ticketingTools = await fetchUnifiedTools(zendesk_connection_id, 'ticketing_ticket_read,ticketing_ticket_write', { type: 'anthropic', defer_tools: 'list_crm_'}); const hrisTools = await fetchUnifiedTools(hris_connection_id, 'hris_employee_read', { type: 'anthropic', defer_tools: 'list_crm_'}); // Combine all tools with defer_loading const tools = [ ...crmTools, ...ticketingTools, ...hrisTools ]; // Total: 150+ tools, with all listX tools being deffered return await anthropic.beta.messages.create({ model: "claude-sonnet-4-5-20250929", betas: ["advanced-tool-use-2025-11-20"], max_tokens: 4096, messages: [{ role: "user", content: "Customer John Doe from Acme Corp called about ticket #12345. Show me his account info, open tickets, and any recent deals." }], tools: tools }); } ``` ## Resources - [Unified MCP Server Overview](https://docs.unified.to/mcp/overview) - [MCP Installation & Usage](https://docs.unified.to/mcp/installation) - [MCP Server Options](https://docs.unified.to/mcp/server-options) - [Anthropic Tool Search Documentation](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool) - [Anthropic MCP Integration Guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool#mcp-integration) ## TLDR Anthropic's `defer_loading` feature is a game-changer for building AI applications with Unified's MCP server. By the LLM API dynamically loading tools on-demand, you can: - **Scale to hundreds of tools** across multiple integrations - **Reduce context usage** by 80-90% - **Improve tool selection accuracy** significantly - **Build more capable AI assistants** that access diverse data sources Start by adding the tool search tool and marking your Unified MCP tools as deferred. Monitor your token usage and tool selection accuracy to see the immediate benefits. The combination of Unified's extensive integration network and Anthropic's intelligent tool search opens up possibilities for building truly comprehensive AI agents that can work across your entire SaaS ecosystem. ## Set Environments for your Unified.to Workspace URL: https://docs.unified.to/guides/set_environments_for_your_unified_workspace # Set Environments for your Unified.to Workspace ------ _June 7, 2023_ We're eager to share the release of environments, a new feature that makes it easier to test and customize your integrations before releasing them to production. For example, you can create environments for staging and production, which gives you more control over testing integrations with our Unified API. [**Try it now**](https://app.unified.to/?utm_source=blog&utm_medium=blog&utm_campaign=environments&utm_content=environments_blog) ## What is an environment? Environments are configurable spaces that allow you to specify different integration credentials for different circumstances. Determine distinct access and configuration rules for various scenarios, such as production, staging, and testing, which gives you more flexibility and control over managing your release processes. Environments can be added under any plan in your Unified.to account's Workspace. ![UnifiedAPI_Environments.png](https://s3.us-east-2.amazonaws.com/unified-article-images/set_environments_for_your_unified_workspace-0.png) ## Capabilities of environments - **Integration testing:** Before releasing integrations to production, you can now test them in a different environment. - **Multiple environments:** You can set up multiple environments such as production, staging, and others personalized to you and your team's needs. - **Environment-specific integrations:** You can now decide which integrations are active in each environment. - **Application credential assignment:** Choose which application credentials to use for each integration in each environment. ## Benefits of environments - **Greater flexibility:** You can tailor your integrations to meet the unique needs of each environment including client specifications. - **Reduced errors:** Testing integrations in a staging environment before moving them to production helps minimize mistakes and unexpected errors. - **Increased productivity:** This feature streamlines the management of your workspace, reducing time spent on setup and adjustments. ## When to use multiple environments Set multiple environments regardless of what plan you're on for different scenarios: **1. Localhost** _**Create a sandbox environment without disrupting your team's workflow or end-user experience**_ Example: Suppose Laura, a developer, is working on integrating a new batch of HR services. She creates her own personal authentication environment within Unified.to, complete with application credentials specific to her environment. This allows Laura to experiment freely without affecting her app's live features or her fellow developers' work. **2. Testing or QA** _**Try out new integrations and run tests before releasing them to production**_ Example: Once Laura has completed her initial development work, she simply copies the credentials to the testing environment. This environment has its own set of credentials and often uses mocked or dummy data for testing purposes. In this stage, Laura and her team run a battery of tests to ensure the new HR integrations work as expected and don't disrupt other functionalities within the app. **3. Staging** _**Mirror your production environment to finely-tune performance and perform final tests**_ Example: After the new HR integrations pass all tests in the testing environment, it's promoted to the staging environment. This environment is a mirror image of the production environment, complete with the same application credentials used in production. The HR integrations are tested under conditions that closely mimic the live application. This final testing phase ensures the new batch of integrations are ready for deployment in the production environment. **4. Production** _**Your live environment where your integrations run and are available to end-users**_ Example: This is the live environment where the application operates, interacts with real data, and serves actual users. It uses production application credentials. With the successful deployment of the new HR integrations, all users of the app can now benefit from the new integrations Laura has been working on. ## How to set new environments Implement this new feature in seconds. Follow these steps: **Setting up Environments** 1. Log in to or create a [Unified.to account](https://app.unified.to/?utm_source=blog&utm_medium=blog&utm_campaign=environments&utm_content=environments_blog) 2. Go to **Workspace Settings** and insert the name of your new environment, (e.g., 'Staging') and press enter 3. Once entered, your environment can be used for configuring individual integrations and generating an Embedded Directory **Configuring Integrations** 1. Navigate to **Integrations** and click ACTIVATE on any integration, for example, Workday ![CleanShot_2023-06-08_at_09.12.242x.png](https://s3.us-east-2.amazonaws.com/unified-article-images/set_environments_for_your_unified_workspace-1.png) 2. From the dropdown menu, select the environment where you to want to add credentials, such OAuth Client ID and Secret ![Workday_Environment_Selection.png](https://s3.us-east-2.amazonaws.com/unified-article-images/set_environments_for_your_unified_workspace-2.png) 3. Input your credentials for Workday for the selected environment and click ACTIVATE ![Note: If you are activating an integration that supports API key and doesn't require OAuth credentials, then you can just click ACTIVATE.](https://s3.us-east-2.amazonaws.com/unified-article-images/set_environments_for_your_unified_workspace-3.png) 4. Repeat for any integration and environment ## Embed Directory After enabling integrations for a specific environment, you can generate an Embedded Directory for that environment. 1. Select the desired authentication environment from the dropdown menu. ![Embedded_Directory_Environment.png](https://s3.us-east-2.amazonaws.com/unified-article-images/set_environments_for_your_unified_workspace-4.png) 2. This will generate a script with a new environment parameter for you. ![Directory_Script.png](https://s3.us-east-2.amazonaws.com/unified-article-images/set_environments_for_your_unified_workspace-5.png) If you're using our API to pull a list of active integrations for your directory, you can pass the environment query parameter to retrieve the correct integrations. ## Building the ultimate Unified API solution for integrations This new authentication environment feature is just one of the many ways we're making Unified.to work better for developers who want to build API-first integrations with industry-leading HR, Sales, Marketing, and Support apps. We're excited for you to try Authentication Environments and see the difference it makes in managing your integrations pre-launch. Our team is here to support you every step of the way. Enjoy exploring Unified.to and let us know if you need a hand. You can try setting Unified.to's Authentication Environments at [unified.to/get-started](https://app.unified.to/?utm_source=blog&utm_medium=blog&utm_campaign=environments&utm_content=environments_blog). ## Setting up a Slack Bot Connection with Unified.to URL: https://docs.unified.to/guides/setting_up_a_slack_bot_connection_with_unified # Setting up a Slack Bot Connection with Unified.to ------ _June 16, 2026_ > This guide walks you through connecting Slack Bot (slackbot) to Unified.to — including the Slack app settings you need for sending messages, interactive buttons, and receiving button-click events as Unified MessagingEvent webhooks. --- ## Overview A Slack Bot integration has three parts: | Part | Where | Purpose | | -------------------- | ------------------------ | ----------------------------------------------------------------------------- | | Slack app | api.slack.com | OAuth credentials, bot scopes, and request URLs Slack calls when events occur | | Connection | Unified dashboard | Authorizes your Slack workspace and stores the bot token | | Webhook subscription | Unified dashboard or API | Tells Unified where to deliver normalized MessagingEvent payloads | You can use Unified's shared Slack OAuth app (fastest) or register your own Slack app if you need full control over the app configuration. --- ## Important: two different URLs Slack and your application use different webhook URLs. Do not point Slack at the same URL you use as your Unified hook_url. | URL | Configured in | Receives | | ------------------------- | --------------------------------------------------------- | -------------------------------------------------------- | | Unified workspace webhook | Slack → Event Subscriptions & Interactivity → Request URL | Raw Slack Events API and interactive payloads from Slack | | Your hook_url | Unified → Webhooks → Create Webhook | Normalized Unified MessagingEvent objects | Flow: Slack → Unified workspace webhook → Unified converts the event → POST to your hook_url. Use your regional Unified API host and include your workspace ID, for example: https://api.unified.to/webhook/workspace/slackbot?workspace_id=YOUR_WORKSPACE_ID EU and AU workspaces should use the corresponding regional API host (for example api-eu.unified.to or api-au.unified.to). --- ## Step 1 — Create a Slack app 1. Go to https://api.slack.com/apps → Create New App → From scratch. 2. Name the app and select the Slack workspace you want to connect. 3. Click Create App. --- ## Step 2 — Add OAuth scopes Open OAuth & Permissions → Scopes → Bot Token Scopes and add the scopes for the objects you plan to use. Minimum for sending messages, reading history, and receiving button clicks: | Scope | Purpose | | ------------------------------------------------------------- | ------------------------------------------- | | chat:write | Send messages and messages with buttons | | channels:read / groups:read / im:read / mpim:read | List channels and DMs | | channels:history / groups:history / im:history / mpim:history | Read message history | | users:read / users:read.email / users.profile:read | Resolve message authors and button clickers | | reactions:read | Reaction events (optional) | | files:read / files:write | Attachments (optional) | > These mirror the messaging_message_write and messaging_event_read scope sets for the slackbot integration. Add additional scopes only if you use those objects. --- ## Step 3 — Set the OAuth redirect URL 1. Under OAuth & Permissions → Redirect URLs, click Add New Redirect URL. 2. Paste the Unified OAuth callback URL shown in the dashboard when you connect Slack (bot). It looks like https://api.unified.to/oauth/code (use your region's host if applicable). 3. Save URLs. --- ## Step 4 — Enable Interactivity 1. Go to Interactivity & Shortcuts and turn Interactivity ON. 2. Set Request URL to your Unified workspace webhook (not your hook_url): https://api.unified.to/webhook/workspace/slackbot?workspace_id=YOUR_WORKSPACE_ID 3. Save. Unified receives Slack block_actions payloads here, converts button clicks into MessagingEvent objects with type BUTTON_CLICK, and forwards them to your hook_url. --- ## Step 5 — (Optional) Enable Event Subscriptions Enable this if you also want message, reaction, or channel membership events (not just button clicks). 1. Go to Event Subscriptions and turn it ON. 2. Set Request URL to the same Unified workspace webhook URL as Step 4: https://api.unified.to/webhook/workspace/slackbot?workspace_id=YOUR_WORKSPACE_ID 3. Under Subscribe to bot events, add the events you need, for example message.channels, app_mention, reaction_added, member_joined_channel. 4. Save. --- ## Step 6 — Activate the integration, create a connection, and add a webhook In the Unified dashboard (or via API), complete all three steps below. 1. Go to Integrations and activate Slack (bot) for your environment. If you use your own Slack app, enter your Client ID and Client Secret. 2. Create a connection for the Slack (bot) integration and complete the OAuth flow to install the app into your Slack workspace. 3. Create a webhook on that connection with object type messaging_event and event created. Set hook_url to your application endpoint (the URL where you want to receive Unified payloads). Your hook_url is only configured on the Unified webhook subscription. Slack should always send events to the Unified workspace webhook from Steps 4–5. --- ## Receiving events When a user clicks a button or triggers a subscribed Slack event, Unified POSTs a normalized payload to your hook_url. Button clicks arrive as MessagingEvent objects with type BUTTON_CLICK — not as raw Slack block_actions JSON. See the Unified webhook documentation for payload structure, signatures, and verification. --- ## Troubleshooting | Symptom | Likely cause | | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | Slack URL verification fails | Request URL is missing workspace_id, uses the wrong regional host, or Unified cannot be reached from Slack | | Button clicks never reach my application | Interactivity Request URL is not set to the Unified workspace webhook, or no Unified webhook with hook_url was created | | My endpoint receives raw Slack JSON | Slack Request URL was set to your hook_url instead of the Unified workspace webhook | | invalid_auth or missing scopes | Re-install the Slack app after adding scopes (Slack requires re-consent) | ## Setting up OAuth 2 Credentials for Greenhouse's APIs URL: https://docs.unified.to/guides/setting_up_oauth_2_credentials_for_greenhouse_apis # Setting up OAuth 2 Credentials for Greenhouse's APIs ------ _May 27, 2024_ ## Important — Read this first! Unified.to has _two_ integrations name with the name 'Greenhouse': 1. The first one that appears in the **Integrations** page is for Greenhouse's [**Harvest API**](https://support.greenhouse.io/hc/en-us/articles/360029266032-Harvest-API-overview) **(v3)**, which uses OAuth2 code-flow to authorize your application. For most of the use-cases, you should use this integration. 2. The second one that appears in the **Integrations** page is for Greenhouse's [**Candidate Ingestion API**](https://developers.greenhouse.io/candidate-ingestion.html), which uses OAuth 2 client-credentials-flow to authorize your application. # Greenhouse Harvest API (v3) 1. [Integration request form](https://www.greenhouse.com/integration-partner#apply-now) should be submitted by every new company. _Applications are reviewed by Greenhouse on the first Monday of every month._ **If a company is already an official Greenhouse partner with a signed agreement, they may proceed directly to step 3.** 2. After completing a review, Greenhouse will share API agreement with company 3. Once the agreement is signed, the company should follow the steps [here](https://harvestdocs.greenhouse.io/docs/harvest-partner-oauth) to have a client application created for their integration. They will provide Greenhouse with a redirect_uri (https://api.unified.to/oauth/code) that will be attached to their specific integration's client application. Greenhouse will provide the client credentials (client_id and client_secret) 4. The company will input their (2) credentials in [https://app.unified.to/integrations/greenhouse](https://app.unified.to/integrations/greenhousehttps://app.unified.to/integrations/greenhouse). Unified will handle all authorization: 1. Access authorization, token management, refreshes and storage ([instructions found here](https://harvestdocs.greenhouse.io/docs/harvest-partner-oauth)) 2. Proxy requests on behalf of the originating partner to harvest v3 APIs 3. Secure access schemes to proxy APIs from originating partner to UAPI provider that ensure no cross contamination 5. The company will complete the remaining onboarding steps with Greenhouse. # Greenhouse Ingestion API ## Submit information about your application to Greenhouse Before you can make use of Unified.to's Embedded Authorization component to let your users authorize Greenhouse to access their accounts, you will need to provide Greenhouse with the following: 1. **Application Name**: The name of your application as it would appear in Greenhouse. 2. **Application URL**: The URL of your application. 3. **Callback URL**: After the end user authorizes your application to access their account, Greenhouse will redirect them to this URL. Provide this one: `https://api.unified.to/oauth/code` 4. **Logo Image**: A 128 by 128 pixel image that Greenhouse will include in their permissions modal. ## Receive the Consumer Key and Consumer Secret provided by Greenhouse When Greenhouse receives the information listed above, they will provide a set of values including these two: - A **consumer key**, which uniquely identifies your application - A **consumer secret**, which acts as proof of your application's identity As its name implies, the consumer secret is confidential. For security purposes, Greenhouse will encrypt the consumer secret before emailing it to you (they'll explain how to decrypt it). ## Activate Greenhouse in Unified.to With your application registered with Greenhouse and the OAuth 2 credentials in hand, you can now activate the Greenhouse integration in Unified.to. In Unified.to, go to the [**Active Integrations**](https://app.unified.to/integrations) page, find the Greenhouse Ingestion API item (and _not_ the Greenhouse Harvest API one) and click on it: ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/setting_up_oauth_2_credentials_for_greenhouse_apis-0.png) On the page that appears, do the following: 1. Select **Your OAuth 2 credentials**. 2. Enter the consumer key value into the **OAuth 2 Client ID** text field. 3. Enter the consumer secret value into the **OAuth 2 Client Secret** text field. 4. Click the **Activate** button to save the changes and activate the Greenhouse integration. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/setting_up_oauth_2_credentials_for_greenhouse_apis-1.png) ## Trello Connection Guide in Unified URL: https://docs.unified.to/guides/trello_connection_guide_in_unified # Trello Connection Guide in Unified ------ _April 17, 2026_ ### Overview To create a Trello connection in Unified, you need to: - Create a Trello app - Copy its API key - Allow Unified domains as **Allowed Origins** --- ### Steps **1. Create a Trello App** - Go to **Trello Power-Ups Admin** - Create a new app - Open the app details page **2. Get API Key** - Navigate to the **API Key** section - Copy your Trello API key **3. Configure in Unified** - Go to:[https://app.unified.to/integrations/trello](https://app.unified.to/integrations/trello) - Paste the API key into the required field **4. Add Allowed Origins in Trello** ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/trello_connection_guide_in_unified-0.png) Add the following URLs in your Trello app settings: - [https://app.unified.to](https://app.unified.to/) - [https://auth.unified.to](https://auth.unified.to/) - The Unified API domain shown during setup, for example: - [https://api.unified.to](https://api.unified.to/) - [https://api-eu.unified.to](https://api-eu.unified.to/) - (or your region-specific API domain) **5. Save & Complete Setup** - Save the Trello app settings - Return to Unified - Complete the connection flow --- ### Notes - Make sure the **regional API domain** matches what is shown in the Unified app during setup - This can vary depending on your environment or region --- ### Troubleshooting If the connection does not work: - Confirm the API key is from the correct Trello app - Ensure all **Allowed Origins** are added exactly (no typos or missing domains) - Verify the correct **regional API domain** is included - Retry the connection after saving changes ## Understanding OAuth2 Authorization flows URL: https://docs.unified.to/guides/understanding_oauth2_authorization_flows # Understanding OAuth2 Authorization flows ------ _October 17, 2025_ _Last updated: May 2026_ [OAuth 2.0](https://oauth.net/2/) is an industry-standard protocol for authorization that lets applications request limited access to a user's data on an API service without sharing passwords. This overview highlights the core flows that Unified supports. OAuth itself has two major versions (1.0 and 2.0), and OAuth 2.0 defines several flows. Overall, Unified supports 70+ OAuth 2.0 variations across integrations, spanning integration-specific scope models, token formats, and authorization requirements. We won't cover OAuth v1, which is no longer widely used and has largely been superseded by OAuth 2.0, published as RFC 6749 in October 2012. RFC 6749 states it "replaces" OAuth 1.0, though OAuth 1.0 (RFC 5849) remains published and is not formally deprecated.[[1]](https://rfc-editor.org/info/rfc6749)[[2]](https://rfc-editor.org/info/rfc5849) [OAuth 2.1](https://oauth.net/2.1/) is still in progress (as of Oct 2025). It consolidates best practices from OAuth 2.0—removing deprecated flows like implicit and password, and enforcing stronger security defaults such as PKCE—so it is not fully backwards-compatible with every 2.0 flow. --- ## OAuth 2.0 Authorization Code Flow The OAuth 2.0 Authorization Code flow (`code` flow) is designed for applications that need to access a user's data in a third-party service (e.g., Google, Salesforce, HubSpot) with the user's explicit consent. It's the standard behind options you see all over the internet like "Sign in with Google." This flow is used for delegated authorization—API access on behalf of a user—and can also support authentication when combined with OpenID Connect. **How it works:** - Your app redirects the user to the integration's authorization page. This URL contains your app's `client ID` from that integration, plus integration-specific permission `scopes` and a `redirect URL`. It can also contain additional information such as `state`, `PKCE challenge`, and many more integration-specific parameters. - The end-user logs in to that integration (if not already logged in) and is shown what permissions your app is requesting. They can then approve or reject that access request. - If approved, the integration redirects back to your app with a temporary code. - Your backend exchanges the code for an access token (and optionally a refresh token). This step uses both the client ID and client secret, securing the transaction. The access token will have an expiry, which requires the use of the refresh token to obtain new access tokens. - Your app uses the access token in the `Authorization` header to call that integration's API on behalf of the user. **Unified.to** [handles all aspects of this OAuth2 flow](https://docs.unified.to/unified/integration/Authorize_new_connection): - creating the authorizing URL correctly - unifying permission scopes (e.g. crm_contact_read, accounting_invoice_write, …) - exchanging codes for access tokens - automatically refreshing access tokens (and optionally refresh tokens) - calling the end API with the access token in the correct header and format This happens with our pre-built components, or directly through our Authorization API. Our customers just input their application's OAuth2 client ID and client secret, obtained once from the integration. Unified also provides instructions on how to obtain those OAuth credentials. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/understanding_oauth2_authorization_flows-0.png) Currently, Unified.to supports 176 integrations that use OAuth2 `code` flow. ## OAuth 2.0 OpenID Connect (OIDC) Flow The [OpenID Connect](https://openid.net/developers/how-connect-works/) flow is built on top of OAuth 2.0 and is used primarily for user authentication (identity), often alongside OAuth 2.0 authorization for API access. It returns an `id_token`—a JWT (JSON Web Token) containing verified user identity claims, such as a name and email—and can also provide a `/userinfo` API endpoint to obtain the verified identity. Depending on the integration and the scopes requested (for example, `offline_access`), it can also issue access and refresh tokens. When you see a "Sign in with Google" button, that is usually an OpenID Connect flow. **Unified.to** [hides this complexity in our Unified Authentication API](https://docs.unified.to/auth/login/Sign_in_a_user), supporting OIDC along with integrations that only support OAuth2 code flow with non-standard `User Info` API endpoints. ## OAuth 2.0 Client Credentials Flow OAuth 2.0 `client credentials` flow is for server-to-server (machine-to-machine) authentication. No user is involved; your backend service authenticates as itself to access application-level or tenant-level data—not an individual user's data. The customer (or a tenant admin) obtains a client ID and client secret from their integration and provides it to your application. **How it works:** - The customer or tenant admin generates an OAuth2 client ID and client secret in their integration and gives it to your application. They usually set what permissions they're willing to grant to those credentials. - Your backend sends the client ID and secret to the integration's token endpoint. - The integration returns an access token. - Your backend uses the token to call APIs. With Unified.to, your end-user will be asked to fill out these credentials, plus any other required credentials to access that integration's API. ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/understanding_oauth2_authorization_flows-1.png) Currently, Unified.to supports 29 integrations that use OAuth2 `client_credentials` flow. ## API Token / Key / Username & Password Authentication APIs can also be authenticated with an `API key`, `API token`, or even a `username and password`. The keys and tokens consist of either random characters or an encoded [JWT](https://www.jwt.io/) (JSON Web Token). They can be sent in the `Authorization` header, in a custom header, or in the URL query as a parameter. If an API uses username and password for authentication instead of an API key, that combination is sent in the `Authorization` header, base64-encoded as "username:password". Because these credentials must be kept secret, they should only be used in backend environments. Using them in client-side code (e.g., a browser) would risk leaking them to the end-user. Unified.to can ask the end-user for any number of API keys/tokens and additional required API credentials. Optionally, our customers can request this information from their end-users and [create a connection manually](https://docs.unified.to/unified/connection/Create_connection). ![image.png](https://s3.us-east-2.amazonaws.com/unified-article-images/understanding_oauth2_authorization_flows-2.png) If you're interested in reading about more complexities with OAuth2, read our blog post: "[How We Normalize OAuth Across 460+ APIs at Unified.to](https://unified.to/blog/how_we_normalize_oauth_across_apis_at_unified)". ## Understanding Response Time Headers in the Unified API URL: https://docs.unified.to/guides/understanding_response_time_headers_in_the_unified_api # Understanding Response Time Headers in the Unified API ------ _July 13, 2026_ Every response from the Unified API includes custom HTTP headers that break down exactly where time was spent during your request. This gives you visibility into whether latency is coming from the third-party integration or from Unified's processing layer. ### The Headers **`X-Response-Time`** — The total end-to-end time for your request, measured from when the Unified API received it to when the response was sent back. **`X-EndApi-Response-Time`** — The time spent waiting on the third-party API (e.g., Salesforce, Slack, HubSpot). This is the network round-trip to the downstream service plus its processing time. **`X-Unified-Response-Time`** — The time spent inside Unified's own code — authentication, data mapping, validation, and response formatting. Calculated as the total time minus the end API time. ### When They Appear `X-Response-Time` is included on every response. The two breakdown headers (`X-Unified-Response-Time` and `X-EndApi-Response-Time`) only appear when your request involved a call to a third-party API. Requests that are handled entirely by Unified (such as validation errors or metadata lookups) will only include `X-Response-Time`. ### Example ```plain text HTTP/1.1 200 OK X-Response-Time: 340ms X-Unified-Response-Time: 12ms X-EndApi-Response-Time: 328ms ``` In this example, the third-party API took 328ms to respond, while Unified added just 12ms of overhead for auth, field mapping, and validation. ### How to Use This - **Debugging slow requests** — If `X-EndApi-Response-Time` is high, the bottleneck is the third-party provider, not Unified. You may want to check the provider's status page or consider caching on your end. - **Monitoring** — Log these headers alongside your own application metrics to build a complete picture of your API call latency. - **Support** — When reaching out about performance concerns, include these header values to help pinpoint the issue faster. ## Unified Assessment API: Embed Assessments Inside ATS Platforms URL: https://docs.unified.to/guides/unified_assessment_api_embed_assessments_inside_ats_platforms # Unified Assessment API: Embed Assessments Inside ATS Platforms ------ _February 9, 2026_ Most assessment integrations rely on application status changes or external tools. Recruiters switch systems, and providers handle fragmented integration logic. Some ATS platforms now support embedded assessment listings, where recruiters select and order assessments directly inside the ATS. Unlike traditional integrations that rely on polling or status changes, the Assessment API enables **real-time, recruiter-initiated assessment requests executed directly inside ATS platforms.** ## What is an Assessment API? An assessment API allows assessment providers to integrate directly into ATS platforms so recruiters can request candidate assessments without leaving the ATS. Instead of triggering assessments based on status changes, recruiters select assessment packages inside the hiring process, and the request is routed to the provider in real time. The Assessment API supports both **embedded ATS integrations and** including package management, order handling, and result submission. ## Why ATS-native assessment integrations matter Embedded assessment integrations increase completion rates and reduce friction because recruiters take action at the point of decision. There is no context switching, and results are written back to the system where hiring decisions are made. ## Three ways to integrate assessments with Unified **1. Embedded (Assessment API)** - Recruiter selects assessment inside ATS - Best UX, requires ATS support **2. Triggered (ATS API)** - Trigger on status change - Works across all ATS **3. Verification-style (Verification API)** - ATS/HRIS calls provider directly - No marketplace required Most assessment providers use a combination of all three to balance coverage and user experience. ## Unified Assessment API The Unified Assessment API allows assessment and background check providers to integrate directly with Applicant Tracking Systems (ATS) platforms that support embedded assessment listings. This API enables recruiters to request assessments for candidates directly from within their ATS. The recruiter does not need to log into a separate assessment platform or use a separate system. Instead, the assessment package is selected inside the ATS, and the request is routed to your platform through Unified. This is different from triggering assessments based on status changes. It is a recruiter-initiated flow inside the ATS itself. ### Core objects in the Assessment API - **Packages**: define assessment offerings, metadata, scoring, and configuration - **Orders**: represent assessment requests, candidate context, and lifecycle state - **Responses**: structured results including scores, attributes, and artifacts Orders include rich candidate, job, and application context, allowing providers to tailor assessments and return structured results directly into the ATS. ### Real-time, read/write assessment workflows The Assessment API supports both: - Creating and managing assessment packages - Receiving assessment orders in real time - Updating orders with structured results and scores All requests are **stateless and routed directly to the source system**—no caching, no sync jobs, no stored records. ### **Using assessment data in AI systems** Because assessment results are structured and returned in real time, they can be used to: - Generate candidate summaries and scoring insights - Feed structured evaluation data into copilots or hiring assistants - Trigger automated follow-ups or recommendations - Train models on hiring outcomes and performance signals ## What you can build with the Assessment API - Embedded assessment marketplaces inside ATS platforms - Partner integrations with ATS vendors (Greenhouse, Ashby, Workable) - Recruiter-driven assessment workflows - Real-time candidate evaluation pipelines - AI scoring and feedback workflows tied to ATS activity ### Embedded assessment marketplaces inside ATS Some ATS platforms support native assessment marketplaces, where providers can list assessment packages directly inside the product. The Assessment API enables this model by: - Listing your packages inside the ATS - Allowing recruiters to select assessments directly - Sending orders in real time - Returning results back into the same flow ## **How the Assessment API differs from other Unified APIs** The **Unified Verification API** is used by ATS or HRIS platforms to call verification or assessment providers in a unified way. In that flow: - The ATS or HRIS platform initiates the request. - They do not need a direct partnership with the assessment provider. - The call is routed through Unified using the unified verification data models. The Assessment API is different. It is designed for assessment providers who want their assessment packages listed directly inside an ATS that supports embedded assessment integrations. The integration is initiated by the recruiter inside the ATS, not by the assessment provider or backend workflow. ### How This Differs from the Unified ATS API The **Unified ATS API** allows assessment providers to listen for job application changes and trigger an assessment when an application reaches a specific status. That flow works across most ATS platforms supported by Unified and does not require a special assessment marketplace integration. It is best suited for ATS platforms that do not support embedded assessment listings. The new Unified Assessment API works in a different way: - It connects directly to ATS platforms that have a dedicated 'assessment API.' - Your assessment packages are listed inside the ATS. - A recruiter manually selects a package for a candidate. - The ATS sends an assessment order through Unified. - You process the assessment. - You update the order. - The results are written back into the ATS. The key difference: The recruiter stays inside the ATS the entire time. There is no external trigger based on status change. The recruiter explicitly orders the assessment. ### When Should You Use Each? For assessment providers: - Use the **Assessment API** when the ATS supports embedded assessment listings and you have a partnership with that ATS. - Use the **ATS API** for all other ATS platforms. Most providers will use both: - Assessment API for supported ATS platforms. - ATS API as the broad coverage fallback. The Assessment API reduces recruiter friction but is limited to ATS platforms that expose this functionality. ### Who this is for - Assessment providers (coding tests, behavioral, psychometric, etc.) - Background check and verification providers - AI-based candidate evaluation platforms - Hiring infrastructure tools integrating into ATS ecosystems ## Why this matters for assessment providers - Higher completion rates (recruiter intent is explicit) - Less friction (no external tools) - Stronger ATS partnerships - Better UX for hiring teams ## Supported ATS Integrations Currently, the Unified Assessment API supports the following ATS platforms: - Ashby - Cornerstone - Greenhouse - Recruitee - TalentLyft - Workable Each of these has a dedicated assessment integration that is separate from the standard ATS integration. Only ATS platforms that support embedded assessment listings can be supported through this API. Expansion to additional supported ATS platforms is ongoing. ## Getting Started ### Step 1: Create a Connection First, you need to manually create a connection for your assessment integration. This connection will be used to authenticate requests from the ATS. When creating the connection, you must supply a **Partner API Key** in the `connection.auth.token` field that you generate yourself. You will then share this API key with your end-customer to configure the integration in their ATS. Always check the authentication requirements for each assessment integration as some will require additional authentication information. For example, Ashby also requires a `Customer API Key` and a `Partner ID`. **Example Connection Creation:** ```json { "integration_type": "greenhouseassessment", "auth": { "token": "YOUR_PARTNER_API_KEY" }, } ``` ### Step 2: Configure Webhooks You also need to set up webhooks to receive assessment orders from the ATS systems. ### Required Webhook Create a webhook with the following configuration: - **object_type**: `assessment_order` - **event**: `created` This webhook will be triggered whenever a recruiter requests an assessment for a candidate in the ATS. ### Optional Webhook Some ATS vendors (like Ashby) also send order cancellation events. You can optionally create an additional webhook: - **object_type**: `assessment_order` - **event**: `deleted` This webhook will notify you when an assessment order is cancelled. ### Step 3: Provide Configuration to Your End-Customer Once you've created the connection and webhook, provide the following information to your end-customer: 1. **Base URL**: `https://api.unified.to/assessments/{connection_id}` - Replace `{connection_id}` with the actual connection ID from Step 1 2. **Partner API Key**: The token value you set in `connection.auth.token` Your end-customer will then input these credentials into their ATS software to enable the integration. ## **How the assessment flow works** Here's how the assessment flow works: 1. **Package Configuration**: You create assessment packages via the Unified API (w/ assessment packages API endpoints) 2. **ATS Lists Packages**: The ATS queries your available packages 3. **Recruiter Orders Assessment**: A recruiter selects a package and orders an assessment for a candidate 4. **Webhook Notification**: [Unified.to](https://unified.to/) sends you a webhook with the order details 5. **Process Assessment**: You send the assessment to the candidate and process their response 6. **Submit Results**: You submit the assessment results back to [Unified.to](https://unified.to/) (w/ assessment order update API endpoint) 7. **Results in ATS**: The results appear in the ATS for the recruiter to review ## API Endpoints ### Assessment Packages Manage your available assessment packages: - `POST /assessment/{connection_id}/package` - Create a new package - `GET /assessment/{connection_id}/package` - List all packages - `GET /assessment/{connection_id}/package/{id}` - Get a specific package - `PUT /assessment/{connection_id}/package/{id}` - Update a package - `DELETE /assessment/{connection_id}/package/{id}` - Delete a package ### Assessment Orders Update assessment order results: - `PUT /assessment/{connection_id}/order/{id}` - Update an order with results ## Webhook Payload When an assessment order is created, you'll receive a webhook with an `AssessmentOrder` payload: ```json { "id": "123", "package_id": "your_package_id", "candidate": { "candidate_id": "candidate_456", "email": "candidate@example.com", "first_name": "John", "last_name": "Doe", "phone": "+1 123 456 7890" }, "application": { "id": "application_789" }, "job": { "id": "job_012", "name": "Software Engineer", }, "status": "OPEN" } ``` ## Submitting Results Once a candidate completes an assessment, submit the results using the update order endpoint: ```bash PUT /assessment/{connection_id}/order/{order_id} ``` **Example Request:** ```json { "status": "COMPLETED", "score": 85, "max_score": 100, "result_url": "", "completed_at": "2024-01-15T10:30:00Z", "attributes": [ { "type": "TEXT", "label": "Overall Assessment", "value": "Strong candidate" } ] } ``` The results will then be written back to the ATS system, where recruiters can view them directly. ## Best Practices Unlike traditional integrations that rely on polling or status changes, the Assessment API enables real-time, recruiter-initiated workflows directly within ATS platforms. 1. **Secure Your API Key**: Treat your Partner API Key as sensitive information. Only share it with trusted end-customers. 2. **Handle Webhooks Reliably**: Implement retry logic and idempotency checks in your webhook handler to ensure you don't miss or duplicate orders. 3. **Validate Webhook Data**: Always validate the webhook payload to ensure it contains the expected candidate and job information before processing. 4. **Monitor Order Status**: Track the status of assessment orders and handle cancellations appropriately when the `deleted` event webhook is received. ## Support For questions or issues with the Unified Assessment API, please contact our support team or refer to the [Unified.to Documentation](https://docs.unified.to/). What is an assessment API for recruiting platforms? An assessment API allows providers to integrate directly with ATS platforms so recruiters can request, manage, and review candidate assessments without leaving the ATS. It enables real-time order handling and structured results returned to the same system. How do assessment providers integrate with ATS platforms? Assessment providers integrate with ATS platforms either through embedded assessment APIs or through status-based triggers. Embedded integrations allow recruiters to select and order assessments directly inside the ATS, while triggered integrations operate based on application state changes. ## Unified's MCP Server URL: https://docs.unified.to/guides/unified_mcp_server # Unified's MCP Server ------ _May 24, 2025_ Unified has launched an MCP server that connects any Unified connection to LLM (Large Language Model) providers supporting the newest MCP protocols. The available MCP `tools` will be determined by the integration's feature support and the connection's requested permissions. All MCP tool calls are executed live against the source API. Unified does not cache or store end-customer data, and access is governed by scoped permissions and tool allow-listing. ## Use-cases: - **End-Customer Conversation with LLMs**: Allows end-customers to interact with an LLM using permissioned tools backed by the data they've explicitly authorized through a Unified connection. - **Debugging End-Customers' Connections**: Allows your product and support teams to assist your end-customers by debugging their Unified connections. Please make sure that you observe all privacy policies, both yours and your regions's, since end-customer data may be made available to the LLM based on the permissions and tools you explicitly configure. Unified provides options to restrict tools, scope permissions, and remove sensitive fields from responses. MCP is a new protocol and it is moving fast. We expect more LLM & agent clients to support its newer Streamable HTTP transport protocol. We also expect that the MCP protocol will continue to expand quickly. Stay tuned as we also keep up. ## Sample queries: - List candidates for job X - List deals for contact with email address joe@foo.com - update application 12345 with a status of HIRED - summarize the accounting balance sheet report - find the vacation policy by listing the pages in Notion - post a message in Slack summarizing this deal - create an invoice for customer X - schedule an interview and notify the candidate More information can be found at [https://docs.unified.to/mcp](https://docs.unified.to/mcp) ## Unlock real-time data with virtual webhooks URL: https://docs.unified.to/guides/unlock_real_time_data_with_virtual_webhooks # Unlock real-time data with virtual webhooks ------ _February 13, 2025_ Product teams building real-time applications face a common challenge: most third-party APIs don't offer webhooks (In fact, our [State of SaaS APIs report](https://unified.to/blog/the_state_of_saas_apis_2024) found only 11% of APIs have built-in support for webhooks). This leads to inefficient polling, manually handling rate limits, and writing custom logic just to keep data fresh. But what if you could control webhook updates while reducing API costs and complexity? That's exactly what Unified.to's [virtual webhooks](https://unified.to/blog/introducing_powerful_enhancements_to_webhooks) enable. In this article, we'll review: - How virtual webhooks work - Why they help control API costs by managing request frequency - How they eliminate the need for custom rate-limit handling systems - Why virtual webhooks are the fastest way to implement real-time updates ## Why use virtual webhooks? ![virtual_webhooks.png](https://s3.us-east-2.amazonaws.com/unified-article-images/unlock_real_time_data_with_virtual_webhooks-0.png) Pairing Unified.to's virtual webhooks with your product's integration strategy means: - **Webhooks for nearly every integration** – Get real-time updates even from APIs that don't natively support webhooks, all through a unified API. - **Cost-efficient API usage** – Unlike native webhooks, which can be unpredictable in frequency and costly, virtual webhooks control API call frequency, reducing overages and optimizing usage. - **One unified schema** – Whether the source supports native webhooks or requires polling, data is normalized into a consistent format—no custom polling logic needed. - **Optimized sync performance** – Intelligent update detection, seamless and automatic rate-limit handling, and configurable polling intervals (e.g., every minute, every 30 minutes, every hour). - **Faster time-to-market** – Virtual webhooks eliminate the need to build custom data collection. - **Historical data access** – Instantly access past records when creating a webhook subscription, eliminating the need for separate API calls. - **Built-in observability** – Monitor webhook activity with logs, audit trails, and health tracking. - **Event-driven AI and automation** – Trigger AI workflows and automation in response to real-time data changes. Instead of dealing with a patchwork of webhook, polling, and batch sync solutions, Unified.to's virtual webhooks create a consistent, scalable way to access real-time data across your integrations. ## How virtual webhooks work Webhooks provide a push-based way to receive data updates, eliminating the need for polling. But most APIs don't support them, which forces developers to either: 1. Poll the API (which wastes API calls and delays updates), or 2. Manually mix polling and webhooks (adding complexity and inconsistencies). Unified.to's virtual webhooks solve this by mimicking native webhooks, even when the source API doesn't provide them: - We monitor your customers' connections on a scheduled interval that you configure (e.g., as frequently as every minute) and detect changes automatically - We push updates to your webhook endpoint, just like a native webhook would - You subscribe and manage webhooks the same way you would with a real webhook No polling logic. No wasted API calls. Just real-time updates, everywhere. [Explore documentation](https://docs.unified.to/guides/how_to_create_and_configure_webhooks) ## How virtual webhooks compare to traditional webhooks and polling Most third-party APIs don't offer native webhooks, forcing teams to choose between batch syncs, polling, or manual workarounds. | **Feature** | **Unified's Virtual + Native Webhooks** | **Native Webhooks** | **Polling** | | ----------------------------------------------- | --------------------------------------- | ------------------- | ---------------------- | | Works even if API doesn't support webhooks | ✅ Yes | ❌ No | ✅ Yes, but inefficient | | Real-time event updates | ✅ Yes | ✅ Yes | ❌ No, delayed | | API rate limit friendly | ✅ Yes | ✅ Yes | ❌ No, high API usage | | Historical data retrieval | ✅ Yes | ❌ No | ❌ No | | Fine-grained sync control | ✅ Yes (adjust polling frequency) | ❌ No | ✅ Yes, but manual | | Built-in error handling & retries | ✅ Yes | ❌ Varies by API | ❌ No | | Webhook observability (logs, health monitoring) | ✅ Yes | ❌ Varies by API | ❌ No | Virtual webhooks provide all the benefits of native webhooks, even when APIs don't support them, while eliminating the inefficiencies of polling. ## Product use cases Let's look at how developers and product teams can leverage virtual webhooks in real-world applications. **AI-powered customer support** Virtual webhooks can update AI models in real-time when new support tickets are created. Instead of polling a help desk API every few minutes, an AI assistant can instantly respond to customer inquiries or escalate issues. [Unified Ticketing API](https://unified.to/ticketing) [Build an AI Support Bot](https://unified.to/blog/how_to_build_a_discord_support_bot_with_unified_and_langbase) **Automated financial reporting** Fintech and accounting applications rely on up-to-date financial data to generate reports, sync transactions, and reconcile accounts. However, many accounting platforms lack real-time webhook support, meaning transaction updates often rely on batch syncs or polling. With Unified.to's virtual webhooks, accounting software can: - Get notified instantly when new transactions, invoices, or payments are recorded - Trigger automatic reconciliation workflows instead of waiting for scheduled syncs [Unified Accounting API](https://unified.to/accounting) **Recruitment automation** Hiring teams often integrate with multiple ATS platforms—some of which lack real-time event support. Virtual webhooks enable AI-powered candidate screening to kick in the moment a new application is received, ensuring faster and more efficient hiring workflows. [Unified ATS API](https://unified.to/ats) [How HeroHunt added 20 integrations in 1 week](https://unified.to/blog/how_herohunt_saved_ten_months_of_engineering_time_with_unified) ## How much polling actually costs Polling isn't just inefficient—it's expensive. Example: - Polling every 5 minutes on 1000 accounts = 288,000 API requests per day - Polling every 1 minute on 1000 accounts = 1.4 million API requests per day - With virtual webhooks, API requests are dramatically reduced, as data is only fetched when changes occur. In low-activity accounts, this can mean near-zero requests, while high-activity accounts still see significant reductions compared to constant polling. Instead of burning API quota on polling, virtual webhooks ensure data flows only when necessary, dramatically reducing costs and improving responsiveness. ## What happens if you don't use virtual webhooks? - You could hit API rate limits while continuously polling for updates - You could rely on batch syncs, leading to outdated or incomplete data - You could spend months building and maintaining custom webhook fallbacks for every integration Or… you could just use Unified.to's virtual webhooks to handle rate limits and get real-time updates across all your integrations. ## Start using Unified.to's virtual webhooks Ready to stop polling and start working with real-time data everywhere? Here's how to get started: - [Start a free trial](https://app.unified.to/login) to access virtual webhooks - [Join our Discord](https://discord.gg/85z7HF7JbD) to connect with our team and get support - [Review our documentation](https://docs.unified.to/guides/how_to_create_and_configure_webhooks) for step-by-step guidance on setting up webhooks - [See our changelog](https://unified.to/changelog) for virtual webhook enhancements ## Use Unified to sign in your users into your application URL: https://docs.unified.to/guides/use_unified_to_sign_in_your_users_into_your_application # Use Unified to sign in your users into your application ------ _August 31, 2023_ Before your users can utilize your application and any of Unified.to's integrations, they need to sign in to your application. Luckily, we've simplified user authentication through our Unified Authentication API. Just follow these steps. ## 1. Activate Authentication integrations 1.1 Click on the 'Integrations' menu, and then on the 'Authentication Integrations' option. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/use_unified_to_sign_in_your_users_into_your_application-0.png) 1.2 Activate the integrations that you would like to use to sign-in your users into your application by clicking on each desired integration. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/use_unified_to_sign_in_your_users_into_your_application-1.png) 1.3 If available, you can chose to use Unified's branding and name on the authentication page that your users see with signing-in to your application. We don't recommend this for production applications, but this is a great option to test the authentication feature. 1.4 If you want to add your own branding to the authentication pages, then you will need to first obtain your own OAuth2 application credentials from the specific integration vendors (eg. Google). For help on creating your own OAuth2 application, please see [Generating OAuth2 Credentials](https://unified.to/blog/start_here_how_to_generate_oauth2_credentials) or click on the 'Get your own OAuth2 credentials' link. Once you have that, enter your client ID and client secret that you obtained from the integration vendor. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/use_unified_to_sign_in_your_users_into_your_application-2.png) 1.5 Click on the 'Activate' button to enable this integration. 1.6 Repeat for all of the integrations that you want to use to authenticate your users. ## 2. Display sign-in links to your users Now that you have a list of Authentication connections, you can incorporate them into your application. You have two options: 1. Remember the integration types (ie. google, discord, workday) and create links that point to our authentication API endpoint: `https//api.unified.to/unified/integration/login/{workspace_id}/{integration_type}?redirect=true`. You can style that link however you like. 2. Call the our API that returns a list of integrations; `https//api.unified.to/unified/integration/workspace/{workspace_id}?summary=true&active=true&categories=auth`. This will return all activated Authentication integrations and each will have information that you can use to style your Sign-in links; ```json [ { logo_url: "https://localhost:8000/docs/images/google.png", name: "Google", type: "google" } ] ``` The Sign-in URL can have the following optional parameters: `redirect` - `true` to redirect the user or if empty, will return the URL as string in its response `success_redirect` - the URL that the user will be redirected to once they have successfully authenticated with that integration vendor `failure_redirect` - the URL that the user will be redirected to if there is an error or other issue preventing the user from being authenticated by the integration vendor `state` - a string that will be sent back to the success_redirect. You can use this to remember a user ID or other identifier. 1. An alternative to building your own Sign-in page is to use our Embedded Sign-in widget. Click on the Settings menu and then on the Embedded Sign-in option. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/use_unified_to_sign_in_your_users_into_your_application-3.png) Copy the CSS and code and insert it into your own application. ## Step 3: Verify a sign-in attempt Once the user clicks on the authentication button/link, they will be redirect to the integration vendor's authentication page. ![Untitled.png](https://s3.us-east-2.amazonaws.com/unified-article-images/use_unified_to_sign_in_your_users_into_your_application-4.png) btw: it is important to note that the authentication pages are not Unified's pages but that of the integration vendor. Unified never sees the user's login credentials including their password. Once the user successfully signs-in, they are redirected back to your application (or to the location of the `success_redirect` URL parameter from step 2). A `jwt` parameter will be appended to that URL. The `jwt` is a base64 encoded [JWT](https://jwt.io/) and is signed with your Workspace Secret. Verify the JWT with your workspace secret on your server (NOT in your browser as the workspace secret is not public). Example code to verify a JWT on a NodeJS server: ```javascript try { let result = JWT.verify( req.payload.jwt, workspace.secret ) } catch (err) { console.error(err); } ``` The decoded JWT will contain a name and emails field: ```json { name: "Jane Smith", emails: ["jane@foo.com", "jsmith89@gmail.com"] } ``` Use the user's email to log them into your application as it is verified by the integration. ## Workday vs Workday Legacy — Why Unified.to Supports Both URL: https://docs.unified.to/guides/workday_vs_workday_legacy_why_unified_supports_both # Workday vs Workday Legacy — Why Unified.to Supports Both ------ _July 22, 2025_ Workday provides **two main API integration formats**: 1. **Workday REST API** — their latest modern API, but still under active development. 2. **Workday SOAP API** (often referred to as _Legacy_) — the older but mature API that still covers some critical endpoints not yet available in REST. To give you maximum flexibility and to ensure you can access **all the data and actions you need**, Unified.to supports **both** APIs. ## Why You Might Need Both While Workday is steadily improving their REST API, there are still a few important gaps. Here's why using both makes sense today: 1. **Application Creation** — Workday's REST API does not currently support creating job applications. This is only available through the Legacy SOAP API. 2. **Candidate Lists** — Retrieving a full list of candidates is not yet supported in REST but is fully available through the Legacy SOAP API. 3. **Create Job** — Similar to applications, creating jobs still relies on the Legacy API. 4. **Attachments** — The REST API **does** support listing and downloading attachments — which the Legacy SOAP API does not. 5. **Future Coverage** — Workday continues to expand REST API features. As new endpoints become available, Unified.to will adopt them immediately so you always have the best option. | | WorkDay REST | WorkDay Legacy | | ------------------ | --------------------------- | ------------------------- | | ATS Activity | List | | | ATS Application | | List, Get, Create, Update | | ATS Candidate | Create, Get | List, Get, Create, Update | | ATS Document | List, Get, Create, Download | | | ATS Interview | List, Get | List, Get, Create, Update | | ATS Job | List, Get | List, Get, Create, Update | | ATS Scorecard | List, Get | List, Get, Create, Update | | HR Employee | List, Get | List, Get, Create, Update | | HR Group | List, Get | | | HR Timeoff | List, Get | | | Accounting Contact | List, Get | | | Accounting Invoice | List, Get | | ## Frequently Asked Questions & Answers **Question: Do I have to send XML requests for Workday Legacy?** **Answer:** No! You always send JSON requests to Unified.to. We handle the conversion to XML behind the scenes for the SOAP API. **Question: Can I use both the REST and Legacy APIs at the same time?** **A****nswer:** Absolutely. Unified.to lets you mix and match endpoints. For example, you can fetch attachments with REST and create applications with Legacy — all through the same unified model. **Question: Is the Workday Legacy connector going to be deprecated?** **Answer:** Not anytime soon. Many Workday customers still rely on SOAP endpoints for critical operations. Unified.to will continue supporting it as long as Workday does. **Question: Is authentication different for Legacy vs REST?** **Answer:** Yes — Workday REST typically uses OAuth 2.0, while the Legacy SOAP API uses basic authentication (aka WS-Security). Unified.to handles this complexity for you. **Question: Are the data models the same between REST and Legacy?** **Answer:** Yes — Unified.to normalizes the data, so you get a consistent unified schema, no matter which Workday API is behind the scenes. **Question: Can I switch from Legacy to REST later?** **Answer:** Yes. As Workday expands REST support, you can migrate endpoints over time. Our team will help you adapt with minimal changes on your side. **Question: Will using both cost more?** **Answer:** No — there's no extra fee for using both. You only pay for your overall Unified.to usage. ## Conclusion Workday is gradually moving more features to its modern REST API — but some critical workflows still require the mature SOAP-based Legacy API. To ensure you don't lose any capability, Unified.to supports **both** — giving you the best of both worlds. **Our recommendation:** Use the REST API wherever possible for modern features and attachments, and rely on the Legacy API to cover any gaps (like creating jobs or applications). Unified.to handles all the complexity behind the scenes, so you can build faster with no surprises. **Have more questions?** Reach out to our support team — we're here to help you get the best out of both Workday APIs! ## Working with hierarchical tree data in Storage, Messaging, and KMS APIs URL: https://docs.unified.to/guides/working_with_hierarchical_tree_data_in_storage_messaging_and_kms_apis # Working with hierarchical tree data in Storage, Messaging, and KMS APIs ------ _November 7, 2024_ This guide explains how to retrieve and traverse hierarchical data structures (i.e., trees) when working with APIs that support parent-child relationships. ## Overview Many APIs organize their data in tree-like structures, where records can have parent-child relationships with each other. 1. The **File Storage API** represents both folders and files as a single object type, making tree traversal straightforward. While you can think of folders as the 'containers' for other files and folders, the API treats them all as the same object type. 2. The **Messaging API** and **Tasks API** use a simple parent-child relationship where each object type can contain children of the next type (e.g., projects contain tasks, tasks contain comments, channels contain messages). Objects may also have parents of the same type. 3. Some **KMS APIs** have more complex relationships where pages and spaces can reference each other in multiple directions e.g., a mesh. While most KMS platforms use a simple tree structure (like Confluence), some (like Notion) support this more flexible mesh-like organization. This guide will show you how to efficiently traverse these hierarchical structures to retrieve all the data you need. ## Before you begin This guide assumes you have: - Basic understanding of [tree data structures](https://en.wikipedia.org/wiki/Tree_(abstract_data_type)) ## Understanding parent-child relationships When an API endpoint supports a `parent_id` parameter, it typically indicates that the data is organized in a hierarchical structure. To fully traverse this structure, you'll need to: 1. Get the top-level entities (parent nodes) 2. For each parent, retrieve its children by passing the parent's ID 3. Recursively repeat this process for any children that can also be parents 4. For KMS Page, you can use its `has_children` field to determine if there are children pages to query. If `has_children` is equal `false` , then no children pages exist. Some integrations di not return this information, so if this field is `undefined` or missing, then you should treat it as a `true` ### Example: Messaging platforms Let's look at how this works with Discord: 1. Discord servers (also called "guilds") are top-level parents 2. Channels exist within servers as children 3. Messages are also related to channels through their `parent_id` 4. To get all channels: - First retrieve all guilds (servers) - Then get channels for each guild using the guild's ID as the `parent_channel_id` ```javascript async function getAllChannels(connectionId) { // Get all top-level guilds first const guilds = await sdk.messaging.listMessagingChannels({ connectionId, }); let allChannels = []; // For each guild, get its channels for (const guild of guilds.data) { const channels = await sdk.messaging.listMessagingChannels({ connectionId, parentChannelId: guild.id, }); allChannels = allChannels.concat(channels.data); } return allChannels; } ``` **API reference:** [Messaging API](https://docs.unified.to/messaging/overview) ### Example: File storage systems File storage systems are another common example of hierarchical data. Here's how to recursively traverse a file system to get all files: ```javascript async function getAllFiles(connectionId, parentId) { const response = await sdk.storage.listStorageFiles({ connectionId, parentId, }); if (!response.data) { return []; } let allFiles = []; for (const item of response.data) { if (item.type === 'FOLDER') { // Recursively get files from subfolders const subfolderFiles = await getAllFiles(connectionId, item.id); allFiles = allFiles.concat(subfolderFiles); } else { allFiles.push(item); } } return allFiles; } ``` **API reference:** [File](https://docs.unified.to/storage/overview)[ ](https://docs.unified.to/storage/overview)[Storage API](https://docs.unified.to/storage/overview) ## Best practices When working with hierarchical data: 1. **Implement pagination**: Some nodes might have many children. Use the `limit` and `offset` parameters to handle large datasets. ```javascript async function getAllChannelsWithPagination(connectionId, parentId = undefined) { let allChannels = []; let offset = 0; const limit = 100; while (true) { const response = await sdk.messaging.listMessagingChannels({ connectionId, parentChannelId: parentId, limit, offset, }); if (!response.data || response.data.length === 0) { break; } allChannels = allChannels.concat(response.data); offset += limit; } return allChannels; } ``` 1. **Handle rate limits**: When recursively fetching data, you might hit API rate limits. Implement backoff strategies and respect the provider's limits. 2. **Cache parent IDs**: If you need to traverse the same structure multiple times, consider caching the parent-child relationships to reduce API calls. ## Integration-specific gotchas ### Working with SharePoint SharePoint implements a distinct three-level hierarchy that requires special attention: 1. **Sites** (top level) 2. **Drives** (within sites) 3. **Folders/Files** (within drives) To work with SharePoint's file system: 1. First retrieve the site ID (top-most parent) 2. Use the site ID to list drives 3. Use the drive ID to create or access folders and files **Important notes:** - You cannot create files or folders directly at the site level - you must first select a drive - Write operations are only supported for folders and files, not for sites or drives - Pagination is not supported for sites and drives listings ## See also - [How to use the Unified File Storage API](https://docs.unified.to/guides/how_to_use_the_unified_file_storage_api#how-to-use-the-unified-file-storage-api) --- # Tutorials ## Build a simple Javascript app that calls the Unified.to API URL: https://docs.unified.to/tutorials/build-a-simple-javascript-app # Build a simple Javascript app that calls the Unified.to API In this tutorial, you’ll build a simple Javascript application that calls the Unified API to list candidates from Lever, an applicant tracking system (ATS). It assumes you have basic knowledge of: - HTML - Javascript - Web development By the end of this tutorial, you’ll be able to: 1. Activate integrations in Unified.to 2. Create connections to third-party API providers 3. Embed Unified.to's Authorization component to display integrations in your app 4. Call the Unified API and display the results ## Background [Unified.to](http://unified.to/) is a platform that provides a unified (i.e. single) API for integrating various software applications. In this tutorial, we'll connect to Lever, an Applicant Tracking System (ATS) used by recruiters and talent teams. An ATS like Lever helps companies manage their hiring process by organizing candidate information, job postings, and application workflows. However, oftentimes app developers will want to integrate with multiple vendors. Integrating directly with multiple platforms can be challenging due to differences in APIs and data models. This is where [Unified.to](http://unified.to/) comes in. It provides a standardized way to interact with multiple platforms through a unified API. This means you can write code once and integrate with multiple systems without having to learn each individual API. ## Prerequisites Before we begin, make sure you have: 1. [Signed up](https://unified.to/) for a Unified.to account 2. Completed our [Get Started](https://app.unified.to/start) guide in the app (optional, but recommended) 3. Downloaded a code editor of your choice, like VS Code 4. Installed NPM and Node.js on your system ## Project setup We've prepared a simple JavaScript and HTML application for you to build upon, which can find [here](https://github.com/unified-to/unified-ats-tutorial). This will serve as the foundation for our tutorial and we recommend that you download it and follow along. Run the commands below: ```js [shell] // inside the folder where you want to download the repo git clone git@github.com:unified-to/unified-javascript-tutorial.git cd unified-javascript-tutorial npm i ``` The app should now be available on `localhost:1234`. This repository contains a basic structure for a single-page application that we'll develop with Unified.to. Take a moment to familiarize yourself with the files we’ll be working with: - `index.html`: The main HTML file for our application. - `app.js`: Where we'll add our JavaScript code. It contains placeholders and predefined variables for your convenience. - `.env`: Where you’ll add sensitive information, like your API token. Open the folder in your preferred code editor. ## Step 1: Retrieve your workspace ID and API token from Unified.to You can find these values in your developer account under [_Settings > API Keys_](https://app.unified.to/settings/api). Record your Workspace ID and API Token in the `.env` file. ```js [.env] UNIFIED_WORKSPACE_ID = your_workspace_id_here; UNIFIED_API_KEY = your_api_token_here; ``` Now that you've updated `.env`, we can start our app. Run the following from the command line: ```js [shell] npm run start ``` You can view your app on `localhost:1234`. ## Step 2: Activate an integration in the Sandbox environment The Sandbox environment is where you can activate, explore, and try out integrations in a safe and non-destructive environment. We're going to call the ATS endpoint by the end of this tutorial, so let's activate an ATS integration. ### Check that you are in the Sandbox environment 1. Navigate to [_Integrations_](https://app.unified.to/integrations) 2. Look for the environment selector near the top of the page. Click on it and select 'Sandbox'. Any subsequent actions you take will happen in the sandbox environment. ### Activate Lever 1. On the same page (Active Integrations), search for 'Lever' by typing it into the search box. 2. Click on the Lever card to open its details page. 3. Leave the default settings as-is. Notice that since we’re in the Sandbox environment, the credentials are pre-filled with mock values. Lever uses OAuth 2 by default for authorization. 4. At the bottom of the page, click 'Activate’. The Lever integration is now activated in your Sandbox environment. This allows you to make test API calls and get a feel for the Unified API without needing to connect to a real Lever account. ## Step 3: Add the Authorization embedded component to your app The Authorization embedded component displays a list of your activated integrations. Your customers can use it to authorize access to third-party vendors for your app. We'll dynamically inject the widget script into the DOM using JavaScript, which allows us to use the environment variables we defined earlier. 1. Open `app.js` and add the following code: ```js [app.js] function embedAuthorizationComponent() { const script = document.createElement('script'); script.src = `https://api.unified.to/docs/unified.js?wid=${UNIFIED_WORKSPACE_ID}&did=unified_widget&env=Sandbox`; script.async = true; script.onload = () => { console.log('Unified.to component script loaded successfully'); }; script.onerror = () => { console.error('Failed to load Unified.to component script'); }; // The script will search the DOM for a div with the id "unified_widget" document.body.appendChild(script); } // Inject the widget on page load document.addEventListener('DOMContentLoaded', () => { embedAuthorizationComponent(); }); ``` 2. In `index.html`, add the following under `
` e.g: ```html [index.html]
``` Now, when your application loads, it will dynamically inject the [Unified.to](http://unified.to/) widget into the DOM. Refresh the page, you should see the widget will appear displaying a list of activated integrations. ## Step 4: Handle and store the connection ID When a user clicks on the Lever integration in the Unified.to widget, a new page will open where they will be asked to grant access to Lever before being redirected back to your app. The redirect URL contains important parameters, including `id` , which represents the Connection ID. We need to store this ID for making subsequent calls to the Unified API. Add the following code to `app.js` to handle the connection process: ```js [app.js] function handleConnectionCallback() { const urlParams = new URLSearchParams(window.location.search); const connectionId = urlParams.get('id'); if (connectionId) { console.log('New connection created:', connectionId); // Store the connectionId for later use localStorage.setItem('unifiedConnectionId', connectionId); } else { console.log('No connection ID found in URL parameters'); } } // Execute the handler when the page loads window.addEventListener('load', handleConnectionCallback); ``` **Warning:** Storing the Connection ID in `localStorage` is used here for simplicity and learning purposes only. In a production environment, you should securely store this information server-side, associated with the user's account. Refer to our guide on [How to Associate a Connection ID with your End-User](https://docs.unified.to/guides/how_to_associate_a_connection_id_with_your_end_user) for best practices. ## Step 5: Create and verify the connection Let’s walk through creating a connection to confirm that it works. ### Create the Connection 1. In the widget, click on the Lever integration. 2. **Authorization Flow**: - A new page will open with a disclaimer that you are on the Tester plan. - Click **Continue** to grant access to Lever. Note: The authorization flow is simplified in the Sandbox environment. In Production, your user will be shown the scopes you are requesting and asked to grant you access. 3. After granting access, you'll be automatically redirected back to your app. ### Verify the Connection Let's verify that the connection was created successfully: 1. **Check the URL**: - Look at the URL in your browser's address bar. - You should see a parameter `id` in the URL. This is your Connection ID. - For example, the URL might look like: `http://localhost:1234?id=abc123...` 2. **Console Output**: - Open your browser's developer tools. - Go to the Console tab. - You should see a message similar to: `New connection created: abc123...` - This confirms that our `handleConnectionCallback` function has captured the Connection ID. 3. **Verify Local Storage** (Optional): - In the developer tools, go to the Application tab. - Under Storage, select Local Storage and your domain. - You should see an item named `unifiedConnectionId` with the value matching the ID from the URL. ## Step 6: Set up API call to the Unified ATS Endpoint In this step, we'll implement the API call to fetch candidate data from the Unified ATS endpoint. ### Implement the API call function In `app.js`, add the following function to fetch ATS candidate data from the ATS endpoint: ```js [app.js] async function fetchATSCandidates(connectionId) { const options = { method: 'GET', url: `https://api.unified.to/ats/${connectionId}/candidate`, headers: { authorization: `bearer ${UNIFIED_API_KEY}`, }, params: { limit: 20, offset: 0, }, }; try { const response = await axios.request(options); return response.data; } catch (error) { console.error('Error fetching ATS candidates:', error); return null; } } ``` **API Reference:** [List all candidates](/ats/candidate/List_all_candidates) Notice that we don't need to specify `Sandbox` as an environment parameter in the API call - the connection we created is part of the Sandbox environment, so any data that comes back from it will be synthetic. ### Fetch and display candidates in the UI In `index.html`, add the following under `
` e.g: ```html [index.html]
    ``` ### Implement the click handler Add the following code to your `app.js` file to handle the button click and display the results: ```js [app.js] function displayCandidates(candidates) { const candidatesList = document.getElementById('candidates-list'); candidatesList.innerHTML = '

    Candidates:

    '; candidates.forEach((candidate) => { const candidateElement = document.createElement('li'); candidateElement.className = 'candidate-item'; candidateElement.innerHTML = `
    ${candidate.name || 'Candidate'} avatar

    ${candidate.name || 'N/A'}

    ${ candidate.emails?.[0]?.email || 'No email provided' }

    `; candidatesList.appendChild(candidateElement); }); } document.getElementById('fetchCandidatesBtn').addEventListener('click', async () => { const connectionId = localStorage.getItem('unifiedConnectionId'); if (!connectionId) { console.error('No connection ID found. Please connect to Lever first.'); return; } const candidates = await fetchATSCandidates(connectionId); if (candidates) { displayCandidates(candidates); } else { console.log('No candidates found or error occurred'); document.getElementById('candidatesList').innerHTML = '

    No candidates found or an error occurred.

    '; } }); ``` ## Step 7: Call the Unified API Now, let's test our application to ensure everything is working correctly. Click **Fetch Candidates**. You should see a list of candidates appear on your screen. Since we're working in the Sandbox environment, these will be mock candidates generated by Unified.to. ![Results of calling the API](/images/screenshots/tutorial-result.png) **Troubleshooting**: If you don't see any candidates or encounter an error, check the browser console for any error messages or verify that your Unified API Key is correctly set in the `.env` file. ## Summary Congratulations! You've built a Javascript app that can call the Unified.to API and fetch candidates from Lever. In this tutorial, you saw how to: - Activate integrations in Unified.to - Create connections by granting access to third-party vendors - Add the [Unified.to](http://unified.to/) Embedded Widget to your app - Make API calls to our unified API ## Next Steps From here, you can start adding integrations from [Unified.to](http://unified.to/) into your own project. The following resources can help you on your way: - Use one of our [SDKs](/reference/sdks) - Get familiar with [webhooks](/concepts/webhooks) - See how to customize the auth flow with our [Customize your authorization flow with the Unified API](/tutorials/customize-auth-flow) tutorial Happy building! ## Customize your authorization flow with the Unified API URL: https://docs.unified.to/tutorials/customize-auth-flow # Customize your authorization flow with the Unified API In this tutorial, you'll build a Javascript app that asks users to grant access to their third-party accounts with the Unified API. Instead of using the Authorization embedded component as in the [starter tutorial](/tutorials/build-a-simple-javascript-app), we'll implement this authorization flow from scratch. This work flow is intended for experienced developers who want more control over the authorization process or need to customize the user experience. By the end of this tutorial, you'll be able to: 1. Fetch and display a list of available integrations from Unified.to 2. Create an authorization URL for a selected integration 3. Handle the authorization callback and create a connection 4. Use the connection to fetch data from an API provider ## Prerequisites Before we begin, make sure you have: 1. [Signed up](https://unified.to/) for a Unified.to account. 2. Completed our [Get Started](https://app.unified.to/start) guide in the app. 3. Downloaded a code editor of your choice, like VS Code. 4. Installed NPM and Node.js on your system. ## Project setup We'll use the same project structure as the previous tutorial. Start by cloning the repository and installing dependencies: ```js [shell] git clone git@github.com:unified-to/unified-javascript-tutorial.git cd unified-javascript-tutorial npm i ``` The app should now be available on `localhost:1234`. Open the folder in your preferred code editor. ## Step 1: Retrieve your workspace ID and API token 1. Log in to your Unified.to account 2. Go to [Settings > API Keys](https://app.unified.to/settings/api) 3. Copy your Workspace ID and API Token 4. Open the `.env` file in your project and add these values: ```js [.env] UNIFIED_WORKSPACE_ID = your_workspace_id_here; UNIFIED_API_KEY = your_api_token_here; ``` Now that you've updated `.env`, we can start our app. Run the following from the command line: ```js [shell] npm run start ``` You can view your app on `localhost:1234`. ## Step 2: Activate an integration in the Sandbox environment _If you've already activated some ATS integrations in the Sandbox environment, skip ahead to step 3._ The Sandbox environment is where you can activate, explore, and try out integrations in a safe and non-destructive environment. We're going to call the ATS endpoint by the end of this tutorial, so let's activate an ATS integration. ### Check that you are in the Sandbox environment 1. Navigate to [_Integrations_](https://app.unified.to/integrations) 2. Look for the environment dropdown near the top of the page. Click on the dropdown that contains 'ENV' and select 'ENV: SANDBOX'. Any subsequent actions you take will happen in the Sandbox environment. ### Activate Lever 1. On the same page (Active Integrations), search for 'Lever' by typing it into the search box. 2. Click on the Lever card to open its details page. 3. Leave the default settings as-is. Notice that since we’re in the Sandbox environment, the credentials are pre-filled with mock values. Lever uses OAuth 2 by default for authorization. 4. At the bottom of the page, click 'Activate’. The Lever integration is now activated in your Sandbox environment. This allows you to make test API calls and get a feel for the Unified API without needing to connect to a real Lever account. ## Step 3: Fetch and display available integrations Time to get coding! First, let's write a function to fetch all of the available and activated integrations from your workspace. Add the following to `app.js`: ```js [app.js] async function fetchIntegrations() { try { const response = await axios.get('https://api.unified.to/unified/integration/workspace', { params: { workspace_id: UNIFIED_WORKSPACE_ID, env: 'Sandbox', categories: 'ats', }, headers: { Authorization: `Bearer ${UNIFIED_API_KEY}`, }, }); return response.data; } catch (error) { console.error('Error fetching integrations:', error); return []; } } ``` **API reference**: [Get all integrations](/unified/integration/Returns_all_integrations) ### Render integrations in the UI We also need to display the integrations in the UI. We'll do this programatically by passing the response from the above function to `displayIntegrations()`. When the app loads, we'll fetch the integrations and then render them on the page. Add the following to `app.js`: ```js [app.js] function displayIntegrations(integrations) { const integrationsListElement = document.getElementById('integrations-list'); integrations.forEach((integration) => { const listItem = document.createElement('div'); listItem.innerHTML = ` `; integrationsListElement.appendChild(listItem); }); } document.addEventListener('DOMContentLoaded', async () => { const integrations = await fetchIntegrations(); displayIntegrations(integrations); }); ``` Now, let's update the view by creating a container to hold the integrations. In `index.html`, add the following under `
    ` e.g: ```html [index.html]

    Available Integrations

      ``` At this point, you should see a list of integrations displayed on the page (if you're just starting out, then the list may only contain one item, but that's OK!) ## Step 4: Create an authorization URL Now that we're displaying our integrations to our users, let's ask them to grant us access to their third-party accounts when they click on an integration. A few things will happen: - When a user clicks on an integration, they will be redirected to the authorization page for that integration. - The user will be asked to authorize your app with the third-party provider. - After granting access to your app, a connection will be created. The user will be redirected to your `success_redirect` URL. Add the following to `app.js`: ```js [app.js] function createAuthLink(integrationType) { const baseUrl = 'https://api.unified.to/unified/integration/auth'; const params = new URLSearchParams({ redirect: '1', env: 'Sandbox', success_redirect: window.location.href, failure_redirect: window.location.href, scopes: 'ats_candidate_read', state: 'abc-123-def-456', }); return `${baseUrl}/${UNIFIED_WORKSPACE_ID}/${integrationType}?${params.toString()}`; } ``` **API reference**: [authorize new connection via authorization URL](https://docs.unified.to/unified/integration/Authorize_new_connection) ### Breaking down the authorization URL API call Let's review the structure of the API call we made to create an authorization URL: `https://api.unified.to/unified/integration/auth/{workspace_id}/{integration_type}?{query_parameters}` - `workspace_id`: Your Unified.to workspace ID - `integration_type`: The name of the integration (e.g., 'lever') - Query parameters: - `redirect=1`: Indicates that this is a redirect request. The server will return a `302` status code and redirect the user. We recommend that you use this method as it is more secure and robust. - `env=Sandbox`: Specifies the environment (use 'Sandbox' for testing) - `success_redirect`: URL to redirect after successful authorization - `failure_redirect:` URL to redirect after failed authorization - `scopes`: Comma-separated list of required scopes (e.g., 'ats_candidate_read'). It's a best practice to always include the scopes you need. - `state`: Data that you want to send back to your success URL in the URL parameters. A common use case is to put your user ID here so that after they have completed the auth flow, you can use the user ID to associate them with the connection you just made. ### Update the display logic for integrations We need to update the `displayIntegrations()` function we created earlier. Replace it with the following: ```js [app.js] function displayIntegrations(integrations) { const integrationsListElement = document.getElementById('integrations-list'); integrations.forEach((integration) => { const listItem = document.createElement('div'); const authLink = createAuthLink(integration.integration_type); listItem.innerHTML = ` ${integration.integration_type} `; integrationsListElement.appendChild(listItem); }); } ``` **API reference**: [Create an authorization URL](/unified/integration/Authorize_new_connection) (a.k.a. create a connection indirectly). When you click on one of your integrations now, you'll be taken to an authorization page. In the next step, we'll handle what happens after an integration is authorized. ## Step 5: Handle the authorization callback After a user successfully authorizes your application, they'll be redirected to your app with some new parameters in the URL. One of these parameters is `id`, which represents the ID of the newly created connection. We will need to store this connection ID in order to use it in future API calls. Create a function to handle the connection ID: ```js [app.js] function handleAuthCallback(connectionId) { localStorage.setItem('unifiedConnectionId', connectionId); // Clear the URL parameters window.history.replaceState({}, document.title, window.location.pathname); } ``` Update the `DOMContentLoaded` event listener to call this function when the page is loaded and a connection ID is present i.e. when the user is redirected to your app. ```js [app.js] // Add the following to the `DOMContentLoaded` event listener const urlParams = new URLSearchParams(window.location.search); const connectionId = urlParams.get('id'); if (connectionId) { handleAuthCallback(connectionId); } ``` **Note**: In a "real" app, you should store the connection ID in your own database. ## Step 6: Use the connection to call the Unified API Let's update our code to fetch candidates using the connection we just created. (Fun fact: From this point onwards, the code you're seeing is the same as the code from the starter tutorial). ```js [app.js] async function fetchATSCandidates(connectionId) { const options = { method: 'GET', url: `https://api.unified.to/ats/${connectionId}/candidate`, headers: { authorization: `bearer ${UNIFIED_API_KEY}`, }, params: { limit: 20, offset: 0, }, }; try { const response = await axios.request(options); return response.data; } catch (error) { console.error('Error fetching ATS candidates:', error); return null; } } ``` **API Reference:** [List all candidates](/ats/candidate/List_all_candidates) Notice that we don't need to specify `Sandbox` as an environment parameter in the API call - the connection we created is part of the Sandbox environment, so any data that comes back from it will be synthetic. Finally, let's display the candidates in the UI. Add the following code to your app: ```js [app.js] function displayCandidates(candidates) { const candidatesList = document.getElementById('candidates-list'); candidatesList.innerHTML = '

      Candidates:

      '; candidates.forEach((candidate) => { const candidateElement = document.createElement('li'); candidateElement.className = 'candidate-item'; candidateElement.innerHTML = `
      ${candidate.name || 'Candidate'} avatar

      ${candidate.name || 'N/A'}

      ${ candidate.emails?.[0]?.email || 'No email provided' }

      `; candidatesList.appendChild(candidateElement); }); } document.getElementById('fetchCandidatesBtn').addEventListener('click', async () => { const connectionId = localStorage.getItem('unifiedConnectionId'); if (!connectionId) { console.error('No connection ID found. Please connect to Lever first.'); return; } const candidates = await fetchATSCandidates(connectionId); if (candidates) { displayCandidates(candidates); } else { console.log('No candidates found or error occurred'); document.getElementById('candidatesList').innerHTML = '

      No candidates found or an error occurred.

      '; } }); ``` In `index.html`, add the following lines under `
      ` e.g: ```html [index.html]
        ``` With all the pieces in place, you can now give this auth flow a try. Click on an integration, authorize your app, and then click on **Fetch candidates** to see what happens. ## Summary Congratulations! You've successfully built your own custom authorization flow to creates connections with the Unified API. This tutorial demonstrated how to: 1. Fetch and display available integrations 2. Redirect users to an authorization page for a selected integration 3. Handle the auth callback and create a connection 4. Use the connection to fetch and display data from a third-party API provider By implementing this authorization flow yourself, you have more control over the user experience and can customize it to fit your application's needs. Happy building! --- # API Reference ## Data Types URL: https://docs.unified.to/reference/datatypes # Data Types | Type | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | string | a string | | number | either an integer or double/float | | array | a list / array of one data type | | boolean | true or false | | object | any object structure | | date | Dates/Timestamps use UTC time and are formatted as ISO 8601 strings (without milliseconds). eg. `2015-04-16T09:14:57Z` | | ID | All object IDs are 24 byte hexadecimal strings that are created by our MongoDB servers and represent a [ObjectId](https://docs.mongodb.com/manual/reference/method/ObjectId/). | | undefined | some values could be `undefined` if no information is available for that field | ## Working with Custom & Original Fields URL: https://docs.unified.to/reference/fields # Working with Custom & Original Fields When integrating with platforms and SaaS apps, you often need control over which fields to receive or send. This guide explains how to work with both standard and custom fields. ## Requesting Specific Fields By default, API calls return all available fields for an object. However, you can optimize your requests by specifying only the fields you need using the `fields` parameter: Some upstream APIs also expose **slow fields** that require additional API calls per record (for example, Greenhouse application `offers` or `raw.offers`). These can be excluded or included by default at the workspace level and overridden per request using `fields`. For details, see [Slow fields](/reference/slow-fields). ``` GET /crm/{connection_id}?/contact?fields=id,name,emails ``` ## Accessing Original Data (Raw Fields) ### Reading Original Data Sometimes you need access to the original, unmodified data from an integration (for example, to access platform-specific fields). To receive this data: 1. Include `raw` in your `fields` parameter 2. The response will include a `raw` object containing the original data from the source ``` GET /crm/{connection_id}?/contact?fields=id,name,emails,raw ``` ## Writing Original Data When creating or updating records, you can include integration-specific data that isn't part of our unified data model: ``` { "name": "John Doe", "raw": { "custom_integration_field": "value" } } ``` ## Accessing the original ID of the object Usually, Unified.to will return the object's ID in the `id` field. But sometimes, Unified.to is required to transform that original ID with additional information. If you require the original object's ID (for example, using our Passthrough API), it will be available in the `raw.__id` field. ## Working with Custom Original Fields Some CRM platforms (like HubSpot and Salesforce) require specific fields to be explicitly included in the API call i.e., they won't be returned if you only added `raw` to the request parameters. If your app requires specific fields in this way ("custom fields"): 1. Include `raw` in your fields parameter 2. Add the specific custom fields you need with the `raw.` prefix For example, to request custom fields named `leadFunnelStage` and `customerSegment`: ``` GET /crm/{connection_id}?/contact?fields=id,name,raw,raw.leadFunnelStage,raw.customerSegment ``` ## Mock API Server URL: https://docs.unified.to/reference/mock # Mock API Server Unified.to has a "mock server" that uses pre-generated synthentic data that emulates the data available in any of our integrations. ## How to call the mock server You can call any REST API endpoint with a `X-Mock: 1` header. Make sure to have an empty `Authorization` header and use URL IDs and payload data that are properly formatted (e.g. ID=5de520f96e439b002043d8dc). ## Sandbox environment You can also use the "Sandbox" environment when making API calls as that will also use the Mock API Server. `?env=Sandbox` ## Pagination URL: https://docs.unified.to/reference/pagination # Pagination All integrations use the same pagination parameters. But not all integrations support all of the pagination parameters. Please see the Feature Support tab in the integration's page in app.unified.to. ## Limit By default, most list endpoints return a maximum of 100 records per page. You can change the number of records on a per-request basis by passing a `limit` parameter (in the request URL parameters for REST). Example: `limit=50` However, you can't exceed 100 records per page on most endpoints. You will know that there aren't any more records, when the number of returned results is less than your requested `limit`. ## Offset When the response exceeds the requested results, you can paginate through the records by padding a `offset` parameter that specifies the position of the first result. This parameter is zero-based regardless of integration's paging mechanism. ## Filtering with Updated Since, Start, or End You can filter the list by specifying the minimum updated date `updated_gte`. This parameter returns lists that have their updated date at of greater than the specific value. For some integrations, you can also use `start_gte` and `end_le` to filter results for that timeframe. All dates are in a `YYYY-MM-DDTHH:MM:SSZ` format. You can leave off the time and the timezone if you like. The default timezone is `UTC`. ## Filtering with Query You can filter the list by specifying a `query` parameter. The search will be integration-specific, but will only be used to filter by `email` or `name`. eg. `query=John` `query=john@example.com`. If you require additional filtering, you must synchronize your customer's data into your own database where you would then be able to filter on any field. Unified.to STRONGLY recommends that you synchronize data if you plan on doing queries. It will be much faster, you will be able to query using whatever criteria you want, and the queries will be consistent and not integration-specific. We recommend using our webhooks for synchronization. ## Sorting & Ordering You can sort the results the `sort` parameter. Valid options include `name`, `updated_at`, and `created_at`. You can order order the results with the `order` parameter which only has two options: `asc` and `desc`. ## Rate limits URL: https://docs.unified.to/reference/rate_limits # Rate limits Rate limiting is an important aspect of working with APIs that helps maintain system stability and ensure fair usage across all users. This guide explains how rate limiting works at Unified.to and how it affects your API usage. ## Background Historically, rate limiting has been a necessary part of API design to: - Prevent server overload from too many simultaneous requests - Ensure fair resource distribution among all users - Protect APIs from abuse or unintended high-volume usage In Unified.to's case, we act as a real-time intermediary between your application and various API platforms. While Unified.to has its own set of rate limits, we also need to respect and enforce the limits set by each API platform to maintain stable integrations. ## How are rate limits determined at Unified.to? Rate limits at Unified.to are determined by two factors: 1. **SaaS platform limits**: Each underlying platform (like HubSpot, Salesforce, etc.) has their own rate limits that determine how many API calls you can make within a given time period. These are the primary limiting factor as we must respect these limitations to maintain stable integrations. 2. **Unified.to limits**: We also implement our own rate limits to ensure fair usage across all users. However, these are generally more generous than the platform-specific limits. Our rate-limit error will have a message of "Too many requests to Unified.to". You can find the specific rate limits for each integration under the **Feature Support** tab on https://app.unified.to/. When you hit a rate limit, you'll receive a 429 (Too Many Requests) response. ## Handle rate limits yourself When making direct API calls, you'll need to implement your own rate limit handling strategy. The most common approach is to use a backoff and retry mechanism: ### Backoff and retry strategy When you receive a rate limit error (HTTP 429), your application should: 1. Temporarily pause making requests 2. Wait for a period of time 3. Retry the request The waiting period typically follows an exponential backoff pattern, where each subsequent retry waits longer than the previous one. For example: - First retry: Wait 1 second - Second retry: Wait 2 seconds - Third retry: Wait 4 seconds - Fourth retry: Wait 8 seconds This exponential increase helps prevent overwhelming the API while still maintaining functionality. ### Best practices for handling rate limits - Add randomness (jitter) to your retry delays to prevent multiple clients from retrying simultaneously - Set a maximum number of retry attempts - Log rate limit occurrences to help identify patterns and adjust your strategy - Consider implementing a request queue to manage high-volume operations ## Use webhooks to avoid worrying about rate limits While you can implement your own rate limit handling, we strongly recommend using our webhooks instead. Here's why: - **Easier and faster integration build time**: Unified.to manages all rate limiting, backoff, and retry logic for you. You can also get all existing data via an initial sync, too - **Efficient resource usage**: Instead of polling for changes, you receive updates only when they occur - **Reliable delivery**: We'll keep trying to deliver webhook events even if your endpoint is temporarily unavailable - **Automatic scaling**: Our webhook system automatically adjusts to rate limits across different providers You can read more about our webhooks retry mechanism [here](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_webhooks#a-note-on-our-webhook-retry-mechanism). ## Related resources - [Understanding webhooks](https://docs.unified.to/reference/webhooks) - [How to create and configure webhooks](https://docs.unified.to/guides/how_to_create_and_configure_webhooks) - [Pagination](https://docs.unified.to/reference/pagination) ## REST API URL: https://docs.unified.to/reference/rest # REST API This API is organized around [REST](http://en.wikipedia.org/wiki/Representational_State_Transfer){:target="\_blank"} and utilizes predictable resource-oriented URLs, accepts various request formats, returns responses in multiple formats, and uses standard HTTP response codes, authentication, and verbs. It provides predictable URLs for accessing resources and uses built-in HTTP features to receive commands and return responses. This makes it easy to communicate with from a wide variety of environments, from command-line utilities to gadgets to the browser URL bar itself. The API accepts JSON in requests and multiple content types for responses, including JSON, CSV, XML, NDJSON, and SCIM+JSON. Only the UTF-8 character encoding is supported for both requests and responses. ## Authentication Send your [API token](https://app.unified.to/settings/api){:target="\_blank"} in the `Authorization` HTTP header for each API request. Example: `Authorization: Bearer eyJhbGciOiJIUzI1NiIXVCJ9` All API requests must be made over [HTTPS](http://en.wikipedia.org/wiki/HTTP_Secure){:target="\_blank"}. API requests made over plain HTTP or without authentication will fail. Furthermore, all connections must support the TLS 1.2 version for their SSL/HTTPS protocol. Find your workspace API Token at [app.unified.to/settings/api](https://app.unified.to/settings/api){target="\_blank"} ## API URLs Unified has two separate data-regions around the world currently. To use the API URL in a data-region, you must register an account with our application in that data region. | Region | API URL | Application URL | | ------------- | ------------------------- | ------------------------- | | North America | https://api.unified.to | https://app.unified.to | | Europe | https://api-eu.unified.to | https://app-eu.unified.to | | Australia | https://api-au.unified.to | https://app-au.unified.to | ## HTTP Methods | Method | Description | | ------------------- | ------------------------------------------------------- | | **GET** | Retrieve a single resource or get an array of resources | | **PUT** / **PATCH** | Update a single resource partially or fully | | **POST** | Create a single resource | | **DELETE** | Remove a single resource | ## Request Format This API accepts [JSON](https://www.json.org/){:target="\_blank"} payloads. You must supply a `Content-Type: application/json` header in PUT and POST requests. The documented JSON properties for this API are case sensitive. For example, `id` and `Id` would not constitute the same property. ## Response Format This API can return responses in multiple formats. You can specify your preferred response format by setting the `Accept` header in your request. The supported response formats are: | Format | Header | | ------------------------------- | ------------------------------- | | JSON (default) | `Accept: application/json` | | NDJSON (Newline Delimited JSON) | `Accept: application/ndjson` | | CSV (Comma-Separated Values) | `Accept: text/csv` | | XML | `Accept: text/xml` | | SCIM+JSON (for SCIM endpoints) | `Accept: application/scim+json` | If no `Accept` header is specified, the API will default to returning JSON responses. When you successfully create or update a resource, the API will respond with the resulting resource in the requested format. This API responds to successful requests with HTTP status codes in the 200 or 300 range, and with HTTP status codes in the 400 range if an error occurred. A list of objects is sent as an array of objects in JSON and NDJSON formats, as rows in CSV format, or as nested elements in XML format. All other operations return a single object. Note: The SCIM+JSON format is only available for specific endpoints (e.g. employee and group). ## Common HTTP Status Codes | Code | Description | | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **200** | Ok: The request was successful. | | **400** | Bad Request: Required parameters are missing or in the wrong format. Check the request payload against the API documentation. | | **401** | Unauthorized: The connection is likely broken and requires recreation. This typically occurs when app access has been revoked or authentication credentials are invalid. | | **403** | Forbidden: The connection lacks the required permissions or scopes. This usually indicates a mismatch between the scopes configured in your provider's developer account and what Unified.to is requesting. | | **404** | Not Found: The requested resource was not found. | | **429** | Rate Limit Exceeded: The provider's API rate limit has been reached. Each platform (and customer plan) has different rate limits. Consider using webhooks or implementing request throttling. | | **500** | Internal Server Error: Our server encountered an unexpected condition. We monitor these errors and will work to resolve them. For platform issues, retry after a short delay. | | **501** | Not Implemented: The requested functionality is not supported by this integration. Check the integration's documentation for supported features. | For more help with error codes, review our [troubleshooting guide](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_connections). ## SDKs URL: https://docs.unified.to/reference/sdks # SDKs Use the following SDKs to access our API. Note that each SDK will use that language's standard naming convention for field names. for example, TypeScript uses CamelCase, but all of our documenation uses snake_case. - **Typescript:** [npmjs.com/package/@unified-api/typescript-sdk](https://www.npmjs.com/package/@unified-api/typescript-sdk){:target="\_blank"} ::sdk-version{name="typescript"} :: - **Python:** [github.com/unified-to/unified-python-sdk](https://github.com/unified-to/unified-python-sdk){:target="\_blank"} ::sdk-version{name="Python"} :: - **PHP:** [github.com/unified-to/unified-php-sdk](https://github.com/unified-to/unified-php-sdk){:target="\_blank"} ::sdk-version{name="php"} :: - **Java:** [central.sonatype.com/artifact/to.unified/unified-java-sdk](https://central.sonatype.com/artifact/to.unified/unified-java-sdk){:target="\_blank"} ::sdk-version{name="java"} :: - **Go:** [github.com/unified-to/unified-go-sdk](https://github.com/unified-to/unified-go-sdk){:target="\_blank"} ::sdk-version{name="go"} :: - **C#:** [nuget.org/packages/UnifiedTo](https://www.nuget.org/packages/UnifiedTo){:target="\_blank"} ::sdk-version{name="csharp"} :: - **Ruby:** [github.com/unified-to/unified-ruby-sdk](https://github.com/unified-to/unified-ruby-sdk){:target="\_blank"} ::sdk-version{name="ruby"} :: - **Other:** You can generate SDKs in other languages by utilizing our [openapi.json](https://api.unified.to/openapi.json){:target="\_blank"} with tools like [OpenAPI Generator](https://openapi-generator.tech/){:target="\_blank"}. # Specification Files Access our API specifications in various formats to integrate with your preferred tools and workflows. They are updated, at least, daily. - **OpenAPI specification:** [openapi.json](https://api.unified.to/openapi.json){:target="\_blank"} / [openapi.yaml](https://api.unified.to/openapi.yaml){:target="\_blank"} ::callout The openapi.json/.yaml file contains our API specification in OpenAPI 3.0 format - the modern standard for REST API documentation. This specification offers enhanced features over Swagger 2.0, including improved security scheme definitions and more flexible parameter descriptions. It's great for generating modern API documentation, client SDKs, and mock servers. You can download one [openapi.json](https://api.unified.to/openapi.json){:target="\_blank"} for all of our Unified APIs, or you can download a openapi.json for each API. - [openapi-accounting.json](https://api.unified.to/docs/openapi-accounting.json){:target="\_blank"} / [openapi-accounting.yaml](https://api.unified.to/docs/openapi-accounting.yaml){:target="\_blank"} - [openapi-ads.json](https://api.unified.to/docs/openapi-ads.json){:target="\_blank"} / [openapi-ats.yaml](https://api.unified.to/docs/openapi-ads.yaml){:target="\_blank"} - [openapi-assessment.json](https://api.unified.to/docs/openapi-assessment.json){:target="\_blank"} / [openapi-ats.yaml](https://api.unified.to/docs/openapi-assessment.yaml){:target="\_blank"} - [openapi-ats.json](https://api.unified.to/docs/openapi-ats.json){:target="\_blank"} / [openapi-ats.yaml](https://api.unified.to/docs/openapi-ats.yaml){:target="\_blank"} - [openapi-calendar.json](https://api.unified.to/docs/openapi-calendar.json){:target="\_blank"} / [openapi-calendar.yaml](https://api.unified.to/docs/openapi-calendar.yaml){:target="\_blank"} - [openapi-commerce.json](https://api.unified.to/docs/openapi-commerce.json){:target="\_blank"} / [openapi-commerce.yaml](https://api.unified.to/docs/openapi-commerce.yaml){:target="\_blank"} - [openapi-crm.json](https://api.unified.to/docs/openapi-crm.json){:target="\_blank"} / [openapi-crm.yaml](https://api.unified.to/docs/openapi-crm.yaml){:target="\_blank"} - [openapi-enrichment.json](https://api.unified.to/docs/openapi-enrichment.json){:target="\_blank"} / [openapi-enrichment.yaml](https://api.unified.to/docs/openapi-enrichment.yaml){:target="\_blank"} - [openapi-hris.json](https://api.unified.to/docs/openapi-hris.json){:target="\_blank"} / [openapi-hris.yaml](https://api.unified.to/docs/openapi-hris.yaml){:target="\_blank"} - [openapi-kms.json](https://api.unified.to/docs/openapi-kms.json){:target="\_blank"} / [openapi-kms.yaml](https://api.unified.to/docs/openapi-kms.yaml){:target="\_blank"} - [openapi-lms.json](https://api.unified.to/docs/openapi-lms.json){:target="\_blank"} / [openapi-lms.yaml](https://api.unified.to/docs/openapi-lms.yaml){:target="\_blank"} - [openapi-martech.json](https://api.unified.to/docs/openapi-martech.json){:target="\_blank"} / [openapi-martech.yaml](https://api.unified.to/docs/openapi-martech.yaml){:target="\_blank"} - [openapi-messaging.json](https://api.unified.to/docs/openapi-messaging.json){:target="\_blank"} / [openapi-messaging.yaml](https://api.unified.to/docs/openapi-messaging.yaml){:target="\_blank"} - [openapi-payment.json](https://api.unified.to/docs/openapi-payment.json){:target="\_blank"} / [openapi-payment.yaml](https://api.unified.to/docs/openapi-payment.yaml){:target="\_blank"} - [openapi-repo.json](https://api.unified.to/docs/openapi-repo.json){:target="\_blank"} / [openapi-repo.yaml](https://api.unified.to/docs/openapi-repo.yaml){:target="\_blank"} - [openapi-scim.json](https://api.unified.to/docs/openapi-scim.json){:target="\_blank"} / [openapi-scim.yaml](https://api.unified.to/docs/openapi-scim.yaml){:target="\_blank"} - [openapi-storage.json](https://api.unified.to/docs/openapi-storage.json){:target="\_blank"} / [openapi-storage.yaml](https://api.unified.to/docs/openapi-storage.yaml){:target="\_blank"} - [openapi-task.json](https://api.unified.to/docs/openapi-task.json){:target="\_blank"} / [openapi-task.yaml](https://api.unified.to/docs/openapi-task.yaml){:target="\_blank"} - [openapi-ticketing.json](https://api.unified.to/docs/openapi-ticketing.json){:target="\_blank"} / [openapi-ticketing.yaml](https://api.unified.to/docs/openapi-ticketing.yaml){:target="\_blank"} - [openapi-uc.json](https://api.unified.to/docs/openapi-uc.json){:target="\_blank"} / [openapi-uc.yaml](https://api.unified.to/docs/openapi-uc.yaml){:target="\_blank"} - [openapi-verification.json](https://api.unified.to/docs/openapi-verification.json){:target="\_blank"} / [openapi-verification.yaml](https://api.unified.to/docs/openapi-verification.yaml){:target="\_blank"} :: - **Swagger specification:** [swagger.json](https://api.unified.to/swagger.json){:target="\_blank"} / [swagger.yaml](https://api.unified.to/swagger.yaml) ::callout Our swagger.json/.yaml file provides a detailed API specification in the Swagger 2.0 format. It's ideal for developers who are working with older tools or platforms that haven't yet migrated to OpenAPI 3.0. Use this specification to generate client libraries, automate testing, and integrate with legacy systems. :: - **Postman collection:** [collection.json](https://api.unified.to/collection.json){:target="\_blank"} ::callout Our `collection.json` file is a ready-to-use Postman collection that lets you start testing our API endpoints immediately. Import this file into Postman to get a complete set of pre-configured API requests. It's an excellent resource for API exploration and testing without writing any code. You can download one [collection.json](https://api.unified.to/collection.json){:target="\_blank"} for all of our Unified APIs, or you can download a collection.json for each API. - [collection-accounting.json](https://api.unified.to/docs/collection-accounting.json){:target="\_blank"} - [collection-ads.json](https://api.unified.to/docs/collection-ads.json){:target="\_blank"} - [collection-assessment.json](https://api.unified.to/docs/collection-assessment.json){:target="\_blank"} - [collection-ats.json](https://api.unified.to/docs/collection-ats.json){:target="\_blank"} - [collection-calendar.json](https://api.unified.to/docs/collection-calendar.json){:target="\_blank"} - [collection-commerce.json](https://api.unified.to/docs/collection-commerce.json){:target="\_blank"} - [collection-crm.json](https://api.unified.to/docs/collection-crm.json){:target="\_blank"} - [collection-enrichment.json](https://api.unified.to/docs/collection-enrichment.json){:target="\_blank"} - [collection-hris.json](https://api.unified.to/docs/collection-hris.json){:target="\_blank"} - [collection-kms.json](https://api.unified.to/docs/collection-kms.json){:target="\_blank"} - [collection-lms.json](https://api.unified.to/docs/collection-lms.json){:target="\_blank"} - [collection-martech.json](https://api.unified.to/docs/collection-martech.json){:target="\_blank"} - [collection-messaging.json](https://api.unified.to/docs/collection-messaging.json){:target="\_blank"} - [collection-payment.json](https://api.unified.to/docs/collection-payment.json){:target="\_blank"} - [collection-repo.json](https://api.unified.to/docs/collection-repo.json){:target="\_blank"} - [collection-scim.json](https://api.unified.to/docs/collection-scim.json){:target="\_blank"} - [collection-storage.json](https://api.unified.to/docs/collection-storage.json){:target="\_blank"} - [collection-task.json](https://api.unified.to/docs/collection-task.json){:target="\_blank"} - [collection-ticketing.json](https://api.unified.to/docs/collection-ticketing.json){:target="\_blank"} - [collection-uc.json](https://api.unified.to/docs/collection-uc.json){:target="\_blank"} - [collection-verification.json](https://api.unified.to/docs/collection-verification.json){:target="\_blank"} Visit the Unified postman workspace at [Postman](https://www.postman.com/unified-to/unified-api-workspace/overview){:target="\_blank"}. :: - **Supabase SQL DDL:** [Unified API's DDL](https://api.unified.to/docs/supabase-ddl.sql){:target="\_blank"} ::callout You can use our SQL DDL to create databases in Supabase matching our data-model schemas. You can download one [DDL SQL](https://api.unified.to/docs/supabase-ddl.sql){:target="\_blank"} for all of our Unified APIs, or you can download a DDL SQL for each API, which will contain just the data models for that API category. - [supabase-ddl-accounting.sql](https://api.unified.to/docs/supabase-ddl-accounting.sql){:target="\_blank"} - [supabase-ddl-ads.sql](https://api.unified.to/docs/supabase-ddl-ads.sql){:target="\_blank"} - [supabase-ddl-assessment.sql](https://api.unified.to/docs/supabase-ddl-assessment.sql){:target="\_blank"} - [supabase-ddl-ats.sql](https://api.unified.to/docs/supabase-ddl-ats.sql){:target="\_blank"} - [supabase-ddl-calendar.sql](https://api.unified.to/docs/supabase-ddl-calendar.sql){:target="\_blank"} - [supabase-ddl-commerce.sql](https://api.unified.to/docs/supabase-ddl-commerce.sql){:target="\_blank"} - [supabase-ddl-crm.sql](https://api.unified.to/docs/supabase-ddl-crm.sql){:target="\_blank"} - [supabase-ddl-enrichment.sql](https://api.unified.to/docs/supabase-ddl-enrichment.sql){:target="\_blank"} - [supabase-ddl-hris.sql](https://api.unified.to/docs/supabase-ddl-hris.sql){:target="\_blank"} - [supabase-ddl-kms.sql](https://api.unified.to/docs/supabase-ddl-kms.sql){:target="\_blank"} - [supabase-ddl-lms.sql](https://api.unified.to/docs/supabase-ddl-lms.sql){:target="\_blank"} - [supabase-ddl-martech.sql](https://api.unified.to/docs/supabase-ddl-martech.sql){:target="\_blank"} - [supabase-ddl-messaging.sql](https://api.unified.to/docs/supabase-ddl-messaging.sql){:target="\_blank"} - [supabase-ddl-payment.sql](https://api.unified.to/docs/supabase-ddl-payment.sql){:target="\_blank"} - [supabase-ddl-repo.sql](https://api.unified.to/docs/supabase-ddl-repo.sql){:target="\_blank"} - [supabase-ddl-scim.sql](https://api.unified.to/docs/supabase-ddl-scim.sql){:target="\_blank"} - [supabase-ddl-storage.sql](https://api.unified.to/docs/supabase-ddl-storage.sql){:target="\_blank"} - [supabase-ddl-task.sql](https://api.unified.to/docs/supabase-ddl-task.sql){:target="\_blank"} - [supabase-ddl-ticketing.sql](https://api.unified.to/docs/supabase-ddl-ticketing.sql){:target="\_blank"} - [supabase-ddl-uc.sql](https://api.unified.to/docs/supabase-ddl-uc.sql){:target="\_blank"} - [supabase-ddl-verification.sql](https://api.unified.to/docs/supabase-ddl-verification.sql){:target="\_blank"} :: - **Snowflake SQL DDL:** [Unified API's DDL](https://api.unified.to/docs/snowflake-ddl.sql){:target="\_blank"} ::callout You can use our SQL DDL to create tables in Snowflake matching our data-model schemas. You can download one [DDL SQL](https://api.unified.to/docs/snowflake-ddl.sql){:target="\_blank"} for all of our Unified APIs, or you can download a DDL SQL for each API, which will contain just the data models for that API category. - [snowflake-ddl-accounting.sql](https://api.unified.to/docs/snowflake-ddl-accounting.sql){:target="\_blank"} - [snowflake-ddl-ads.sql](https://api.unified.to/docs/snowflake-ddl-ads.sql){:target="\_blank"} - [snowflake-ddl-assessment.sql](https://api.unified.to/docs/snowflake-ddl-assessment.sql){:target="\_blank"} - [snowflake-ddl-ats.sql](https://api.unified.to/docs/snowflake-ddl-ats.sql){:target="\_blank"} - [snowflake-ddl-calendar.sql](https://api.unified.to/docs/snowflake-ddl-calendar.sql){:target="\_blank"} - [snowflake-ddl-commerce.sql](https://api.unified.to/docs/snowflake-ddl-commerce.sql){:target="\_blank"} - [snowflake-ddl-crm.sql](https://api.unified.to/docs/snowflake-ddl-crm.sql){:target="\_blank"} - [snowflake-ddl-enrichment.sql](https://api.unified.to/docs/snowflake-ddl-enrichment.sql){:target="\_blank"} - [snowflake-ddl-hris.sql](https://api.unified.to/docs/snowflake-ddl-hris.sql){:target="\_blank"} - [snowflake-ddl-kms.sql](https://api.unified.to/docs/snowflake-ddl-kms.sql){:target="\_blank"} - [snowflake-ddl-lms.sql](https://api.unified.to/docs/snowflake-ddl-lms.sql){:target="\_blank"} - [snowflake-ddl-martech.sql](https://api.unified.to/docs/snowflake-ddl-martech.sql){:target="\_blank"} - [snowflake-ddl-messaging.sql](https://api.unified.to/docs/snowflake-ddl-messaging.sql){:target="\_blank"} - [snowflake-ddl-payment.sql](https://api.unified.to/docs/snowflake-ddl-payment.sql){:target="\_blank"} - [snowflake-ddl-repo.sql](https://api.unified.to/docs/snowflake-ddl-repo.sql){:target="\_blank"} - [snowflake-ddl-scim.sql](https://api.unified.to/docs/snowflake-ddl-scim.sql){:target="\_blank"} - [snowflake-ddl-storage.sql](https://api.unified.to/docs/snowflake-ddl-storage.sql){:target="\_blank"} - [snowflake-ddl-task.sql](https://api.unified.to/docs/snowflake-ddl-task.sql){:target="\_blank"} - [snowflake-ddl-ticketing.sql](https://api.unified.to/docs/snowflake-ddl-ticketing.sql){:target="\_blank"} - [snowflake-ddl-uc.sql](https://api.unified.to/docs/snowflake-ddl-uc.sql){:target="\_blank"} - [snowflake-ddl-verification.sql](https://api.unified.to/docs/snowflake-ddl-verification.sql){:target="\_blank"} :: - **Cloudflare Workers:** [github repo](https://github.com/unified-to/unified-cloudflare-worker){:target="\_blank"} ::callout You can use our API in a Cloudflare Worker by using our Cloudflare Worker template [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/unified-to/unified-cloudflare-worker){:target="\_blank"} :: ## Slow fields URL: https://docs.unified.to/reference/slow-fields # Slow fields Some fields in upstream APIs are expensive to fetch because they require extra API calls per record. We call these **slow fields**. Slow fields let you choose between **faster responses** and **more complete data**. You control the default behavior at the workspace level, and you can always override it per request with the `fields` query parameter. --- ## What are slow fields? **Slow fields** are resource fields that require additional API calls to fetch from the upstream provider. Examples: - A job application’s `offers` in Greenhouse requires a separate API call per application. - Nested raw objects may expose additional slow fields like `raw.offers`. Because these fields can be expensive to retrieve, we treat them specially: - They can be **excluded by default** to keep responses fast. - They can be **included when explicitly requested** using the `fields` parameter. - Or they can be **included by default** if you prefer completeness over latency. --- ## Workspace setting: Slow fields opt-in You can control how slow fields behave by default from your workspace settings in the Unified app under **Settings → Workspace → Slow fields opt-in** ([workspace settings](https://app.unified.to/settings/workspace)). - `false` (**Opt-out / recommended default**) - Slow fields are **excluded by default**. - Fewer upstream calls and lower latency. - Slow fields are only fetched when explicitly requested via `fields`. - `true` (**Opt-in**) - Slow fields are **included by default**. - Responses are more complete. - Responses can be slower due to additional API calls. You can still override this default on any request using the `fields` parameter. For a general overview of how `fields` works (including raw fields), see [Working with Custom & Original Fields](/reference/fields). --- ## How slow fields interact with `fields=…` You can control which fields are returned using the `fields` query parameter. The behavior depends on how you’ve configured **Slow fields opt-in** for your workspace. ### When Slow fields opt-in is set to **Opt-out** (default) Slow fields are excluded unless you request them: ```http GET /ats/{connection_id}/application ``` Returns standard (fast) fields only. ```http GET /ats/{connection_id}/application?fields=offers ``` Returns only the `offers` field (a slow field; this may trigger extra upstream API calls). ```http GET /ats/{connection_id}/application?fields=job_id,candidate_id ``` Returns only the requested fast fields (no slow fields). ### When Slow fields opt-in is set to **Opt-in** Slow fields are included unless you filter them out: ```http GET /ats/{connection_id}/application ``` Returns all fields, including slow fields (slower, more complete). ```http GET /ats/{connection_id}/application?fields=job_id,candidate_id ``` Returns only the requested fields (fast, excludes other slow fields). ```http GET /ats/{connection_id}/application?fields=job_id,offers ``` Returns `job_id` plus the `offers` slow field (may trigger extra upstream API calls). --- ## Raw objects and slow fields The same logic applies to nested raw objects: - `fields=raw.offers` Requests the slow `offers` field inside the raw payload. - `fields=raw.last_activity_at` Requests only a non-slow raw field (no extra API calls). Slow fields are always explicit: if you do not request them (and your workspace is not opted in by default), they will not be fetched. --- ## When to opt in vs opt out **Recommended patterns:** 1. **High‑volume or latency‑sensitive traffic** - Set **Slow fields opt-in** to **Opt-out**. - Only request slow fields when you actually need them, via `fields=…`. 2. **Comprehensive data syncs or backfills** - Set **Slow fields opt-in** to **Opt-in**, _or_ - Leave it as **Opt-out** and explicitly request all needed slow fields with `fields`. 3. **Webhooks and push-based flows** - Configure the `fields` parameter to include only the fields your consumer needs. - Avoid including slow fields unless your webhook handler can tolerate the extra latency. --- ## Identifying which fields are slow Slow fields are **integration- and object-specific**. For example: - **Greenhouse Applications**: `offers`, `raw.offers` Other integrations and object types may expose different slow fields. To see which fields are slow for a given object, check the integration-specific documentation or contact support. ## Introduction to webhooks URL: https://docs.unified.to/reference/webhooks # Webhooks This guide covers the basics of webhooks and their underlying mechanisms. For our webhook API reference, see: [Webhook API Overview](https://docs.unified.to/unified/webhook/model) To learn how to use webhooks, see: [How to create and configure webhooks](https://docs.unified.to/guides/how_to_create_and_configure_webhooks) If you’re having trouble with webhooks, see: [How to troubleshoot unhealthy webhooks](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_webhooks) ## Background: What are webhooks? Webhooks allow servers to send real-time notifications to other servers when a specific event occurs. They are sometimes called “HTTP callbacks” or “push APIs”. Webhooks enable instant communication between different systems, making them crucial for maintaining data synchronization and triggering automated workflows. For instance, Stripe's payment processing system uses webhooks to notify a merchant's server immediately when a customer completes a purchase, allowing for instant order fulfillment and inventory updates. Think of webhooks as a delivery service that brings packages directly to your doorstep. In contrast, traditional REST APIs are like repeatedly checking your mailbox to see if you've received any mail. With webhooks, you don't need to keep asking, "Is there any new mail?" Instead, the data comes to you as soon as it's available, saving time and resources. Webhooks can make your synchronization strategy less complicated since your application will receive updated data directly, eliminating the need for frequent polling and reducing the risk of missing important updates. ### Differences between polling for updates and receiving pushed updates **Polling for updates (via APIs)** The traditional way of polling for new data from an API is by calling a `List` endpoint with something like a `updated_gte: Date` filter - this returns data that has been updated since that date. How often you poll for new data should be determined by how important that data is to your application as well as your budget. If the data isn’t time-sensitive i.e. you don’t need to have the data the moment it becomes available, then this strategy may be sufficient. But you need to take care of retry strategies when the API returns a rate limit error, which can make this method more complicated than using a webhook. You’ll also need to handle any network errors that could occur. **Pushed updates (via webhooks)** Modern APIs and applications leverage webhooks for synchronization because of their simplicity and timeliness. You, the app developer, do not have to create a retry mechanism to actively poll the server for new data or deal with rate-limiting and other network errors. The hard work is done by the third-party API provider (and us!) to send you updates as they come. ### How do webhooks work? Webhooks operate on a subscription model, where your application registers to receive updates about specific events from a third-party API provider or vendor. Here's a step-by-step breakdown of how webhooks typically work: 1. Create a webhook endpoint on your server that can receive POST requests. This endpoint will handle the incoming webhook data. 2. Subscribe to the events you want to monitor by registering your webhook endpoint's URL with the third-party API provider. This is usually done through the provider's developer dashboard or via their API. 3. Specify which types of events you want to receive notifications for. This could be anything from lead generation to payment confirmations to new deals, depending on the service. 4. Receive and process data. When a target event occurs, the provider sends a POST request to your registered endpoint. Your server receives this data in real-time and can process it according to your application's needs. 5. Handle the response. Your endpoint should respond promptly to the webhook request, typically with a 200 OK status, to acknowledge receipt. This helps prevent unnecessary retries from the provider. By following this process, your application can receive and react to important events as they happen, without the need for constant polling. This real-time communication enables more efficient and responsive systems, allowing you to build more dynamic and interconnected applications. ### Differences between Created and Updated events? `Created` events only trigger on newly created records. `Updated` events are trigger on both updated and newly created records. ## Webhook payload This is the payload that your server receives when webhook data comes in: | Name | Type | Description | | --------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `data` | _object array_ | An array of objects specific to the webhook's connection (e.g. CRM contacts). | | `webhook` | [_Webhook_](https://docs.unified.to/unified/webhook/model) | The webhook object. You can use the id to manage or delete your webhook. | | `nonce` | _string_ | Random string. | | `sig` | _base64 string_ | Cryptographic signature to ensure that the data is coming from Unified.to and hasn't been modified mid-stream. Uses SHA-1. `@deprecated` | | `sig256` | _base64 string_ | Cryptographic signature to ensure that the data is coming from Unified.to and hasn't been modified mid-stream. Uses SHA-256. | | `external_xref` | _string_ | When creating a connection, you can provide an `external_xref` field and this will be sent back to you in the associated webhook. This value represents the user or account that is signed into your application. | | `type` | _enum_ | `INITIAL-PARTIAL, INITIAL-COMPLETE, VIRTUAL, NATIVE` | ### Values for `type`: - `INITIAL-PARTIAL`: included with each page of results for the initial sync - `INITIAL-COMPLETE`: included with last page (e.g. last 5 elements for the limit 100) of initial sync or with an empty list (no more results) - `VIRTUAL`: included with every page when reading new data from a virtual webhook - `NATIVE`: included with every page when reading new data from a native webhook ### Best practices for signature validation: The `sig256` value is generated using the HMAC-SHA256 crypto method. The key is your workspace secret (found at [app.unified.to/settings/api](https://app.unified.to/settings/api)) while the contents are the combination of `data` and `nonce` i.e. `HMAC-SHA256(workspace.secret, data + nonce)` Things to keep in mind: 1. **Data**: The `data` in the signature calculation refers to the data in the payload body as outlined in the table above. 2. **Order of data**: When validating the signature, the order of the payload data should match the order in which it was received. In Golang, unmarshalling json to a `map[string]interface{}` will not respect the order, because a map in golang is a hashmap. When checking for the data body, be sure to use `json.RawMessage` when trying to unmarshal the `data` property in the payload first and use that to compose the signature. 3. **Avoid adding extra spaces**: When processing the payload, be cautious about introducing extra spaces. The original payload data is sent without extra spacing, and this should be maintained during validation. In Python, when you deserialize and then re-serialize the payload data, the resulting string may not match the original input. This is because the default serialization process often adds extra spaces between fields and objects, altering the format of the data. The following code snippet demonstrates how to perform signature validation in Python: ```python [validate_webhook.py] import hmac import hashlib import base64 import json from secrets import compare_digest from flask import Request def validate_webhook(request: Request): """ Validates webhook signature using HMAC-SHA256. HMAC-SHA256(secret, JSON.stringify(data.data) + data.nonce) """ data = request.data # Full payload, includes `data`, `nonce`, `sig256` sig256 = data['sig256'] # Base64-encoded signature nonce = str(data['nonce']) # Ensure nonce is string serialized_data = json.dumps(data['data'], separators=(',', ':'), ensure_ascii=False) # Compact JSON format # Compute HMAC-SHA256 signature key = bytes(settings.UNIFIED_WORKSPACE_SECRET, 'UTF-8') message = bytes(serialized_data + nonce, 'UTF-8') digester = hmac.new(key, message, hashlib.sha256) digest = base64.b64encode(digester.digest()).decode() # Encode in Base64 # Use secure timing attack-resistant comparison return compare_digest(digest, sig256) ``` The example below demonstrates how to perform validation in Javascript/Typescript: ```typescript [validateWebhook.ts] import { createHmac, timingSafeEqual } from 'crypto'; function validateWebhook( workspaceSecret: string, data: { data: any[]; webhook: any; nonce: string; sig256: string; external_xref?: string; } ): boolean { // JSON.stringify(data.data) should match the sender's encoding const serializedData = JSON.stringify(data.data); // Compute HMAC-SHA256 signature const computedSig = Buffer.from( createHmac('sha256', workspaceSecret) .update(serializedData) .update(data.nonce) .digest('base64'), 'utf8' ); // Decode provided signature const providedSig = Buffer.from(data.sig256, 'utf8'); // Use timing-safe comparison return computedSig.length === providedSig.length && timingSafeEqual(computedSig, providedSig); } ``` This example demonstrates how to perform validation in Go: ```python [validate_webhook.go] package main import ( "crypto/hmac" "crypto/sha256" "crypto/subtle" "encoding/base64" "encoding/json" "fmt" ) type WebhookPayload struct { Data *json.RawMessage `json:"data"` ExternalXref string `json:"external_xref"` Nonce string `json:"nonce"` Sig256 string `json:"sig256"` Webhook Webhook `json:"webhook"` } type Webhook struct { CheckedAt string `json:"checked_at"` WorkspaceID string `json:"workspace_id"` } func main() { // This is an example webhook payload. You can replace this // with your request body jsonString := `{\"data\":[{\"id\":\"1\",\"created_at\":\"2025-01-20T12:53:07.976Z\",\"parent_id\":\"2\",\"status\":\"active\",\"updated_at\":\"2025-01-20T12:56:46.118Z\"}],\"external_xref\":\"id=123&page=1\",\"nonce\":\"13742787657\",\"sig256\":\"oDrpCGEp3gcqoHu+zTIQDk0B+t8=\",\"type\":\"VIRTUAL\",\"webhook\":{\"checked_at\":\"2025-02-20T12:56:09.742Z\",\"workspace_id\":\"67737e908861c2ba895d8d4c\"}}` byteData := []byte(jsonString) var payload WebhookPayload err := json.Unmarshal(byteData, &payload) if err != nil { fmt.Println(err) } fmt.Println(ValidateWebhook(`YOUR_WORKSPACE_SECRET`, payload)) } func ValidateWebhook(workspaceSecret string, payload WebhookPayload) bool { if payload.Sig256 == "" || payload.Nonce == "" || payload.Data == nil { fmt.Println("Missing required fields: sig256, nonce, or data") return false } jsonData, err := json.Marshal(payload.Data) if err != nil { fmt.Println(err) return false } serializedData := string(jsonData) h := hmac.New(sha256.New, []byte(workspaceSecret)) h.Write([]byte(serializedData)) h.Write([]byte(payload.Nonce)) digest := h.Sum(nil) // Encode computed signature in base64 computedSig := base64.StdEncoding.EncodeToString(digest) // Use constant-time comparison for security return subtle.ConstantTimeCompare([]byte(computedSig), []byte(payload.Sig256)) == 1 } ``` ## Native vs virtual webhooks Most APIs do not support webhooks natively, but we've built a robust virtualization system that allows you to subscribe to “virtual” webhooks for some integrations exactly as if they were “native” webhooks. Read more about the differences between native and virtual webhooks here: [Understanding virtual webhooks](/concepts/virtual_webhooks). ## Additional webhook features At Unified.to, you can pull historical data from your connection (i.e. the third-party account), select which fields to receive data from, manually trigger webhooks for testing purposes, observe an audit trail of webhook activity, and monitor the health of your webhooks. To learn how to use these features, see: [How to troubleshoot unhealthy webhooks](https://docs.unified.to/guides/how_to_troubleshoot_unhealthy_webhooks) --- # API Category Overviews ## accounting URL: https://docs.unified.to/accounting/overview The Unified Accounting API enables developers and product managers to access financial data from multiple accounting platforms through a single, standardized interface. Retrieve invoices, bills, transactions, contacts, and financial reports from QuickBooks, Xero, FreshBooks, and other major accounting platforms - all with one API. ## What is the Unified Accounting API? Accounting platforms like QuickBooks, Xero, and FreshBooks each have unique APIs with different authentication methods, data models, and endpoints. The Unified Accounting API normalizes these differences, allowing you to build once and support all major accounting platforms without maintaining separate integrations for each provider. ## Key Benefits for Developers - **Single Integration**: Write code once to support QuickBooks, Xero, FreshBooks, and more - no need to learn multiple accounting APIs - **Normalized Data Models**: Work with consistent invoice, bill, transaction, and financial report structures across all platforms - **Real-Time Data Access**: Every request hits the source API live with no caching, ensuring you always have up-to-date financial data - **Zero Maintenance**: No need to track API version changes or deprecations across multiple accounting platforms - **Faster Development**: Ship accounting features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred accounting platform without custom development - **Competitive Advantage**: Launch with support for all major accounting platforms while competitors build integrations one at a time - **Customer Flexibility**: Allow customers to access their financial data from QuickBooks, Xero, or any supported platform through your product - **Reduced Time-to-Market**: Get accounting integration features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new accounting platforms as they emerge without significant engineering resources ## Accounting Data Objects The Accounting API provides access to the following standardized objects: - **Accounts**: Chart of accounts, account types, and account balances - **Balance Sheet**: Assets, liabilities, and equity reporting - **Bills**: Vendor bills and payables - **Cash Flow**: Cash flow statements and reports - **Categories**: Expense and income categories - **Contacts**: Customers, vendors, and other business contacts - **Credit Memos**: Credit notes and customer credits - **Expenses**: Business expenses and spending records - **Invoices**: Customer invoices and receivables - **Journals**: Journal entries and general ledger data - **Orders**: Purchase and sales orders - **Organizations**: Company information and settings - **Profit & Loss**: Income statements and P&L reports - **Purchase Orders**: Vendor purchase orders - **Reports**: Various financial reports and analytics - **Sales Orders**: Customer sales orders - **Tax Rates**: Tax codes and rates - **Transactions**: Financial transactions and entries - **Trial Balance**: Trial balance reports ## Common Use Cases ### Financial Dashboards & Reporting Build unified dashboards that display financial data from multiple accounting platforms. Enable finance teams to view invoices, bills, expenses, and financial reports from QuickBooks, Xero, or any supported platform from a single interface. ### Expense Management Applications Create expense tracking and management tools that sync with customers' accounting systems. Pull expense data, categorize transactions, and push expense reports back to their accounting platform. ### Billing & Invoice Automation Automate billing workflows by accessing invoice data from accounting platforms. Create tools that monitor invoice status, track payments, and generate reports on accounts receivable. ### Financial Analytics & BI Pull financial data from accounting platforms into your data warehouse or business intelligence tools. Combine accounting data with other business metrics for comprehensive financial analytics and forecasting. ### Multi-Entity Consolidation Aggregate financial data from multiple accounting systems for businesses operating across different entities, subsidiaries, or regions. Create consolidated financial reports from multiple QuickBooks or Xero instances. ### Bookkeeping & Accounting Automation Build AI-powered tools that categorize transactions, reconcile accounts, and generate financial reports by accessing live data from accounting platforms. ## Supported Accounting Platforms The Unified Accounting API supports integration with major accounting platforms including QuickBooks, Xero, FreshBooks, Wave, Zoho Books, and many others. Each integration provides access to the standardized data objects listed above. ## Why Use a Unified Accounting API? **Traditional Approach:** - Build separate integrations for QuickBooks, Xero, FreshBooks, etc. - Learn 5+ different authentication systems and API specifications - Maintain code for 5+ different API endpoints, data formats, and error handling patterns - Monitor and update integrations when any platform deprecates or changes their API - Handle platform-specific quirks, rate limits, and pagination implementations **With Unified Accounting API:** - Integrate once with a single API that works across all accounting platforms - Use one authentication flow for all platforms - Work with normalized financial data objects - Automatic handling of API changes, rate limits, and platform-specific differences - Built-in support for webhooks and real-time data synchronization ## Integration Scenarios ### For Financial Software Add "Connect Your Accounting Software" features that work with any major accounting platform. Build expense management, budgeting, or financial planning tools without building separate integrations. ### For AI & Automation Tools Create AI-powered financial assistants that can read transaction data, analyze spending patterns, generate insights, and provide financial recommendations across multiple platforms using a single API. ### For Analytics & BI Platforms Pull accounting data from all major platforms into your data warehouse or analytics tool. Create unified financial reports, dashboards, and forecasting models without building multiple data pipelines. ### For Banking & Fintech Connect banking products with customers' accounting systems. Enable automatic transaction categorization, reconciliation, and financial reporting by accessing their accounting platform data. ### For E-commerce Platforms Sync e-commerce sales data with customers' accounting systems. Automatically create invoices, track inventory, and update financial records in their QuickBooks or Xero account. ## Platform-Specific Notes ### QuickBooks QuickBooks overwrites `taxrate_id` for some US-based accounts. You will need to enable Hybrid sales Tax or Automated sales tax within QuickBooks to specify `taxrate_id` for individual invoices. [Learn more](https://blogs.intuit.com/2017/12/11/using-quickbooks-online-api-automated-sales-tax/) ### GoCardless Creating a contact requires GoCardless Advanced or GoCardless Pro with approved payment pages. [Learn more](https://developer.gocardless.com/api-reference/#customers-create-a-customer) ## Real-Time, Live Data Unlike other integration platforms that cache data or run periodic sync jobs, the Unified Accounting API provides real-time access to financial data. Every API request hits the source platform directly, ensuring you always have the most current financial information. This real-time architecture is ideal for: - Financial dashboards displaying live invoice and expense data - Real-time expense tracking and approval workflows - Up-to-the-minute financial reporting and analytics - Automated reconciliation and bookkeeping workflows ## Privacy & Security Unified.to never stores your customers' financial data. All requests are stateless and pass through to the accounting platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. ### Note on Quickbooks Quickbooks overwrites `taxrate_id` for some US-based accounts. You will need to enable Hybrid sales Tax or Automated sales tax within Quickbooks to specify `taxrate_id` for individual invoices. [Learn more.](https://blogs.intuit.com/2017/12/11/using-quickbooks-online-api-automated-sales-tax/) ### Note on GoCardless Creating a contact requires GoCardless Advanced or GoCardless Pro with approved payment pages. [Learn more.](https://developer.gocardless.com/api-reference/#customers-create-a-customer) [Run In Postman](https://god.gw.postman.com/run-collection/16228585-f1672c61-3fc1-40b3-9a3b-853597940ab5?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-f1672c61-3fc1-40b3-9a3b-853597940ab5%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## ads URL: https://docs.unified.to/ads/overview The Unified Advertising API enables developers and product managers to access campaign data and performance metrics from multiple advertising platforms through a single, standardized interface. Retrieve campaigns, ads, and reports from Google Ads, Meta Ads (Facebook & Instagram), and TikTok Ads - all with one API. ## What is the Unified Advertising API? Advertising platforms like Google Ads, Meta Ads, and TikTok Ads each have unique APIs with different authentication methods, data models, and endpoints. The Unified Advertising API normalizes these differences, allowing you to build once and support all major advertising platforms without maintaining separate integrations for each provider. ## Key Benefits for Developers - **Single Integration**: Write code once to support Google Ads, Meta Ads, TikTok Ads, and more - no need to learn multiple advertising APIs - **Normalized Data Models**: Work with consistent campaign, ad, and performance data structures across all platforms - **Real-Time Data Access**: Every request hits the source API live with no caching, ensuring you always have up-to-date campaign and performance data - **Zero Maintenance**: No need to track API version changes or deprecations across multiple advertising platforms - **Faster Development**: Ship advertising features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred advertising platform without custom development - **Competitive Advantage**: Launch with support for all major ad platforms while competitors build integrations one at a time - **Customer Flexibility**: Allow customers to view and analyze campaigns across Google, Meta, and TikTok from a single interface - **Reduced Time-to-Market**: Get advertising analytics features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new advertising platforms as they emerge without significant engineering resources ## Advertising Data Objects The Advertising API provides access to the following standardized objects: - **Campaigns**: Ad campaigns with budgets, targeting, scheduling, spend data, and status information - **Ads**: Individual advertisements including ad type, creatives, copy, headlines, descriptions, call-to-action, and destination URLs - **Groups**: Ad groups with targeting configurations and settings - **Organizations**: Ad account information and organization details - **Reports**: Performance metrics including impressions, clicks, conversions, spend, and other KPIs ## Common Use Cases ### Multi-Platform Advertising Dashboards Build unified dashboards that display campaign performance across Google Ads, Meta Ads, and TikTok Ads side-by-side. Enable marketers to view and compare campaign data, ad performance, and spending across platforms from a single interface. ### Agency Reporting Platforms Create white-label reporting and analytics tools for agencies tracking campaign performance across multiple platforms for different clients. Support any advertising platform without building custom integrations for each one. ### AI-Powered Campaign Analytics Build AI agents and analytics tools that analyze performance data, identify trends, generate insights, and provide recommendations across all advertising platforms using a single, consistent data model. ### Cross-Platform Reporting & Attribution Aggregate advertising data from multiple platforms into unified reports. Track campaign performance across Google, Meta, and TikTok to understand multi-channel advertising effectiveness and ROI. ### Performance Monitoring & Alerts Create tools that monitor campaign performance, track spending, and send alerts when campaigns reach budget thresholds or performance metrics change across multiple advertising platforms. ### Data Warehousing & BI Integration Pull advertising data from all platforms into your data warehouse or business intelligence tools. Combine advertising data with other business metrics for comprehensive analytics and reporting. ## Supported Advertising Platforms The Unified Advertising API currently supports: - **Google Ads**: Access campaigns, ads, ad groups, and performance reports - **Meta Ads**: Retrieve Facebook and Instagram campaign and ad data - **TikTok Ads**: Access TikTok advertising campaign and ad information Additional platforms are continuously being added based on customer demand. ## Why Use a Unified Advertising API? **Traditional Approach:** - Build separate integrations for Google Ads, Meta Ads, TikTok Ads, etc. - Learn 3+ different authentication systems (OAuth flows, API keys, tokens) - Maintain code for 3+ different API endpoints, data formats, and error handling patterns - Monitor and update integrations when any platform deprecates or changes their API - Handle platform-specific rate limits, pagination, and webhook implementations **With Unified Advertising API:** - Integrate once with a single API that works across all advertising platforms - Use one authentication flow for all platforms - Work with normalized campaign, ad, and performance data objects - Automatic handling of API changes, rate limits, and platform-specific quirks - Built-in webhook support for real-time updates across all platforms ## Integration Scenarios ### For SaaS Applications Add "Connect Your Ad Accounts" features that work with any major advertising platform. Build marketing analytics, reporting, or performance monitoring tools that support Google, Meta, and TikTok without 3x the development effort. ### For AI & Automation Tools Create AI-powered advertising assistants that can read campaign data, analyze performance, generate insights, and provide recommendations across multiple platforms using a single, consistent API interface. ### For Analytics & BI Platforms Pull advertising data from all major platforms into your data warehouse or analytics tool. Create unified reports, dashboards, and attribution models without building multiple data pipelines. ### For Marketing Agencies Build proprietary dashboards and reporting tools for tracking client campaign performance across all advertising platforms. Support any platform your clients use without custom development for each one. ### For E-commerce Platforms Embed advertising analytics directly into your e-commerce platform, allowing merchants to view campaign performance and ad spend from Google Shopping, Facebook Ads, and TikTok Ads within your product. ## Real-Time, Live Data Unlike other integration platforms that cache data or run periodic sync jobs, the Unified Advertising API provides real-time access to advertising data. Every API request hits the source platform (Google, Meta, TikTok) directly, ensuring you always have the most current campaign performance, budget, and status information. This real-time architecture is ideal for: - AI applications that need fresh data for analysis and recommendations - Analytics dashboards displaying live performance metrics - Reporting tools that require up-to-the-minute campaign data - Monitoring systems that need accurate, real-time spend and performance data ## Privacy & Security Unified.to never stores your customers' advertising data. All requests are stateless and pass through to the advertising platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. --- **SEO Keywords**: unified advertising API, Google Ads API, Meta Ads API, Facebook Ads API, Instagram Ads API, TikTok Ads API, advertising platform integration, multi-platform ad reporting, advertising API for developers, campaign data API, ad performance tracking, unified marketing API, advertising analytics API, cross-platform advertising reports, ad tech integration, advertising data integration ## analytics URL: https://docs.unified.to/analytics/overview The Unified Analytics API enables developers and product managers to access web and product analytics data from multiple platforms through a single, standardized interface. Retrieve properties, events, sessions, visitors, and performance reports from Google Analytics, Mixpanel, PostHog, Pendo, and YouTube Analytics - all with one API. ## What is the Unified Analytics API? Web and product analytics platforms like Google Analytics, Mixpanel, PostHog, Pendo, and YouTube Analytics each have unique APIs with different authentication methods, data models, query languages, and reporting structures. The Unified Analytics API normalizes these differences, allowing you to build once and support traffic analysis, user behavior tracking, conversion reporting, and event ingestion across analytics providers without maintaining separate integrations for each one. ## Key Benefits for Developers - **Single Integration**: Write code once to support multiple analytics platforms - no need to learn different reporting APIs and event schemas - **Normalized Data Models**: Work with consistent property, event, session, visitor, and report structures across all platforms - **Real-Time Data Access**: Every request hits the source API live with no caching, ensuring you always have up-to-date analytics data - **Zero Maintenance**: No need to track API version changes or deprecations across multiple analytics platforms - **Faster Development**: Ship analytics features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred analytics platform without custom development - **Competitive Advantage**: Launch with support for major analytics tools while competitors build integrations one at a time - **Customer Flexibility**: Allow customers to view traffic, engagement, and conversion data from their analytics accounts through a single interface - **Reduced Time-to-Market**: Get analytics dashboards and reporting features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new analytics platforms as they emerge without significant engineering resources ## Analytics Data Objects The Analytics API provides access to the following standardized objects: - **Properties**: Websites, apps, and analytics accounts with name, timezone, currency, industry, and hierarchical parent relationships - **Events**: User interactions including page views, screen views, clicks, form submissions, purchases, sign-ups, video plays, and custom events - with page context, device info, geo data, UTM parameters, and monetary values - **Sessions**: User visit sessions with start and end times, duration, landing and exit pages, page view counts, acquisition source/medium/campaign, device and location data, and bounce or conversion flags - **Visitors**: Identified and anonymous users with profile attributes, engagement totals (sessions, page views, events), and first/last seen timestamps - **Reports**: Aggregated metrics over date ranges, filterable by metric type (users, sessions, page views, conversions, revenue, video engagement, and more) and dimension (date, page, source, campaign, country, device, and more) ## Common Use Cases ### Multi-Platform Analytics Dashboards Build unified dashboards that display traffic, engagement, and conversion metrics across Google Analytics, Mixpanel, PostHog, and other analytics platforms side-by-side. Enable marketers and product teams to compare performance, user behavior, and acquisition channels from a single interface. ### Agency & Client Reporting Create white-label reporting tools for agencies tracking website and product performance across multiple clients and analytics accounts. Support any connected analytics platform without building custom integrations for each one. ### Conversion & Funnel Analysis Query session and event data to analyze user journeys, track conversions, and measure funnel performance. Filter events by type (page views, purchases, sign-ups) and attribute results to traffic sources and campaigns. ### E-Commerce & Revenue Analytics Pull report metrics for transactions, revenue, average order value, and ecommerce conversion rates. Combine traffic and revenue data to understand which channels and pages drive the most value. ### Event Tracking & Data Ingestion Create events and visitors programmatically to send tracking data into connected analytics platforms. Record page views, custom events, and user attributes through a normalized event schema with UTM and geo fields. ### Data Warehousing & BI Integration Pull analytics data from all platforms into your data warehouse or business intelligence tools. Combine web analytics with CRM, advertising, and commerce data for comprehensive cross-channel reporting. ### AI-Powered Analytics Insights Build AI agents and analytics tools that analyze traffic patterns, identify trends, surface anomalies, and generate recommendations across analytics platforms using a single, consistent data model. ## Supported Analytics Platforms The Unified Analytics API currently supports: - **Google Analytics**: Access properties, events, and performance reports for website and app traffic - **Mixpanel**: Retrieve events, visitors, properties, and reports for product analytics and user behavior - **PostHog**: Access properties, events, sessions, visitors, and reports for open-source product analytics - **Pendo**: Retrieve properties, events, visitors, and reports for product adoption and in-app usage analytics - **YouTube Analytics**: Access properties and video performance reports for channel and content metrics Additional platforms are continuously being added based on customer demand. ## Why Use a Unified Analytics API? **Traditional Approach:** - Build separate integrations for Google Analytics, Mixpanel, PostHog, Pendo, and YouTube Analytics - Learn multiple authentication systems and reporting APIs - Maintain code for different event schemas, query parameters, and metric definitions - Monitor and update integrations when any platform deprecates or changes their API - Handle platform-specific rate limits, pagination, and dimension naming conventions **With Unified Analytics API:** - Integrate once with a single API that works across analytics platforms - Use one authentication flow for all platforms - Work with normalized property, event, session, visitor, and report objects - Automatic handling of API changes, rate limits, and platform-specific differences - Consistent filtering by property, date range, event type, source, medium, and campaign ## Integration Scenarios ### For SaaS Applications Add "Connect Your Analytics" features that work with any supported analytics platform. Build traffic dashboards, conversion reports, or user behavior tools without building separate integrations for each provider. ### For AI & Automation Tools Create AI-powered analytics assistants that read traffic data, analyze user behavior, identify opportunities, and provide recommendations across multiple platforms using a single API. ### For Analytics & BI Platforms Pull analytics data from all major platforms into your data warehouse or analytics tool. Create unified traffic reports, acquisition dashboards, and conversion analyses without building multiple data pipelines. ### For Marketing Agencies Build proprietary dashboards and reporting tools for tracking client website and campaign performance. Support any analytics platform your clients use without custom development for each one. ### For Product Teams Embed analytics insights directly into your product, allowing customers to view traffic, engagement, and conversion data from their connected analytics accounts within your application. ## Real-Time, Live Data Unlike other integration platforms that cache data or run periodic sync jobs, the Unified Analytics API provides real-time access to analytics data. Every API request hits the source platform directly, ensuring you always have the most current traffic, event, and report information. This real-time architecture is ideal for: - Analytics dashboards displaying live traffic and engagement metrics - AI applications that need fresh data for analysis and recommendations - Reporting tools that require up-to-the-minute conversion and revenue data - Monitoring systems that track real-time user behavior and session activity ## Privacy & Security Unified.to never stores your customers' analytics data. All requests are stateless and pass through to the analytics platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. --- **SEO Keywords**: unified analytics API, Google Analytics API, Mixpanel API, PostHog API, Pendo API, YouTube Analytics API, web analytics API, product analytics API, analytics platform integration, multi-platform analytics reporting, website traffic API, event tracking API, session analytics API, conversion tracking API, analytics data integration, unified marketing analytics, cross-platform analytics reports, visitor analytics API, analytics reporting API for developers ## assessment URL: https://docs.unified.to/assessment/overview The Unified Assessment API enables assessment and background check providers to seamlessly integrate with Applicant Tracking Systems (ATS) platforms through a single, standardized interface. Recruiters can request assessments for candidates directly from within their ATS—Workable, Ashby, Greenhouse, and others—while assessment providers receive orders via webhooks and submit results back to the ATS, all with one API. ## What is the Unified Assessment API? ATS platforms like Workable, Ashby, and Greenhouse each have their own assessment APIs with different authentication methods, data models, and endpoints. The Unified Assessment API normalizes these differences, allowing assessment providers to build once and support multiple ATS platforms without maintaining separate integrations for each provider. Unlike the Unified Verification API, where applications initiate requests to verification providers through Unified, the Assessment API connects directly into ATS “assessment APIs” and allows recruiters to request assessments without leaving their ATS. For assessment providers, this reduces friction for recruiters and keeps them in their familiar workflow. ## Key Benefits for Developers - **Single Integration**: Write code once to support Workable, Ashby, Greenhouse, and more—no need to learn multiple ATS assessment APIs - **Normalized Data Models**: Work with consistent assessment package, order, and result structures across all supported ATS platforms - **Real-Time Webhooks**: Receive assessment orders via webhooks as soon as recruiters request them, with no polling required - **Zero Maintenance**: No need to track API version changes or deprecations across multiple ATS assessment integrations - **Faster Development**: Ship ATS assessment integration features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred ATS platform without custom development - **Competitive Advantage**: Launch with support for Workable, Ashby, Greenhouse, and more while competitors build integrations one at a time - **Customer Flexibility**: Allow recruiters to request assessments from within their ATS without switching to a third-party interface - **Reduced Time-to-Market**: Get assessment integration features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new ATS platforms as they add assessment API support ## Assessment Data Objects The Assessment API provides access to the following standardized objects: - **Assessment Packages**: Available assessment packages, pricing, and package configuration that ATS platforms display to recruiters - **Assessment Orders**: Orders placed by recruiters when they request an assessment for a candidate, including candidate, job, and application details - **Order Results**: Scores, status, completion timestamps, and custom attributes submitted back to the ATS when assessments are complete ## Common Use Cases ### Candidate Assessment Integration Build assessment and testing tools that integrate with customers’ ATS platforms. Recruiters request assessments from within their ATS; your application receives orders via webhooks, sends assessments to candidates, and submits results back for recruiters to review. ### Background Check Workflows Connect background check services with ATS platforms that support assessment APIs. Recruiters initiate checks from within their ATS; your service processes the request, performs verification, and updates the order with results that appear directly in the ATS. ### Skills and Aptitude Testing Create skills assessment and aptitude testing products that work with Workable, Ashby, Greenhouse, and other supported ATS platforms. Recruiters order assessments for candidates; your platform delivers the test, scores it, and pushes results back to the ATS. ### Interview Preparation Assessments Build tools that deliver pre-interview assessments or preparation materials. Recruiters order assessments for candidates at specific pipeline stages; your application tracks completion and reports results back to the ATS. ### Behavioral and Personality Assessments Integrate behavioral or personality assessments into the hiring flow. Recruiters request assessments from within their ATS; your platform collects responses, computes scores, and submits structured results for recruiters to review alongside other candidate data. ## Supported ATS Platforms The Unified Assessment API supports integration with ATS platforms that offer assessment APIs, including Workable, Ashby, and Greenhouse. Each integration uses assessment-only connections that are separate from their standard ATS integrations. ## Why Use a Unified Assessment API? **Traditional Approach:** - Build separate integrations for Workable, Ashby, Greenhouse, and each new ATS that adds assessment support - Learn multiple authentication systems and API specifications - Maintain code for different webhook formats, data models, and result submission endpoints - Monitor and update integrations when any platform deprecates or changes their assessment API - Handle platform-specific package structures, order payloads, and result formats **With Unified Assessment API:** - Integrate once with a single API that works across supported ATS platforms - Use one authentication flow (Partner API Key) for all platforms - Work with normalized assessment package and order objects - Receive orders via standardized webhooks and submit results through a single update endpoint - Built-in webhook support for assessment order creation and cancellation events ## Integration Scenarios ### For Assessment Software Providers Add “Connect Your ATS” features that work with Workable, Ashby, Greenhouse, and other supported platforms. Configure assessment packages via the Unified API; receive orders via webhooks and submit results without building separate integrations for each ATS. ### For Background Check Providers Integrate background verification services with ATS platforms that support assessment APIs. Recruiters initiate checks from within their ATS; your service receives candidate details via webhooks, performs verification, and updates orders with results that appear in the ATS. ### For Skills Testing Platforms Connect skills and aptitude testing tools with customers’ ATS systems. Recruiters request assessments for candidates; your platform receives orders, delivers tests, scores responses, and pushes results back through the Unified Assessment API. ## Real-Time, Live Data The Unified Assessment API provides real-time access to assessment orders via webhooks. When a recruiter requests an assessment in their ATS, you receive a webhook immediately with the order details. Every result submission is written back to the source ATS in real time, ensuring recruiters see up-to-date assessment status and scores. This real-time architecture is ideal for: - Immediate notification when recruiters order assessments - Live order status and result updates in the ATS - Automated workflows that react to new orders and cancellations instantly ## Privacy & Security Unified.to never stores your customers’ assessment data. All requests are stateless and pass through to the ATS platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. ## ats URL: https://docs.unified.to/ats/overview The Unified ATS (Applicant Tracking System) API enables developers and product managers to access recruiting data from multiple ATS platforms through a single, standardized interface. Retrieve jobs, candidates, applications, interviews, documents, scorecards, and activities from Greenhouse, Lever, Workable, and other major ATS platforms - all with one API. ## What is the Unified ATS API? Applicant Tracking Systems (ATS) like Greenhouse, Lever, and Workable each have unique APIs with different authentication methods, data models, and endpoints. The Unified ATS API normalizes these differences, allowing you to build once and support all major ATS platforms without maintaining separate integrations for each provider. ATS platforms allow recruiters to track jobs, candidates, applications, interviews, documents and scorecards throughout the recruiting and hiring process. ## Key Benefits for Developers - **Single Integration**: Write code once to support Greenhouse, Lever, Workable, and more - no need to learn multiple ATS APIs - **Normalized Data Models**: Work with consistent job, candidate, application, and interview structures across all platforms - **Real-Time Data Access**: Every request hits the source API live with no caching, ensuring you always have up-to-date recruiting data - **Zero Maintenance**: No need to track API version changes or deprecations across multiple ATS platforms - **Faster Development**: Ship ATS integration features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred ATS platform without custom development - **Competitive Advantage**: Launch with support for all major ATS platforms while competitors build integrations one at a time - **Customer Flexibility**: Allow customers to access their recruiting data from Greenhouse, Lever, or any supported ATS through your product - **Reduced Time-to-Market**: Get recruiting integration features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new ATS platforms as they emerge without significant engineering resources ## ATS Data Objects The ATS API provides access to the following standardized objects: - **Activities**: Recruiting activities, notes, and candidate interactions - **Applications**: Job applications and candidate submissions - **Application Statuses**: Application stages and hiring pipeline status - **Candidates**: Applicant profiles, resumes, and candidate information - **Companies**: Employer organizations and company information - **Documents**: Resumes, cover letters, and candidate documents - **Interviews**: Interview schedules, feedback, and interview details - **Jobs**: Job postings, requisitions, and open positions - **Scorecards**: Interview scorecards and candidate evaluations ## Common Use Cases ### Recruiting Analytics & Reporting Build recruiting dashboards that display hiring metrics across multiple ATS platforms. Track time-to-hire, pipeline velocity, candidate sources, and recruiting performance from Greenhouse, Lever, or any supported ATS from a single interface. ### Candidate Sourcing Tools Create sourcing and talent acquisition tools that integrate with customers' ATS systems. Automatically add sourced candidates to job pipelines, track candidate engagement, and measure sourcing effectiveness. ### Interview Scheduling Automation Automate interview scheduling by accessing candidate and interview data from ATS platforms. Build tools that coordinate interviewer availability, send calendar invites, and track interview completion. ### Background Check Integration Connect background check services with ATS platforms. Automatically initiate background checks when candidates reach specific stages, update application status based on results, and store verification documents. ### Recruitment Marketing Build career site builders and recruitment marketing tools that sync with ATS job postings. Automatically publish jobs to career pages, track application sources, and measure recruitment marketing ROI. ### AI-Powered Candidate Screening Create AI tools that analyze candidate data, screen resumes, and rank applicants by accessing candidate and job information across multiple ATS platforms using a single API. ### Offer Management Build offer letter generation and approval tools that integrate with ATS systems. Access candidate information, track offer status, and update application stages when offers are accepted or declined. ## Supported ATS Platforms The Unified ATS API supports integration with major applicant tracking systems including Greenhouse, Lever, Workable, JazzHR, SmartRecruiters, iCIMS, Jobvite, and many others. Each integration provides access to the standardized data objects listed above. ## Why Use a Unified ATS API? **Traditional Approach:** - Build separate integrations for Greenhouse, Lever, Workable, etc. - Learn 5+ different authentication systems and API specifications - Maintain code for 5+ different API endpoints, data formats, and error handling patterns - Monitor and update integrations when any platform deprecates or changes their API - Handle platform-specific candidate data structures and field mappings **With Unified ATS API:** - Integrate once with a single API that works across all ATS platforms - Use one authentication flow for all platforms - Work with normalized job, candidate, application, and interview objects - Automatic handling of API changes, rate limits, and platform-specific differences - Built-in webhook support for real-time recruiting data updates ## Integration Scenarios ### For Recruiting Software Add "Connect Your ATS" features that work with any major ATS platform. Build recruiting tools, sourcing platforms, or interview solutions without building separate integrations for each ATS. ### For HR Analytics Platforms Pull recruiting data from all major ATS platforms into your analytics tool. Create unified hiring reports, pipeline dashboards, and diversity metrics without building multiple data pipelines. ### For Background Check Providers Integrate background verification services with customers' ATS systems. Automatically receive candidate information, update application status, and store verification results in their ATS. ### For Interview Platforms Connect video interviewing and assessment tools with ATS platforms. Sync interview schedules, push interview recordings and scorecards back to the ATS, and update candidate pipeline status. ### For Job Board Aggregators Build job distribution tools that publish jobs from ATS platforms to multiple job boards. Automatically sync job postings, track applications from different sources, and measure job board performance. ## Real-Time, Live Data Unlike other integration platforms that cache data or run periodic sync jobs, the Unified ATS API provides real-time access to recruiting data. Every API request hits the source platform directly, ensuring you always have the most current candidate and job information. This real-time architecture is ideal for: - Recruiting dashboards displaying live pipeline and candidate data - Real-time candidate status updates and notifications - Up-to-the-minute job posting and application information - Automated recruiting workflows that react to ATS changes instantly ## Privacy & Security Unified.to never stores your customers' candidate data. All requests are stateless and pass through to the ATS platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-7389d176-1245-4837-a523-9aac31b57ecb?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-7389d176-1245-4837-a523-9aac31b57ecb%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## auth URL: https://docs.unified.to/auth/overview Allow your application to sign-in your users with an OAuth2 or SAML integration. There are two ways to use the authentication APIs: - Redirect your user to the [OAuth2 login endpoint](/auth/login/Sign_in_a_user){:target="\_blank"} / [SAML SSO endpoint](/auth/saml/Sign_in_a_user_via_SAML){:target="\_blank"} with a specific `integration_type` and your `workspace_id` - Call the [Integrations API endpoint](/unified/integration/Returns_all_activated_integrations_in_a_workspace){:target="\_blank"} with `categories=auth,saml` to get a list of authentication integrations, then construct the sign-in URL for each integration: **For OAuth2 login:** `https://api.unified.to/unified/integration/auth/{workspace_id}/{integration.type}?redirect=true` **For SAML SSO login:** `https://api.unified.to/unified/integration/saml/{workspace_id}/{integration.type}?redirect=true` Authentication-only integrations should not be used to create connections. They are intended to sign in your users. If you're trying to authorize your customers and create connections on your own, refer to our tutorial: [Customize your authorization flow with the Unified API](/tutorials/customize-auth-flow). ## Instructions ### 1. Activate authentication integrations Go to [https://app.unified.to/integrations?tab=auth](https://app.unified.to/integrations?tab=auth){:target="\_blank"} or [https://app.unified.to/integrations?tab=saml](https://app.unified.to/integrations?tab=saml){:target="\_blank"} and activate integrations that you would like to have your users sign-in to your application with. ### 2. Display Sign-in links to your users ### 2.1. Use our embedded Embedded Sign-in widget (OAuth2-only) Go to [https://app.unified.to/settings/embed](https://app.unified.to/settings/embed?tab=Sign-in){:target="\_blank"} and configure our embedded sign-in widget. ### 2.2. Use our getActivatedIntegrations API Call the [getActivatedIntegrations](https://docs.unified.to/unified/integration/Returns_all_activated_integrations_in_a_workspace) API endpoint to retrieve a list of activated authentication integrations `categories=auth` or `categories=saml`. ```js [ { logo_url: 'https://api.unified.to/docs/images/google.png', name: 'Google', type: 'google', }, ]; ``` Construct the sign-in URL from your `workspace_id` and each integration's `type`: **For OAuth2 login:** `https://api.unified.to/unified/integration/auth/{workspace_id}/{integration.type}?redirect=true` **For SAML SSO login:** `https://api.unified.to/unified/integration/saml/{workspace_id}/{integration.type}?redirect=true` The Sign-in URL can have the following optional parameters: | | | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `redirect=true` | redirect the user or if empty, will return the URL as string in its response | | `success_redirect` | the URL that the user will be redirected to once they have successfully authenticated with that integration vendor | | `failure_redirect` | the URL that the user will be redirected to if there is an error or other issue preventing the user from being authenticated by the integration vendor | | `state` | a string that will be sent back to the success_redirect. You can use this to remember a user ID or other identifier. | ### 3. Verify the login Once the user successfully signs-in, they are redirected back to your application (or to the location of the `success_redirect` URL parameter from step 2). A `jwt` parameter will be appended to that URL. The `jwt` is a base64 encoded [JWT](https://jwt.io/) and is signed with your Workspace Secret found at [https://app.unified.to/settings/api](https://app.unified.to/settings/api){:target="\_blank"}. Verify the JWT with your workspace secret on your server (NOT in your browser as the workspace secret is not public). Example code to verify a JWT on a NodeJS server: ```js try { let result = JWT.verify(req.payload.jwt, workspace.secret); } catch (err) { console.error(err); } ``` The decoded JWT will contain `name` and `emails` field: ```js { "name": "Jane Smith", "emails": ["jane@foo.com", "jsmith89@gmail.com"] } ``` Use the emails to log the user into your application as it is verified by the integration. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-f6e189b9-b71c-4a09-8a47-f6763f9c24dc?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-f6e189b9-b71c-4a09-8a47-f6763f9c24dc%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## calendar URL: https://docs.unified.to/calendar/overview The Unified Calendar API enables developers and product managers to access calendar and scheduling data from multiple calendar platforms through a single, standardized interface. Retrieve calendars, events, busy times, scheduling links, and meeting recordings from Google Calendar, Outlook Calendar, Office 365, and other major calendar platforms - all with one API. ## What is the Unified Calendar API? Calendar platforms like Google Calendar, Outlook, and Office 365 each have unique APIs with different authentication methods, data models, and endpoints. The Unified Calendar API normalizes these differences, allowing you to build once and support all major calendar platforms without maintaining separate integrations for each provider. ## Calendar Data Objects The Unified Calendar API gives you access to: - **Busy Times**: A person's free/busy availability and time blocks - **Calendars**: Calendar accounts and calendar information - **Events**: Calendar events, meetings, and appointments - **Links**: Scheduling links and booking pages - **Recordings**: Meeting recordings and transcripts ## Common Use Cases ### Scheduling & Booking Tools Build scheduling assistants and appointment booking tools that check availability across multiple calendar platforms. Access free/busy times from Google Calendar, Outlook, or any supported platform to find optimal meeting times. ### Meeting Analytics Analyze meeting patterns and time usage by accessing calendar event data. Track meeting frequency, duration, attendees, and calendar utilization across organizations. ### Calendar Sync Applications Create calendar sync tools that keep multiple calendars in sync. Mirror events between Google Calendar and Outlook, or sync personal and work calendars automatically. ### AI Meeting Assistants Build AI-powered meeting assistants that access calendar events, meeting recordings, and transcripts. Generate meeting summaries, extract action items, and provide meeting insights. ### Resource Scheduling Develop resource booking systems that integrate with calendar platforms. Schedule conference rooms, equipment, or other resources by accessing calendar availability data. ### Time Tracking Integration Connect time tracking tools with calendar data. Automatically log time based on calendar events, categorize meetings, and generate timesheets from calendar information. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-ae23181a-d514-493e-acba-89870fd0d3e0?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-ae23181a-d514-493e-acba-89870fd0d3e0%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## cdp URL: https://docs.unified.to/cdp/overview The Unified CDP API enables developers and their applications to access customer data platform profiles, segments, events, sources, destinations, and activations from multiple CDPs through a single, standardized interface. Work with Adobe Experience Platform, Twilio Segment, Salesforce Data Cloud, mParticle, and other major CDPs - all with one API. ## What is the Unified CDP API? Customer Data Platforms (CDPs) like Twilio Segment, Adobe Experience Platform, Salesforce Data Cloud, and mParticle each have unique APIs with different authentication methods, data models, and endpoints. The Unified CDP API normalizes these differences, allowing you to build once and support all major CDPs without maintaining separate integrations for each provider. A CDP collects, unifies, and activates customer data across marketing, product, and analytics systems. It typically manages customer profiles, behavioral events, audience segments, data sources, activation destinations, and sync jobs. ## Key Benefits for Developers - **Single Integration**: Write code once to support Twilio Segment, Adobe Experience Platform, Salesforce Data Cloud, mParticle, and more - no need to learn multiple CDP APIs - **Normalized Data Models**: Work with consistent profile, segment, event, source, destination, and activation structures across all platforms - **Real-Time Data Access**: Every request hits the source API live with no caching, ensuring you always have up-to-date customer and audience data - **Zero Maintenance**: No need to track API version changes or deprecations across multiple CDP platforms - **Faster Development**: Ship CDP integration features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred CDP without custom development - **Competitive Advantage**: Launch with support for major CDPs while competitors build integrations one at a time - **Customer Flexibility**: Allow customers to manage profiles, audiences, and activations across Segment, Adobe, Salesforce Data Cloud, and other CDPs from a single interface - **Reduced Time-to-Market**: Get customer data and audience features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new CDP platforms as they emerge without significant engineering resources ## CDP Data Objects The CDP API provides access to the following standardized objects: - **Profiles**: Unified customer profiles including identity, contact details, traits/metadata, segment membership, consent preferences, and anonymous vs. known status - **Segments**: Audiences and cohorts with membership rules, compute mode (realtime or batch), size, and active status - **Events**: Behavioral and identity events (track, page, screen, identify, group, alias) with timestamps, identifiers, and event properties - **Sources**: Inbound data connectors that feed profiles and events into the CDP - **Destinations**: Outbound activation destinations where audience and profile data is sent - **Activations**: Sync jobs that push segments (and related data) to destinations on a schedule or in realtime ## Common Use Cases ### Multi-CDP Customer 360 Dashboards Build dashboards that display unified customer profiles, segment membership, and recent events across Twilio Segment, Adobe Experience Platform, Salesforce Data Cloud, and other CDPs. Give marketers and product teams a single view of customer identity and engagement. ### Audience Sync & Activation Tools Create tools that list segments, destinations, and activations so users can sync audiences to ad platforms, email tools, and analytics destinations without learning each CDP's activation model. ### Event Streaming & Analytics Pipelines Ingest and query behavioral events from multiple CDPs into your analytics stack. Normalize track, page, screen, and identify events into a consistent schema for reporting, attribution, and product analytics. ### Identity Resolution & Profile Enrichment Use profile identifiers (email, user ID, anonymous ID, device ID, CRM ID, and more) and consent data to power identity stitching, enrichment workflows, and privacy-aware personalization across platforms. ### AI-Powered Customer Insights Build AI agents that analyze profile traits, segment membership, and event history to generate insights, recommend next-best actions, and detect churn or conversion opportunities across any connected CDP. ### Data Warehouse & BI Integration Pull profiles, segments, events, and activation status from all supported CDPs into your data warehouse or BI tools. Combine CDP data with CRM, ads, and product metrics for end-to-end customer analytics. ## Supported CDP Platforms The Unified CDP API currently supports: - **Adobe Experience Platform**: Access segments, sources, destinations, and activations - **Bloomreach Engagement**: Retrieve profiles and events - **BlueConic**: Access profiles, segments, events, sources, and destinations - **Lytics**: Retrieve segments, profiles, sources, destinations, and activations - **mParticle**: Access profiles and sources - **Salesforce Data Cloud**: Retrieve profiles, sources, and activations - **Twilio Segment**: Access segments, profiles, sources, destinations, and activations - **Tealium**: Retrieve segments - **Treasure Data**: Access segments, sources, and destinations Additional platforms are continuously being added based on customer demand. ## Why Use a Unified CDP API? **Traditional Approach:** - Build separate integrations for Segment, Adobe Experience Platform, Salesforce Data Cloud, mParticle, etc. - Learn multiple authentication systems (OAuth flows, API keys, tokens, workspace credentials) - Maintain code for different profile, audience, event, and activation models - Monitor and update integrations when any platform deprecates or changes their API - Handle platform-specific identity schemas, consent models, and activation pipelines **With Unified CDP API:** - Integrate once with a single API that works across all major CDPs - Use one authentication flow for all platforms - Work with normalized profile, segment, event, source, destination, and activation objects - Automatic handling of API changes, rate limits, and platform-specific quirks - Built-in webhook support for real-time updates across all platforms ## Integration Scenarios ### For SaaS Applications Add "Connect Your CDP" features that work with any major customer data platform. Build audience management, profile lookup, or activation monitoring tools that support Segment, Adobe, Salesforce Data Cloud, and more without multiplying engineering effort. ### For AI & Automation Tools Create AI-powered customer data assistants that can read profiles, analyze event streams, inspect segment membership, and recommend activations across multiple CDPs using a single, consistent API interface. ### For Analytics & BI Platforms Pull customer profiles, segments, and events from all major CDPs into your data warehouse or analytics tool. Create unified audience reports and identity graphs without building multiple data pipelines. ### For Marketing & Ad Tech Platforms Sync audiences and profile attributes from customers' CDPs into your advertising, email, or personalization product. Support the CDPs your customers already use without custom connectors for each one. ### For Privacy & Consent Platforms Access consent preferences and profile identifiers across CDPs to support privacy workflows, preference centers, and compliance tooling with a normalized consent model. ## Real-Time, Live Data Unlike other integration platforms that cache data or run periodic sync jobs, the Unified CDP API provides real-time access to customer data platform data. Every API request hits the source platform (Segment, Adobe Experience Platform, Salesforce Data Cloud, and others) directly, ensuring you always have the most current profiles, segments, events, and activation status. This real-time architecture is ideal for: - AI applications that need fresh customer and event data for analysis and recommendations - Dashboards displaying live audience sizes, profile traits, and activation status - Activation tools that require up-to-date segment membership before syncing destinations - Monitoring systems that need accurate, real-time event and sync health data ## Privacy & Security Unified.to never stores your customers' CDP data. All requests are stateless and pass through to the CDP platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. --- **SEO Keywords**: unified CDP API, customer data platform API, Twilio Segment API, Adobe Experience Platform API, Salesforce Data Cloud API, mParticle API, BlueConic API, Lytics API, Tealium API, Treasure Data API, Bloomreach API, customer profile API, audience segment API, event tracking API, CDP activation API, identity resolution API, multi-CDP integration, customer 360 API, audience sync API, CDP data integration ## clubs URL: https://docs.unified.to/clubs/overview The Unified Clubs API enables developers and product managers to access sports club and team data from multiple platforms through a single, standardized interface. Retrieve groups, members, events, venues, and activities from Strava, RevSpot, TeamSnap, PlayMQ, and other sports and fitness platforms - all with one API. ## What is the Unified Clubs API? Sports and fitness platforms like Strava, RevSpot, TeamSnap, and PlayMQ each have unique APIs with different authentication methods, data models, and endpoints. The Unified Clubs API normalizes these differences, allowing you to build once and support major club, team, and activity platforms without maintaining separate integrations for each provider. ## Clubs Data Objects The Clubs API provides access to the following standardized objects: - **Activities**: Workouts, training sessions, and athletic activities with distance, duration, elevation, and performance metrics - **Events**: Games, practices, and scheduled events with opponents, scores, status, and venue details - **Groups**: Sports clubs, teams, racing groups, and organizations with membership and sport metadata - **Locations**: Venues, fields, and facilities with addresses and coordinates - **Members**: Athletes, players, coaches, and club members with roles and membership status ## Common Use Cases ### Team & League Management Build team management tools that work across youth sports, league, and club platforms. Access rosters, schedules, and event details from TeamSnap, PlayMQ, or any supported platform in a single application. ### Athlete Performance Analytics Create training and performance dashboards by aggregating activity data from fitness and endurance platforms. Track distance, pace, heart rate, elevation, and workout history across Strava and other activity providers. ### Schedule & Venue Coordination Develop scheduling tools that sync games, practices, and events with venue and location data. Manage calendars, opponents, scores, and facility assignments without building separate integrations for each sports platform. ### Club Membership & Rosters Integrate membership and roster data across club and team platforms. List members, track admin roles, and reflect membership status for coaches, parents, and league administrators. ### Multi-Platform Sports Apps Let users connect the platforms they already use—whether a running club on Strava, a youth team on TeamSnap, or a league on PlayMQ—and present groups, events, and activities in one unified experience. ### Parent & Coach Tools Build apps for parents and coaches that surface upcoming events, practice locations, team rosters, and recent athletic activity from connected accounts across supported integrations. ## commerce URL: https://docs.unified.to/commerce/overview The Unified Commerce API enables developers and product managers to access e-commerce data from multiple platforms through a single, standardized interface. Retrieve products, inventory, collections, locations, reviews, and sales channel data from Shopify, WooCommerce, BigCommerce, and other major e-commerce platforms - all with one API. ## What is the Unified Commerce API? E-Commerce refers to the buying and selling of goods and services over the internet, enabling businesses to reach customers globally and conduct transactions digitally. E-commerce platforms like Shopify, WooCommerce, and BigCommerce each have unique APIs with different authentication methods and data models. The Unified Commerce API normalizes these differences. ## Commerce Data Objects The Commerce API provides access to the following standardized objects: - **Collections**: Product collections, categories, and catalogs - **Inventory**: Stock levels, inventory tracking, and warehouse data - **Items**: Products, SKUs, variants, and product information - **Locations**: Store locations, warehouses, and fulfillment centers - **Reviews**: Product reviews, ratings, and customer feedback - **Sales Channels**: Sales channels, marketplaces, and distribution channels ## Common Use Cases ### Multi-Platform Product Management Enable retailers to easily integrate and manage online marketplaces with the top e-commerce platforms. Sync product catalogs, inventory levels, and pricing across Shopify, WooCommerce, and other platforms from a single interface. ### Inventory Synchronization Streamline inventory management across multiple sellers and platforms. Keep stock levels synchronized, prevent overselling, and manage inventory across different sales channels and warehouses. ### Product Data Integration Import product data from different e-commerce stores to create a unified catalog across multiple platforms. Aggregate product information, images, descriptions, and pricing for analytics or marketplace integration. ### Review & Rating Aggregation Collect and analyze product reviews from multiple e-commerce platforms. Build review management tools that aggregate customer feedback, analyze sentiment, and display ratings across all sales channels. ### E-commerce Analytics Pull product, inventory, and sales channel data into analytics platforms. Track product performance, inventory turnover, and channel effectiveness across multiple e-commerce systems. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-522803c5-6821-4e1a-b548-3f62a090921a?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-522803c5-6821-4e1a-b548-3f62a090921a%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## crm URL: https://docs.unified.to/crm/overview The Unified CRM API enables developers and product managers to access customer relationship management data from multiple CRM platforms through a single, standardized interface. Retrieve contacts, companies, deals, pipelines, leads, and events from Salesforce, HubSpot, Pipedrive, and other major CRM platforms - all with one API. ## What is the Unified CRM API? Customer Relationship Management (CRM) platforms like Salesforce, HubSpot, and Pipedrive each have unique APIs with different authentication methods, data models, and endpoints. The Unified CRM API normalizes these differences, allowing you to build once and support all major CRM platforms without maintaining separate integrations for each provider. A CRM solution is typically used by salespeople to manage their sales prospects. It is also used by marketing and customer support/success teams as well. ## Key Benefits for Developers - **Single Integration**: Write code once to support Salesforce, HubSpot, Pipedrive, and more - no need to learn multiple CRM APIs - **Normalized Data Models**: Work with consistent contact, company, deal, and pipeline structures across all platforms - **Real-Time Data Access**: Every request hits the source API live with no caching, ensuring you always have up-to-date CRM data - **Zero Maintenance**: No need to track API version changes or deprecations across multiple CRM platforms - **Faster Development**: Ship CRM integration features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred CRM platform without custom development - **Competitive Advantage**: Launch with support for all major CRM platforms while competitors build integrations one at a time - **Customer Flexibility**: Allow customers to access their sales data from Salesforce, HubSpot, or any supported CRM through your product - **Reduced Time-to-Market**: Get CRM features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new CRM platforms as they emerge without significant engineering resources ## CRM Data Objects The CRM API provides access to the following standardized objects: - **Companies**: Organizations, accounts, and business entities - **Contacts**: People, leads, and customer contact information - **Deals**: Opportunities, sales deals, and revenue pipeline - **Events**: Activities, meetings, calls, and customer interactions - **Leads**: Prospective customers and lead information - **Pipelines**: Sales pipelines, stages, and deal progression ## Common Use Cases ### Sales Enablement Tools Build sales productivity tools that sync with customers' CRM systems. Access contact and deal data to provide sales intelligence, automate follow-ups, and track sales performance across Salesforce, HubSpot, or any supported CRM. ### Customer Data Platforms Create unified customer profiles by aggregating data from CRM platforms and other sources. Combine contact information, deal history, and interaction data from multiple CRMs into a single customer view. ### Marketing Automation Integration Sync marketing campaign data with CRM contacts and leads. Track how marketing activities influence sales pipeline and revenue by connecting marketing platforms with customers' CRM data. ### AI-Powered Sales Assistants Build AI agents that analyze CRM data, identify sales opportunities, predict deal outcomes, and provide recommendations by accessing contact, company, and deal information across multiple CRM platforms. ### Analytics & Reporting Dashboards Pull CRM data into analytics platforms to create custom sales reports, pipeline visualizations, and revenue forecasts. Combine CRM data with other business metrics for comprehensive business intelligence. ### Lead Routing & Assignment Automate lead distribution by accessing lead and contact data from CRM platforms. Build intelligent lead routing systems that assign leads based on territory, product, or sales rep capacity. ## Supported CRM Platforms The Unified CRM API supports integration with major CRM platforms including Salesforce, HubSpot, Pipedrive, Zoho CRM, Microsoft Dynamics, Close, Copper, and many others. Each integration provides access to the standardized data objects listed above. ## Why Use a Unified CRM API? **Traditional Approach:** - Build separate integrations for Salesforce, HubSpot, Pipedrive, etc. - Learn 5+ different authentication systems and API specifications - Maintain code for 5+ different API endpoints, data formats, and error handling patterns - Monitor and update integrations when any platform deprecates or changes their API - Handle platform-specific field mappings, custom objects, and data structures **With Unified CRM API:** - Integrate once with a single API that works across all CRM platforms - Use one authentication flow for all platforms - Work with normalized contact, company, deal, and pipeline objects - Automatic handling of API changes, rate limits, and platform-specific differences - Built-in webhook support for real-time CRM data updates ## Integration Scenarios ### For SaaS Applications Enable your end-users to add an integration to their CRM account from your Integrations Page in your software application. Support any major CRM platform without building separate integrations for each one. ### For AI & Automation Tools Create AI-powered CRM assistants that can read contact data, analyze sales patterns, generate insights, and provide sales recommendations across multiple platforms using a single API. ### For Analytics & BI Platforms Pull CRM data from all major platforms into your data warehouse or analytics tool. Create unified sales reports, pipeline dashboards, and revenue forecasts without building multiple data pipelines. ### For Communication Tools Enhance communication platforms with CRM context. Display contact information, deal status, and interaction history from customers' CRM systems directly in your chat, email, or phone tools. ### For Project Management Connect project management with CRM data. Automatically create projects from won deals, assign tasks to account owners, and track client deliverables linked to CRM accounts. ## Real-Time, Live Data Unlike other integration platforms that cache data or run periodic sync jobs, the Unified CRM API provides real-time access to CRM data. Every API request hits the source platform directly, ensuring you always have the most current contact, company, and deal information. This real-time architecture is ideal for: - Sales dashboards displaying live pipeline and deal data - Real-time lead routing and assignment workflows - Up-to-the-minute contact and company information - Automated sales workflows that react to CRM changes instantly ## Privacy & Security Unified.to never stores your customers' CRM data. All requests are stateless and pass through to the CRM platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-8acafcf5-aec0-4160-8e1f-4682042f11e1?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-8acafcf5-aec0-4160-8e1f-4682042f11e1%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## datastore URL: https://docs.unified.to/datastore/overview The Unified Datastore API enables developers and product managers to access structured data from databases, data lakes, and spreadsheets through a single, standardized interface. Retrieve databases, tables, records, and run queries against Google Sheets, Airtable, Snowflake, and other data platforms - all with one API. ## What is the Unified Datastore API? Data platforms like Google Sheets, Airtable, and Snowflake each have unique APIs with different authentication methods, data models, and query languages. The Unified Datastore API normalizes these differences, allowing you to build once and read and write tabular data across spreadsheets, operational databases, and cloud data warehouses without maintaining separate integrations for each provider. ## Datastore Data Objects The Datastore API provides access to the following standardized objects: - **Databases**: Workbooks, bases, schemas, and data lake connections with metadata and access details - **Queries**: Structured queries and SQL with filters, sorting, pagination, and aggregations - **Records**: Rows and entries with typed field values mapped to a common schema - **Tables**: Sheets, tables, and views with column definitions, field types, and relationships ## Common Use Cases ### Spreadsheet & No-Code Data Sync Build tools that sync data between Google Sheets, Airtable, and other tabular platforms. Read and write records across spreadsheets and bases without building custom integrations for each product. ### Analytics & Reporting Create dashboards and reports by querying data from Snowflake and other data platforms. Run filtered queries, aggregations, and counts to power BI tools and internal reporting. ### ETL & Data Pipelines Develop data pipelines that extract records from spreadsheets and operational stores, transform them with a consistent query model, and load them into warehouses or downstream systems. ### Operational App Backends Use connected spreadsheets and databases as lightweight backends for internal tools and workflows. List tables, create and update records, and query data through one API regardless of the underlying platform. ### Cross-Platform Data Migration Migrate data between platforms by reading tables and records from one integration and writing them to another. Move bases, sheets, and table schemas with normalized field types and relationships. ### AI & Automation Workflows Build AI agents and automations that read structured data, filter records, and update rows across Google Sheets, Airtable, Snowflake, and other supported integrations using a single query and record model. ## enrich URL: https://docs.unified.to/enrich/overview The Unified Enrichment API enables developers and product managers to access data enrichment services from multiple platforms through a single, standardized interface. Enrich company and person data using Clearbit, FullContact, Hunter, and other major data enrichment platforms - all with one API. ## What is the Unified Enrichment API? Data enrichment platforms like Clearbit, FullContact, and Hunter each have unique APIs with different authentication methods and data models. The Unified Enrichment API normalizes these differences, allowing you to build once and support all major enrichment platforms without maintaining separate integrations. ## Enrichment Data Objects The Enrichment API provides access to the following standardized objects: - **Companies**: Company information, firmographics, and business data - **Persons**: Person profiles, contact information, and demographics ## Common Use Cases ### Lead Enrichment Automatically enrich leads with additional company and contact information. Turn email addresses into full profiles with job titles, company data, social profiles, and more. ### CRM Data Enhancement Enrich CRM contacts and accounts with up-to-date firmographic and demographic data. Fill in missing fields, validate information, and keep customer records current. ### Sales Intelligence Build sales tools that provide enriched prospect information. Access company size, industry, revenue, technology stack, and decision-maker details to help sales teams prioritize and personalize outreach. ### Marketing Personalization Enrich marketing database with additional attributes for better segmentation and personalization. Access company and person data to create more targeted marketing campaigns. ### Form Simplification Reduce form fields by enriching partial data. Capture just an email address and automatically fill in company name, job title, location, and other details using enrichment APIs. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-1c980c5b-55cd-482c-be46-68a6baee892b?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-1c980c5b-55cd-482c-be46-68a6baee892b%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## forms URL: https://docs.unified.to/forms/overview The Unified Forms API enables developers and product managers to access form and survey data from multiple form builder platforms through a single, standardized interface. Retrieve forms, submissions, fields, and responses from Typeform, Google Forms, Jotform, SurveyMonkey, and other major form platforms - all with one API. ## What is the Unified Forms API? Form platforms like Typeform, Google Forms, Jotform, and SurveyMonkey each have unique APIs with different authentication methods, data models, and endpoints. The Unified Forms API normalizes these differences, allowing you to build once and support all major form platforms without maintaining separate integrations for each provider. Form builders are used by businesses to collect information from customers, employees, and partners through surveys, registration forms, feedback forms, lead capture forms, and more. ## Key Benefits for Developers - **Single Integration**: Write code once to support Typeform, Google Forms, Jotform, and more - no need to learn multiple form APIs - **Normalized Data Models**: Work with consistent form, submission, and field structures across all platforms - **Real-Time Data Access**: Every request hits the source API live with no caching, ensuring you always have up-to-date form responses - **Zero Maintenance**: No need to track API version changes or deprecations across multiple form platforms - **Faster Development**: Ship form integration features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred form platform without custom development - **Competitive Advantage**: Launch with support for all major form builders while competitors build integrations one at a time - **Customer Flexibility**: Allow customers to access their form data from Typeform, Google Forms, or any supported platform through your product - **Reduced Time-to-Market**: Get form integration features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new form platforms as they emerge without significant engineering resources ## Forms Data Objects The Forms API provides access to the following standardized objects: - **Forms**: Form templates, questions, field definitions, settings, and configuration including published URLs and active status - **Submissions**: Form responses with answers, respondent information, and submission timestamps ## Common Use Cases ### Lead & Customer Data Capture Collect leads and customer information from multiple form platforms into your CRM or database. Automatically sync form submissions from Typeform, Google Forms, or any form builder into your sales and marketing systems. ### Survey & Feedback Analysis Build analytics dashboards that aggregate survey responses and feedback from multiple form platforms. Analyze customer satisfaction, employee engagement, and product feedback collected through various form tools. ### Form Response Automation Create automated workflows triggered by form submissions. When someone fills out a contact form, registration form, or survey, automatically trigger actions in your application or send data to other systems. ### Multi-Channel Data Collection Aggregate data collected through different form platforms into a unified database. Collect information through Typeform surveys, Google Forms, and other tools while maintaining a single source of truth. ### AI-Powered Form Analytics Build AI agents that analyze form responses, identify trends, generate insights, and provide recommendations by accessing submission data across multiple form platforms through a single interface. ### Lead Scoring & Qualification Automatically score and qualify leads based on form submission data. Analyze responses from contact forms, registration forms, and surveys to prioritize and route leads to the right teams. ### Compliance & Data Management Centralize form data collection for compliance and data governance. Ensure consistent data handling across all form platforms and maintain audit trails of form submissions. ## Supported Form Platforms The Unified Forms API supports integration with major form builder platforms including Typeform, Google Forms, Jotform, SurveyMonkey, Microsoft Forms, Wufoo, Formstack, and many others. Each integration provides access to the standardized data objects listed above. ## Why Use a Unified Forms API? **Traditional Approach:** - Build separate integrations for Typeform, Google Forms, Jotform, etc. - Learn 5+ different authentication systems and API specifications - Maintain code for 5+ different API endpoints, data formats, and error handling patterns - Monitor and update integrations when any platform deprecates or changes their API - Handle platform-specific field types, response formats, and data structures **With Unified Forms API:** - Integrate once with a single API that works across all form platforms - Use one authentication flow for all platforms - Work with normalized form and submission objects - Automatic handling of API changes, rate limits, and platform-specific differences - Built-in webhook support for real-time form submission notifications ## Integration Scenarios ### For SaaS Applications Add "Connect Your Forms" features that work with any major form platform. Build marketing tools, CRM systems, or data collection platforms that automatically sync form submissions without building separate integrations for each form builder. ### For AI & Automation Tools Create AI-powered assistants that can access form data, analyze responses, identify patterns, and provide insights across multiple platforms using a single, consistent API interface. ### For Analytics & BI Platforms Pull form submission data from all major platforms into your data warehouse or analytics tool. Create unified response dashboards, trend analysis, and reporting without building multiple data pipelines. ### For Marketing Automation Connect marketing automation platforms with form builder tools. Automatically add form respondents to email campaigns, segment audiences based on form responses, and track conversion funnels. ### For Customer Data Platforms Unify customer data collected through various form platforms. Build comprehensive customer profiles by combining form responses with other customer interaction data. ## Real-Time, Live Data Unlike other integration platforms that cache data or run periodic sync jobs, the Unified Forms API provides real-time access to form data. Every API request hits the source platform directly, ensuring you always have the most current form submissions and responses. This real-time architecture is ideal for: - Lead capture systems that need immediate access to new submissions - Real-time notification systems for form responses - Automated workflows triggered by form submissions - Dashboards displaying up-to-the-minute form response data ## Privacy & Security Unified.to never stores your customers' form data or responses. All requests are stateless and pass through to the form platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. ## genai URL: https://docs.unified.to/genai/overview The Unified Generative AI API enables developers and product managers to access AI capabilities from multiple platforms through a single, standardized interface. Work with AI models, generate embeddings, and manage prompts across OpenAI, Anthropic, Google AI, and other major AI platforms - all with one API. ## What is the Unified Generative AI API? AI platforms like OpenAI, Anthropic, and Google AI each have unique APIs with different authentication methods and data models. The Unified Generative AI API normalizes these differences, allowing you to build once and support all major AI platforms without maintaining separate integrations. ## Generative AI Data Objects The Generative AI API provides access to the following standardized objects: - **Embeddings**: Text embeddings and vector representations - **Models**: AI models, capabilities, and model information - **Prompts**: Prompt templates, configurations, and prompt management ## Common Use Cases ### Multi-Model AI Applications Build AI applications that work with multiple AI providers. Switch between OpenAI, Anthropic, or other models without rewriting integration code, enabling fallback strategies and cost optimization. ### AI Model Comparison Create tools that compare outputs from different AI models. Test the same prompt across multiple providers to evaluate quality, cost, and performance. ### Embedding Generation Generate text embeddings using different AI platforms. Build vector search, semantic similarity, or RAG (Retrieval Augmented Generation) applications that work with any embedding provider. ### Prompt Management Tools Develop prompt management and testing tools that work across AI platforms. Store, version, and test prompts with different models to optimize AI application performance. ### AI Cost Optimization Build tools that route AI requests to different providers based on cost, availability, or performance. Implement intelligent load balancing across multiple AI platforms. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-55c9c20f-5abd-4ef9-b409-ae6ac13c2814?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-55c9c20f-5abd-4ef9-b409-ae6ac13c2814%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## hris URL: https://docs.unified.to/hris/overview The Unified HRIS (Human Resources Information Systems) API enables developers and product managers to access HR data from multiple HR platforms through a single, standardized interface. Retrieve employee information, groups, locations, benefits, payroll, time off, and more from BambooHR, Workday, ADP, and other major HRIS platforms - all with one API. ## What is the Unified HRIS API? HRIS platforms like BambooHR, Workday, and ADP each have unique APIs with different authentication methods, data models, and endpoints. The Unified HRIS API normalizes these differences, allowing you to build once and support all major HR platforms without maintaining separate integrations for each provider. HRIS solutions mostly deal with employees and their groups. Groups represent collections of personnel at a company and may have different labels depending on the company type and size (departments, teams, divisions, etc.). ## Key Benefits for Developers - **Single Integration**: Write code once to support BambooHR, Workday, ADP, and more - no need to learn multiple HRIS APIs - **Normalized Data Models**: Work with consistent employee, group, and HR data structures across all platforms - **Real-Time Data Access**: Every request hits the source API live with no caching, ensuring you always have up-to-date employee data - **Zero Maintenance**: No need to track API version changes or deprecations across multiple HR platforms - **Faster Development**: Ship HRIS integration features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred HR platform without custom development - **Competitive Advantage**: Launch with support for all major HRIS platforms while competitors build integrations one at a time - **Customer Flexibility**: Allow customers to access their employee data from BambooHR, Workday, or any supported HRIS through your product - **Reduced Time-to-Market**: Get HR integration features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new HRIS platforms as they emerge without significant engineering resources ## HRIS Data Objects The HRIS API provides access to the following standardized objects: - **Benefits**: Health insurance, retirement plans, and employee benefits - **Companies**: Organization information and company entities - **Deductions**: Payroll deductions and withholdings - **Devices**: Company-issued devices and equipment assigned to employees - **Employees**: Employee profiles, contact information, and employment details - **Groups**: Departments, teams, divisions, and organizational units - **Locations**: Office locations, work sites, and geographic information - **Payslips**: Pay stubs and payroll information - **Time Off**: Vacation, sick leave, PTO requests and balances - **Time Shifts**: Work schedules, shifts, and time tracking ## Common Use Cases ### Employee Directory & Org Charts Build employee directories and organizational charts that sync with customers' HRIS systems. Display current employee information, reporting structures, departments, and contact details from BambooHR, Workday, or any supported platform. ### Identity & Access Management Automate user provisioning and de-provisioning by syncing with HRIS employee data. Automatically create accounts when employees are hired, update permissions when they change roles, and revoke access when they leave. ### Benefits Administration Create benefits enrollment and management tools that integrate with HRIS platforms. Access employee benefit information, eligibility, and enrollment status across multiple HR systems. ### Time & Attendance Tracking Build time tracking and attendance tools that sync with HRIS systems. Access employee schedules, time off balances, and shift information to create comprehensive workforce management solutions. ### Payroll Integration Connect payroll systems with HRIS data. Access employee information, deductions, and pay schedules to automate payroll processing and reporting. ### HR Analytics & Reporting Pull employee data from HRIS platforms into analytics tools. Create custom HR reports, analyze workforce trends, track turnover, and generate insights from employee data across multiple HR systems. ### Onboarding & Offboarding Automation Automate employee onboarding and offboarding workflows by accessing HRIS data. Trigger workflows when new employees are added or when employees are terminated, ensuring consistent processes across all HR platforms. ## Supported HRIS Platforms The Unified HRIS API supports integration with major HR platforms including BambooHR, Workday, ADP, Namely, Gusto, Rippling, Personio, and many others. Each integration provides access to the standardized data objects listed above. ## Why Use a Unified HRIS API? **Traditional Approach:** - Build separate integrations for BambooHR, Workday, ADP, etc. - Learn 5+ different authentication systems and API specifications - Maintain code for 5+ different API endpoints, data formats, and error handling patterns - Monitor and update integrations when any platform deprecates or changes their API - Handle platform-specific employee data structures and field mappings **With Unified HRIS API:** - Integrate once with a single API that works across all HRIS platforms - Use one authentication flow for all platforms - Work with normalized employee, group, and HR data objects - Automatic handling of API changes, rate limits, and platform-specific differences - Built-in webhook support for real-time employee data updates ## Integration Scenarios ### For SaaS Applications Add "Connect Your HR System" features that work with any major HRIS platform. Build productivity tools, collaboration software, or workforce management solutions without building separate integrations for each HR platform. ### For IT & Security Tools Automate identity and access management by syncing with HRIS employee data. Provision user accounts, manage permissions, and enforce security policies based on current employee status and organizational structure. ### For AI & Automation Tools Create AI-powered HR assistants that can access employee data, analyze workforce patterns, generate insights, and provide recommendations across multiple platforms using a single API. ### For Analytics & BI Platforms Pull HR data from all major platforms into your data warehouse or analytics tool. Create unified workforce reports, retention dashboards, and headcount analytics without building multiple data pipelines. ### For Benefits Providers Connect benefits administration with customers' HRIS systems. Automatically sync employee eligibility, enrollment data, and coverage information between benefits platforms and HR systems. ## Real-Time, Live Data Unlike other integration platforms that cache data or run periodic sync jobs, the Unified HRIS API provides real-time access to HR data. Every API request hits the source platform directly, ensuring you always have the most current employee information. This real-time architecture is ideal for: - Employee directories with up-to-date contact information - Real-time user provisioning and de-provisioning - Current organizational charts and reporting structures - Automated workflows that react to employee changes instantly ## Privacy & Security Unified.to never stores your customers' employee data. All requests are stateless and pass through to the HRIS platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-ab78fdcd-2c27-43a9-9b00-ada3b765020e?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-ab78fdcd-2c27-43a9-9b00-ada3b765020e%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## kms URL: https://docs.unified.to/kms/overview The Unified Knowledge Management System (KMS) API enables developers and product managers to access knowledge base data from multiple platforms through a single, standardized interface. Retrieve spaces, pages, and comments from Confluence, Notion, Guru, and other major knowledge management platforms - all with one API. ## What is the Unified KMS API? Knowledge management platforms like Confluence, Notion, and Guru each have unique APIs with different authentication methods and data models. The Unified KMS API normalizes these differences, allowing you to build once and support all major KMS platforms without maintaining separate integrations. ## KMS Data Objects The KMS API provides access to the following standardized objects: - **Comments**: Page comments, discussions, and feedback - **Pages**: Wiki pages, documents, and knowledge articles - **Spaces**: Workspaces, knowledge bases, and content hierarchies ## Common Use Cases ### Knowledge Base Search Build unified search tools that query across multiple knowledge management platforms. Search Confluence, Notion, and other platforms simultaneously to find relevant documentation and information. ### Documentation Portals Create documentation portals that aggregate content from multiple knowledge bases. Display knowledge articles from different sources in a single, searchable interface. ### AI Knowledge Assistants Build AI assistants that access knowledge base content to answer questions. Retrieve relevant pages and documentation from Confluence, Notion, or other platforms to power AI-driven support tools. ### Content Migration Tools Develop tools that migrate or sync content between different knowledge management platforms. Move documentation from Confluence to Notion, or keep multiple knowledge bases synchronized. ### Knowledge Analytics Analyze knowledge base usage patterns. Track page views, popular content, outdated articles, and content gaps across multiple knowledge management platforms. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-0d64464b-a325-4eec-8c53-6ab2a6c83fc3?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-0d64464b-a325-4eec-8c53-6ab2a6c83fc3%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## lms URL: https://docs.unified.to/lms/overview The Unified Learning Management System (LMS) API enables developers and product managers to access educational data from multiple LMS platforms through a single, standardized interface. Retrieve courses, classes, students, and instructor data from Canvas, Moodle, Blackboard, and other major LMS platforms - all with one API. ## What is the Unified LMS API? Learning management platforms like Canvas, Moodle, and Blackboard each have unique APIs with different authentication methods and data models. The Unified LMS API normalizes these differences, allowing you to build once and support all major LMS platforms without maintaining separate integrations. ## LMS Data Objects The LMS API provides access to the following standardized objects: - **Classes**: Class sections, sessions, and enrollments - **Courses**: Course catalogs, curricula, and course information - **Instructors**: Teachers, professors, and instructor profiles - **Students**: Student profiles, enrollments, and learner data ## Common Use Cases ### Learning Analytics Build learning analytics dashboards that aggregate data from multiple LMS platforms. Track student engagement, course completion rates, and learning outcomes across Canvas, Moodle, or any supported platform. ### Student Information Systems Integrate student information systems with LMS platforms. Sync student enrollments, course registrations, and academic records between different educational systems. ### Educational Tools & Extensions Create educational tools that work with any LMS platform. Build study aids, assignment helpers, or collaboration tools that integrate with students' and instructors' existing LMS. ### Reporting & Compliance Generate educational reports and compliance documentation by accessing course and student data from LMS platforms. Track accreditation requirements, learning objectives, and educational outcomes. ### Course Marketplace Integration Connect course marketplaces or content providers with LMS platforms. Automatically provision courses, sync enrollments, and track learner progress across different systems. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-4e8ac380-28f2-4883-97f2-a83627c1659a?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-4e8ac380-28f2-4883-97f2-a83627c1659a%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## martech URL: https://docs.unified.to/martech/overview The Unified Marketing Automation API enables developers and product managers to access marketing campaign data from multiple marketing platforms through a single, standardized interface. Retrieve lists, members, and campaign information from Mailchimp, HubSpot Marketing, ActiveCampaign, and other major marketing automation platforms - all with one API. ## What is the Unified Marketing Automation API? A Marketing Automation solution can be email marketing software or a marketing CRM, enabling businesses to manage and automate their marketing campaigns and customer communications. Each platform has unique APIs with different authentication methods and data models. The Unified Marketing Automation API normalizes these differences. ## Marketing Data Objects Marketing API endpoints typically work with the following objects: - **Lists**: Marketing lists (also known as Campaigns or Audiences in other platforms) - **Members**: List members and contacts (similar to CRM Contacts, and may be identical when sourced from the same platform) ## Common Use Cases ### Campaign Analytics Dashboards Analyze Campaigns: Leverage real-time aggregated campaign data from multiple sources into a single dashboard. Track email performance, list growth, and engagement metrics across Mailchimp, HubSpot, and other platforms. ### Lead Nurturing Integration Nurture Leads: Integrate customer data from multiple systems to automate nurturing workflows. Sync contacts between marketing platforms and CRM systems to create seamless lead nurturing campaigns. ### Content Personalization Personalize Content: Power AI content recommendations with customer data from various marketing systems. Access member profiles and engagement history to deliver personalized marketing content. ### Marketing List Management Build tools that manage marketing lists across multiple platforms. Sync subscriber lists, manage unsubscribes, and segment audiences across different email marketing and marketing automation systems. ### Multi-Platform Email Analytics Aggregate email campaign performance data from multiple marketing platforms. Create unified reports showing email open rates, click-through rates, and conversion metrics across all marketing tools. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-a2293725-e649-46e6-ba69-9fb8bb15876f?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-a2293725-e649-46e6-ba69-9fb8bb15876f%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## messaging URL: https://docs.unified.to/messaging/overview The Unified Messaging API enables developers and product managers to access communication data from multiple messaging platforms through a single, standardized interface. Retrieve channels, messages, and events from Slack, Microsoft Teams, Discord, and other major messaging platforms - all with one API. ## What is the Unified Messaging API? Messaging platforms like Slack, Microsoft Teams, and Discord each have unique APIs with different authentication methods and data models. The Unified Messaging API normalizes these differences, allowing you to build once and support all major messaging platforms without maintaining separate integrations. ## Messaging Data Objects The Messaging API provides access to the following standardized objects: - **Channels**: Channels, rooms, groups, and conversations - **Events**: Message events, reactions, and activity notifications - **Messages**: Chat messages, threads, and direct messages ## Common Use Cases ### Chatbots & Virtual Assistants Build chatbots that work across Slack, Teams, Discord, and other messaging platforms. Create a single bot that can respond to messages, answer questions, and automate workflows across all platforms. ### Message Analytics & Insights Analyze communication patterns across messaging platforms. Track message volume, response times, channel activity, and team collaboration metrics from Slack, Teams, or any supported platform. ### Cross-Platform Messaging Create tools that sync messages between different messaging platforms. Bridge communications from Slack to Teams, or enable cross-platform team collaboration. ### Compliance & Archiving Build compliance and archiving tools that capture messages from multiple messaging platforms. Store, search, and analyze messages for regulatory compliance and e-discovery. ### Workflow Automation Automate workflows triggered by messaging events. Create tools that perform actions based on specific messages, keywords, or reactions across all messaging platforms. ## Handling threaded messages Use the `parent_id` for maximum flexibility in handling threaded messages. Note: `parent_message_id` and `root_message_id` are deprecated. `parent_id`: Represents the **immediate predecessor message** in a thread. This allows you to identify which message a given message directly replies to. Threading relationships form a **tree** of messages, where each reply points to its parent ### Replying to Messages - Set `parent_id` to the ID of the message being replied to. This ensures the reply is properly threaded and appears under the correct message across all integrations that support threaded conversations. ### Listing Messages in a Thread You can retrieve messages within a thread using the `parent_id` relationship. #### Using the `expand` Filter When listing messages, you can set the `expand` filter to `true` to automatically include **all nested descendants** of a message, not just its immediate children. This provides a full flat view of the thread hierarchy in a single query. ### Consistency Across Integrations Using the `parent_id` structure ensures: - **Consistent message hierarchy** across sender and receiver systems. - **Simplified logic** for reconstructing conversation trees. If a message does **not** include a `parent_id`, it is treated as either: - A **standalone message**, or - The **root** of a new thread. ## Working with Hierarchical Data in Messaging APIs Messaging integrations typically organize data in a hierarchical (tree-like) structure. For detailed guidance on traversing hierarchical data, refer to our guide: [How to traverse hierarchical data](https://docs.unified.to/guides/working_with_hierarchical_tree_data_in_storage_messaging_and_kms_apis) [Run In Postman](https://god.gw.postman.com/run-collection/16228585-a38b869f-6c79-4b37-b300-ac2abc4e9811?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-a38b869f-6c79-4b37-b300-ac2abc4e9811%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## metadata URL: https://docs.unified.to/metadata/overview The Metadata API provides a seamless way to view additional object definitions (i.e., metadata). The Metadata **objects** field will display unified object types as keys, and the corresponding values will be arrays of IDs associated with those objects. For example: ``` { "objects": { "commerce_item": ["item1", "item2"], "commerce_collection": ["collection1", "collection2"], "commerce_item_variant": ["variant1", "variant2"] } } ``` The Metadata API enhances the `commerce_metadata `object, which is being deprecated. The API is being released starting with **commerce_item**, **commerce_collection**, and **commerce_item_variant** with expanded support for other unified models coming in the future. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-c29da10b-4d4b-42e9-8c66-182bdff740ec?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-c29da10b-4d4b-42e9-8c66-182bdff740ec%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## passthrough URL: https://docs.unified.to/passthrough/overview The Passthrough API allows you to access SaaS endpoints through your existing connections, even if those endpoints aren't explicitly supported in our unified models. Think of it as a way to "pass through" your API requests directly to the third-party platform while still leveraging Unified.to's connection management and authentication handling. ::callout For examples of the Passthrough API in action, see: [How to use the Passthrough API](https://docs.unified.to/guides/how_to_use_the_passthrough_api) :: ## How it works When you make a Passthrough API request: 1. You send your request to Unified.to's Passthrough endpoint, specifying: - The connection ID for the integration you want to access - The platform-specific API path you want to call - Any HTTP method, headers, query parameters, or body data needed 2. Unified.to handles: - Authentication with the platform using your stored connection credentials - Routing your request to the correct platform API endpoint 3. The SaaS application processes your request and sends back a response 4. Unified.to forwards the response back to you without modification ### Path construction The path you provide should be relative to the platform's base API URL. For example: - If the full endpoint URL is `https://api.vendor.com/v2/users` - You would specify `v2/users` as the path You can find the base URL for each integration under the **Feature Support > Passthrough** tab on [app.unified.to/integrations](https://app.unified.to/integrations) (click through to the integration to see it). ### Headers, parameters, and payloads - You can send any platform-specific headers your request needs, such as `Content-Type` - For write operations, you can include a payload in the request - Query parameters can be included in the path or as separate parameters - All headers from the response are passed back to you - There is only one reserved URL parameter, and that is `__domain`. Use it to override the default API URL for that integration. ### Response handling Unlike Unified.to's standard endpoints which return normalized data, Passthrough API responses contain raw data. This means: - Response formats will vary between platforms - You'll need to handle platform-specific data structures - Error formats will be platform-specific [Run In Postman](https://god.gw.postman.com/run-collection/16228585-6ad0f5a9-728f-4872-ab03-28c25cf7f1b0?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-6ad0f5a9-728f-4872-ab03-28c25cf7f1b0%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## payment URL: https://docs.unified.to/payment/overview The Unified Payment API enables developers and product managers to access payment data from multiple payment platforms through a single, standardized interface. Retrieve payment links, transactions, payouts, refunds, and subscriptions from Stripe, PayPal, Square, and other major payment platforms - all with one API. ## What is the Unified Payment API? Payment platforms like Stripe, PayPal, and Square each have unique APIs with different authentication methods and data models. The Unified Payment API normalizes these differences, allowing you to build once and support all major payment platforms without maintaining separate integrations. ## Payment Data Objects The Payment API provides access to the following standardized objects: - **Links**: Payment links and checkout URLs - **Payments**: Payment transactions and charges - **Payouts**: Transfers, payouts, and disbursements - **Refunds**: Refunds, chargebacks, and reversals - **Subscriptions**: Recurring payments and subscription billing ## Common Use Cases ### Payment Analytics & Reporting Build financial dashboards that aggregate payment data from multiple platforms. Track revenue, transaction volume, refund rates, and subscription metrics across Stripe, PayPal, or any supported platform. ### Multi-Platform Payment Processing Enable customers to connect their preferred payment processor. Build e-commerce, SaaS, or marketplace applications that work with any major payment platform without building separate integrations. ### Subscription Management Create subscription management tools that work across different payment platforms. Track recurring revenue, manage subscriptions, analyze churn, and monitor subscriber growth. ### Reconciliation & Accounting Automate payment reconciliation by accessing transaction data from payment platforms. Match payments with invoices, track payouts, and sync payment data with accounting systems. ### Payment Insights & Fraud Detection Build tools that analyze payment patterns, detect fraud, and provide insights by accessing payment and refund data across multiple payment platforms. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-b36e2b16-73d2-4f81-b966-db0553054911?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-b36e2b16-73d2-4f81-b966-db0553054911%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## repo URL: https://docs.unified.to/repo/overview The Unified Repository API enables developers and product managers to access version control data from multiple repository platforms through a single, standardized interface. Retrieve repositories, commits, branches, pull requests, and organization data from GitHub, GitLab, Bitbucket, and other major version control platforms - all with one API. ## What is the Unified Repository API? Version control platforms like GitHub, GitLab, and Bitbucket each have unique APIs with different authentication methods and data models. The Unified Repository API normalizes these differences, allowing you to build once and support all major repository platforms without maintaining separate integrations. ## Repository Data Objects The Repository API provides access to the following standardized objects: - **Branches**: Git branches and branch information - **Commits**: Git commits, commit history, and changes - **Organizations**: GitHub organizations, GitLab groups, and workspace data - **Pull Requests**: Pull requests, merge requests, and code reviews - **Repositories**: Code repositories and repository metadata ## Common Use Cases ### Development Analytics Build developer productivity dashboards that aggregate data from GitHub, GitLab, or Bitbucket. Track commit activity, pull request velocity, code review metrics, and team contribution patterns. ### Code Review Tools Create code review and collaboration tools that work across different repository platforms. Access pull request data, review comments, and approval status from any version control system. ### CI/CD Integration Integrate continuous integration and deployment tools with multiple repository platforms. Trigger builds on commits, track deployment status, and manage release workflows across GitHub, GitLab, or Bitbucket. ### Repository Management Build repository management tools that work across platforms. Automate repository creation, manage access permissions, and synchronize repository settings across different version control systems. ### Developer Productivity Tools Create tools that analyze code commits, identify bottlenecks, track project progress, and provide insights by accessing commit and pull request data across multiple repository platforms. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-6d8ac32b-39b9-48b4-9b74-03ed21e37a42?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-6d8ac32b-39b9-48b4-9b74-03ed21e37a42%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## scim URL: https://docs.unified.to/scim/overview Use the SCIM API to manage user provisioning, deprovisioning, and attribute updates in an employee directory. Our SCIM API is based on the [RFC 7644](https://datatracker.ietf.org/doc/html/rfc7644) standard with extensions in the User schema: - urn:ietf:params:scim:schemas:extension:enterprise:2.0:User - urn:ietf:params:scim:schemas:extension:lattice:attributes:1.0:User - urn:ietf:params:scim:schemas:extension:peakon:2.0:User ## Supported Integrations All [HR integrations](/hris/integrations) support our SCIM API, regardless if that end API supports the SCIM specification. To add a SCIM integration, search for HR integrations and activate them. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-8d5e8501-1a0a-4760-915f-cfc345beff58?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-8d5e8501-1a0a-4760-915f-cfc345beff58%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## shipping URL: https://docs.unified.to/shipping/overview The Unified Shipping API enables developers and product managers to access shipping data and functionality from multiple shipping platforms through a single, standardized interface. Retrieve shipments, tracking information, shipping rates, labels, and carrier data from ShipStation, Shippo, EasyPost, FedEx, UPS, and other major shipping platforms - all with one API. ## What is the Unified Shipping API? Shipping platforms like ShipStation, Shippo, EasyPost, FedEx, and UPS each have unique APIs with different authentication methods, data models, and endpoints. The Unified Shipping API normalizes these differences, allowing you to build once and support all major shipping platforms without maintaining separate integrations for each provider. Shipping platforms enable businesses to create shipping labels, track packages, compare carrier rates, and manage fulfillment operations across multiple carriers and services. ## Key Benefits for Developers - **Single Integration**: Write code once to support ShipStation, Shippo, EasyPost, FedEx, UPS, and more - no need to learn multiple shipping APIs - **Normalized Data Models**: Work with consistent shipment, tracking, label, and rate structures across all platforms - **Real-Time Data Access**: Every request hits the source API live with no caching, ensuring you always have up-to-date shipping and tracking data - **Zero Maintenance**: No need to track API version changes or deprecations across multiple shipping platforms - **Faster Development**: Ship shipping integration features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred shipping platform without custom development - **Competitive Advantage**: Launch with support for all major shipping platforms while competitors build integrations one at a time - **Customer Flexibility**: Allow customers to access their shipping data from ShipStation, Shippo, or any supported platform through your product - **Reduced Time-to-Market**: Get shipping features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new shipping platforms as they emerge without significant engineering resources ## Shipping Data Objects The Shipping API provides access to the following standardized objects: - **Addresses**: Shipping addresses, validation, and address information - **Carriers**: Shipping carriers, services, and carrier account information - **Labels**: Shipping labels, label generation, and label data - **Packages**: Package information, dimensions, weight, and package details - **Rates**: Shipping rates, rate quotes, and carrier pricing - **Shipments**: Shipment records, shipping orders, and fulfillment data - **Tracking**: Package tracking information, delivery status, and tracking events ## Common Use Cases ### Multi-Carrier Shipping Management Build shipping management tools that work with any major shipping platform. Enable customers to create labels, compare rates, and track shipments across FedEx, UPS, USPS, DHL, and other carriers from a single interface. ### E-commerce Fulfillment Integration Integrate shipping functionality into e-commerce platforms. Automatically generate shipping labels, track orders, and update customers with delivery status by connecting with customers' preferred shipping platforms. ### Shipping Analytics & Reporting Create dashboards that aggregate shipping data from multiple platforms. Track shipping costs, delivery times, carrier performance, and fulfillment metrics across ShipStation, Shippo, or any supported platform. ### Rate Comparison Tools Build tools that compare shipping rates across multiple carriers and platforms. Help customers find the best shipping options by accessing rate data from all connected shipping platforms. ### Order Fulfillment Automation Automate order fulfillment workflows by accessing shipment and label data from shipping platforms. Automatically create labels, update order status, and send tracking information to customers. ### Inventory & Warehouse Management Connect warehouse management systems with shipping platforms. Sync inventory data with shipping operations, track package locations, and manage fulfillment across multiple warehouses and carriers. ### Returns Management Build returns processing tools that integrate with shipping platforms. Automatically generate return labels, track return shipments, and update return status across multiple carriers. ### Shipping Cost Optimization Create tools that analyze shipping patterns and optimize costs by accessing rate and shipment data across multiple platforms. Identify opportunities to reduce shipping expenses and improve carrier selection. ## Supported Shipping Platforms The Unified Shipping API supports integration with major shipping platforms including ShipStation, Shippo, EasyPost, FedEx, UPS, USPS, DHL, Canada Post, and many others. Each integration provides access to the standardized data objects listed above. ## Why Use a Unified Shipping API? **Traditional Approach:** - Build separate integrations for ShipStation, Shippo, EasyPost, FedEx, UPS, etc. - Learn 5+ different authentication systems and API specifications - Maintain code for 5+ different API endpoints, data formats, and error handling patterns - Monitor and update integrations when any platform deprecates or changes their API - Handle platform-specific shipment data structures, rate formats, and label generation methods **With Unified Shipping API:** - Integrate once with a single API that works across all shipping platforms - Use one authentication flow for all platforms - Work with normalized shipment, tracking, label, and rate objects - Automatic handling of API changes, rate limits, and platform-specific differences - Built-in webhook support for real-time shipping updates and tracking events ## Integration Scenarios ### For E-commerce Platforms Add "Connect Your Shipping Account" features that work with any major shipping platform. Enable merchants to generate labels, track orders, and manage fulfillment without building separate integrations for each shipping provider. ### For Order Management Systems Integrate shipping functionality into order management and fulfillment systems. Access shipment data, generate labels, and track deliveries across multiple shipping platforms from a single interface. ### For Shipping Analytics Platforms Pull shipping data from all major platforms into your analytics tool. Create unified shipping reports, carrier performance dashboards, and cost analysis without building multiple data pipelines. ### For Warehouse Management Systems Connect WMS platforms with shipping providers. Automatically create shipments, generate labels, and update inventory based on shipping data from multiple platforms. ### For Returns Management Tools Build returns processing solutions that integrate with customers' shipping platforms. Automatically generate return labels, track return shipments, and update return status across all major carriers. ### For Multi-Channel Sellers Enable sellers managing inventory across multiple channels to access shipping functionality from any platform. Support ShipStation, Shippo, or any shipping provider without custom development. ## Real-Time, Live Data Unlike other integration platforms that cache data or run periodic sync jobs, the Unified Shipping API provides real-time access to shipping data. Every API request hits the source platform directly, ensuring you always have the most current shipment status, tracking information, and rate data. This real-time architecture is ideal for: - Shipping dashboards displaying live shipment and tracking data - Real-time order fulfillment and label generation - Up-to-the-minute delivery status and tracking updates - Automated shipping workflows that react to platform changes instantly ## Privacy & Security Unified.to never stores your customers' shipping data. All requests are stateless and pass through to the shipping platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. ## signing URL: https://docs.unified.to/signing/overview The Unified E-Signature API enables developers and product managers to access electronic signature data from multiple e-signature platforms through a single, standardized interface. Create and send documents for signature, manage signatories, use templates, and track signing status across DocuSign, Adobe Sign, Dropbox Sign, PandaDoc, and other major e-signature platforms - all with one API. ## What is the Unified E-Signature API? Electronic signature platforms like DocuSign, Adobe Sign, Dropbox Sign, and PandaDoc each have unique APIs with different authentication methods, data models, and endpoints. The Unified E-Signature API normalizes these differences, allowing you to build once and support all major e-signature platforms without maintaining separate integrations for each provider. E-signature platforms are used by sales, legal, HR, and operations teams to prepare, send, and collect legally binding electronic signatures on contracts, agreements, NDAs, offer letters, and other documents. ## Key Benefits for Developers - **Single Integration**: Write code once to support DocuSign, Adobe Sign, Dropbox Sign, PandaDoc, and more - no need to learn multiple e-signature APIs - **Normalized Data Models**: Work with consistent document, signatory, and template structures across all platforms - **Real-Time Data Access**: Every request hits the source API live with no caching, ensuring you always have up-to-date signing status and document data - **Zero Maintenance**: No need to track API version changes or deprecations across multiple e-signature platforms - **Faster Development**: Ship e-signature integration features in days instead of months of integration work ## Benefits for Product Managers - **Multi-Platform Support**: Offer customers the ability to connect their preferred e-signature platform without custom development - **Competitive Advantage**: Launch with support for all major e-signature platforms while competitors build integrations one at a time - **Customer Flexibility**: Allow customers to send and track signature requests through DocuSign, Adobe Sign, or any supported platform from within your product - **Reduced Time-to-Market**: Get e-signature features to market quickly without waiting for engineering to build multiple integrations - **Scalable Product Strategy**: Easily add new e-signature platforms as they emerge without significant engineering resources ## E-Signature Data Objects The E-Signature API provides access to the following standardized objects: - **Documents**: Signing documents, agreements, and contracts with status, sent and completed timestamps, expiration, and download URLs - **Signatories**: Signers and recipients on a document, including name, email, role, signing order, and signature status - **Templates**: Reusable document templates for creating new signing documents with predefined fields and signatories ## Common Use Cases ### Contract & Agreement Workflows Build contract management tools that send, track, and store signed agreements across multiple e-signature platforms. Automatically generate contracts, route them to signatories, and update records when documents are fully executed. ### Sales Proposal & Quote Signing Integrate e-signature capabilities into CPQ and proposal tools. Send quotes and proposals for signature through the customer's preferred e-signature platform and track when deals are closed. ### HR & Onboarding Automation Automate offer letters, employment agreements, NDAs, and onboarding paperwork. Send new-hire documents for signature through any supported e-signature platform and update HRIS records when documents are completed. ### Legal Document Management Build legal operations and contract lifecycle management (CLM) tools that integrate with any major e-signature platform. Track document status, manage signing order, and store executed documents alongside matter records. ### Real Estate & Lending Send disclosures, loan documents, lease agreements, and closing paperwork for signature. Track multi-party signing workflows across buyers, sellers, agents, and lenders using a single API. ### AI-Powered Document Automation Build AI agents that generate documents from templates, route them to the correct signatories, monitor signing status, and take follow-up actions when documents are signed, declined, or expired - across any supported e-signature platform. ### Compliance & Audit Trails Pull signing events, completed documents, and signatory data into compliance and audit systems. Create unified audit trails for executed agreements regardless of which e-signature platform was used. ## Supported E-Signature Platforms The Unified E-Signature API supports integration with major electronic signature platforms including DocuSign, Adobe Sign, Dropbox Sign, and PandaDoc. Each integration provides access to the standardized data objects listed above. ## Why Use a Unified E-Signature API? **Traditional Approach:** - Build separate integrations for DocuSign, Adobe Sign, Dropbox Sign, PandaDoc, etc. - Learn different authentication systems and API specifications for each provider - Maintain code for multiple API endpoints, envelope/document models, and webhook event formats - Monitor and update integrations when any platform deprecates or changes their API - Handle platform-specific concepts like envelopes, agreements, signers, recipients, and tags **With Unified E-Signature API:** - Integrate once with a single API that works across all e-signature platforms - Use one authentication flow for all platforms - Work with normalized document, signatory, and template objects - Automatic handling of API changes, rate limits, and platform-specific differences - Built-in webhook support for real-time signing status updates ## Integration Scenarios ### For SaaS Applications Enable your end-users to add an integration to their e-signature account from your Integrations Page in your software application. Support any major e-signature platform without building separate integrations for each one. ### For Contract Lifecycle Management (CLM) Embed e-signature functionality into CLM platforms. Let customers send contracts for signature through their existing DocuSign, Adobe Sign, or other e-signature account while keeping all contract data in your platform. ### For CRM & Sales Tools Connect sales platforms to e-signature platforms so reps can send proposals, quotes, and contracts for signature directly from deal records. Automatically update deal status when documents are signed. ### For HR & HRIS Platforms Send offer letters, employment contracts, and policy acknowledgments from HRIS and onboarding tools. Track which employees have completed which documents across any supported e-signature platform. ### For AI & Automation Tools Create AI-powered document agents that draft, send, monitor, and follow up on signature requests across multiple e-signature platforms using a single API. ### For Document Generation & Workflow Tools Build document automation platforms that generate documents from templates and send them for signature. Support any major e-signature platform your customers already use. ## Real-Time, Live Data Unlike other integration platforms that cache data or run periodic sync jobs, the Unified E-Signature API provides real-time access to e-signature data. Every API request hits the source platform directly, ensuring you always have the most current document status, signatory activity, and completion timestamps. This real-time architecture is ideal for: - Dashboards displaying live signing progress and pending signatures - Real-time notifications when documents are signed, declined, or expired - Up-to-the-minute document status and signatory tracking - Automated workflows that react to signing events instantly ## Privacy & Security Unified.to never stores your customers' documents or signature data. All requests are stateless and pass through to the e-signature platforms directly. Traffic is regionalized (US/EU/AU) to comply with data residency requirements. ## storage URL: https://docs.unified.to/storage/overview The Unified Storage API enables developers and product managers to access cloud storage data from multiple platforms through a single, standardized interface. Retrieve and manage files from Google Drive, Dropbox, OneDrive, Box, and other major cloud storage platforms - all with one API. ## What is the Unified Storage API? Cloud storage platforms like Google Drive, Dropbox, and OneDrive each have unique APIs with different authentication methods and data models. The Unified Storage API normalizes these differences, allowing you to build once and support all major cloud storage platforms without maintaining separate integrations. ## Storage Data Objects The Storage API provides access to the following standardized objects: - **Files**: Documents, folders, images, and file metadata ## Common Use Cases ### Contract Management Systems Build contract lifecycle management tools that access contracts stored across multiple cloud storage platforms. Track contract versions, expiration dates, and approval workflows by accessing files from Google Drive, Dropbox, SharePoint, or any supported storage platform. Automatically extract contract metadata, monitor renewal dates, and ensure compliance regardless of where contracts are stored. ### Enterprise Search Create enterprise search solutions that index and search documents across all cloud storage platforms. Build unified search interfaces that find files, contracts, presentations, and documents stored in Google Drive, Dropbox, OneDrive, Box, or other platforms from a single search query. Enable employees to discover content across their organization's entire cloud storage ecosystem. ### Document Management Systems Build document management tools that work with any cloud storage platform. Access, organize, and manage files from Google Drive, Dropbox, or OneDrive through a single interface. ### Backup & Sync Tools Create backup and synchronization tools that work across multiple cloud storage providers. Sync files between different platforms or create multi-cloud backup solutions. ### Collaboration Tools Build collaboration software that integrates with customers' existing cloud storage. Access shared files, track changes, and manage document permissions across multiple storage platforms. ### Content Aggregation Aggregate content from multiple cloud storage accounts. Build search tools that find files across Google Drive, Dropbox, and other platforms, or create unified file browsers. ### AI Document Processing Build AI tools that process documents from any cloud storage platform. Extract text, analyze images, or generate insights from files stored in Google Drive, Dropbox, or other platforms. ## Working with Hierarchical Data in File Storage APIs File Storage integrations typically organize data in a hierarchical (tree-like) structure. For detailed guidance on traversing hierarchical data, refer to our guide: [How to traverse hierarchical data](https://docs.unified.to/guides/working_with_hierarchical_tree_data_in_storage_messaging_and_kms_apis) ## The StorageFile Object Both files and folders are represented by the StorageFile object. They are distinguished by the `type` field: - `type: "FILE"` - Represents a file - `type: "FOLDER"` - Represents a folder This means you'll use the same API endpoints to work with both files and folders, just with different type values. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-2349a087-c8bd-43e7-a6ce-80e7319e3478?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-2349a087-c8bd-43e7-a6ce-80e7319e3478%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## task URL: https://docs.unified.to/task/overview The Unified Task Management API enables developers and product managers to access project and task data from multiple project management platforms through a single, standardized interface. Retrieve tasks, projects, comments, and changes from Asana, Trello, Monday.com, ClickUp, and other major task management platforms - all with one API. ## What is the Unified Task Management API? Project management platforms like Asana, Trello, and Monday.com each have unique APIs with different authentication methods and data models. The Unified Task Management API normalizes these differences, allowing you to build once and support all major task management platforms without maintaining separate integrations. ## Task Management Data Objects The Task Management API provides access to the following standardized objects: - **Changes**: Task changes, updates, and audit logs - **Comments**: Task comments, discussions, and notes - **Projects**: Projects, boards, workspaces, and portfolios - **Tasks**: Tasks, issues, to-dos, and work items ## Common Use Cases ### Project Analytics & Reporting Build project dashboards that aggregate task data from multiple platforms. Track project progress, team productivity, task completion rates, and sprint velocity across Asana, Trello, or any supported platform. ### Time Tracking Integration Connect time tracking tools with task management platforms. Automatically log time to tasks, generate timesheets based on task assignments, and track project budgets. ### Cross-Platform Task Sync Create tools that sync tasks between different project management platforms. Mirror tasks from Asana to Trello, or keep personal and work task lists synchronized. ### AI Project Assistants Build AI-powered project management assistants that access task data, analyze project health, identify blockers, and provide recommendations across multiple platforms using a single API. ### Resource Planning Tools Develop resource allocation tools that integrate with task management platforms. Access task assignments, workload data, and project timelines to optimize team capacity and resource allocation. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-f0c9db03-2443-41b2-94ae-a6f849628483?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-f0c9db03-2443-41b2-94ae-a6f849628483%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## ticketing URL: https://docs.unified.to/ticketing/overview The Unified Ticketing API enables developers and product managers to access customer support data from multiple ticketing platforms through a single, standardized interface. Retrieve tickets, customers, notes, and categories from Zendesk, Freshdesk, Intercom, and other major customer support platforms - all with one API. ## What is the Unified Ticketing API? Customer support platforms like Zendesk, Freshdesk, and Intercom each have unique APIs with different authentication methods and data models. The Unified Ticketing API normalizes these differences, allowing you to build once and support all major support platforms without maintaining separate integrations. ## Ticketing Data Objects The Ticketing API provides access to the following standardized objects: - **Categories**: Ticket categories, types, and classifications - **Customers**: Support customers and end-users - **Notes**: Ticket notes, comments, and internal messages - **Tickets**: Support tickets, issues, and customer requests ## Common Use Cases ### Support Analytics & Reporting Build support dashboards that aggregate ticket data from multiple platforms. Track response times, resolution rates, customer satisfaction, and support team performance across Zendesk, Freshdesk, or any supported ticketing system. ### Customer Context Tools Create tools that display complete customer support history from multiple ticketing platforms. Show all customer interactions, tickets, and notes in a unified view for better customer service. ### AI Support Assistants Build AI-powered support agents that access ticket data, analyze customer issues, suggest solutions, and automate responses across multiple support platforms using a single API. ### Ticket Routing & Assignment Automate ticket routing and assignment by accessing ticket and customer data. Build intelligent systems that assign tickets based on category, priority, agent expertise, or workload. ### Multi-Platform Support Tools Create support tools that work across different ticketing platforms. Build chatbots, knowledge bases, or customer portals that integrate with customers' existing ticketing systems. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-2716a8c7-a426-430a-bf02-7789c7ee5189?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-2716a8c7-a426-430a-bf02-7789c7ee5189%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## uc URL: https://docs.unified.to/uc/overview The Unified Communications (UC) API enables developers and product managers to access communication data from multiple unified communications platforms through a single, standardized interface. Retrieve calls, contacts, comments, and recordings from RingCentral, 8x8, Zoom Phone, and other major UC platforms - all with one API. ## What is the Unified Communications API? Unified Communications platforms like RingCentral, 8x8, and Zoom Phone each have unique APIs with different authentication methods and data models. The Unified Communications API normalizes these differences, allowing you to build once and support all major UC platforms without maintaining separate integrations. ## UC Data Objects The UC API provides access to the following standardized objects: - **Calls**: Call logs, call history, and call details - **Comments**: Call notes, comments, and annotations - **Contacts**: Phone contacts and directory information - **Recordings**: Call recordings and voicemail ## Common Use Cases ### Call Analytics & Reporting Build call center dashboards that aggregate data from multiple UC platforms. Track call volume, duration, wait times, and agent performance across RingCentral, 8x8, or any supported platform. ### CRM Integration Connect UC platforms with CRM systems. Automatically log calls to CRM records, display customer information during calls, and track call history with customer accounts. ### Call Recording Analysis Build tools that analyze call recordings using AI. Extract transcripts, analyze sentiment, identify keywords, and generate insights from call recordings across multiple UC platforms. ### Contact Center Tools Create contact center applications that work with any UC platform. Build call routing, queue management, and agent productivity tools that integrate with customers' existing phone systems. ### Call Intelligence Develop call intelligence tools that analyze call patterns, identify trends, and provide recommendations by accessing call data from multiple unified communications platforms. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-fa88e73d-7540-4309-9739-d33ec3b11587?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-fa88e73d-7540-4309-9739-d33ec3b11587%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## unified URL: https://docs.unified.to/unified/overview This is the unified.to management API that allows you to manage your connections and other objects. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-756fde70-27e8-430f-b55c-5cc8ab98a8d9?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-756fde70-27e8-430f-b55c-5cc8ab98a8d9%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) ## Data Model Unified data model ## verification URL: https://docs.unified.to/verification/overview The Unified Verification API enables developers and product managers to access identity and background verification data from multiple platforms through a single, standardized interface. Manage verification packages and requests from Checkr, Truework, Onfido, and other major verification platforms - all with one API. ## What is the Unified Verification API? Verification platforms like Checkr, Truework, and Onfido each have unique APIs with different authentication methods and data models. The Unified Verification API normalizes these differences, allowing you to build once and support all major verification platforms without maintaining separate integrations. ## Verification Data Objects The Verification API provides access to the following standardized objects: - **Packages**: Verification packages, screening bundles, and check types - **Requests**: Verification requests, background checks, and screening orders ## Common Use Cases ### Applicant Background Screening Integrate background check capabilities into recruiting and HR applications. Order background checks, employment verification, and identity checks for job applicants across multiple verification providers. ### Onboarding Automation Automate employee or contractor onboarding by integrating verification workflows. Trigger background checks when candidates accept offers and track verification status through the hiring process. ### Compliance Management Build compliance tracking tools that work with multiple verification providers. Monitor verification completion, track expiration dates, and ensure regulatory compliance across different screening services. ### Multi-Provider Verification Enable customers to use their preferred verification provider. Support Checkr, Truework, Onfido, or other platforms without building separate integrations for each service. ### Verification Analytics Track verification metrics across multiple providers. Analyze completion times, pass rates, and verification costs to optimize screening processes and vendor selection. [Run In Postman](https://god.gw.postman.com/run-collection/16228585-6c80ccd9-9565-455d-baf6-942e89cface4?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16228585-6c80ccd9-9565-455d-baf6-942e89cface4%26entityType%3Dcollection%26workspaceId%3D0a62ef4e-9382-41f4-8a59-44c03de1e0d5) --- # MCP (Model Context Protocol) ## additional api endpoints URL: https://docs.unified.to/mcp/additional-api-endpoints # Unified MCP Server ## Additional API Endpoints [Get Tools](#get-tools) · [Call Tool](#post-toolsidcall) ### GET /tools Get a list of the MCP tools associated with the connection. The payload will include an object with the parameters with name as the key and value. Add these URL parameters to the MCP GetTools API URL: | | | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `permissions` | A comma-delimited list of permissions from [Unified.to](https://docs.unified.to/guides/how_to_configure_webhooks_in_hubspot#select-permission-scopes-on-unifiedto) | | `tools` | A comma-delimited list of tool IDs to restrict the MCP server. | | `aliases` | A list of words and their aliases to create additional descriptions for tools. In the format of `word1:alias1,alias2;word2:alias1,alias2`. eg. `?aliases=employee:user,person;employees:users,people` | | `include_external_tools` | Include an integration's tools from all of its API and not just the default supported unified API | | `type` | Use this parameter to change the structure of the result to be used for:
        · **OpenAI**'s [function calling](https://platform.openai.com/docs/guides/function-calling) when `type=openai`
        · **Anthropic**'s [function calling](https://www.anthropic.com/engineering/writing-tools-for-agents) when `type=anthropic`
        · **Google Gemini**'s [function declarations](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling) when `type=gemini`
        · **Cohere**'s [functions](https://docs.cohere.com/docs/tools) when `type=cohere`
        · **Grok**'s [functions](https://docs.x.ai/docs/guides/function-calling) when `type=grok`
        · **Groq**'s [functions](https://console.groq.com/docs/tool-use) when `type=groq` | | | | The default (Anthropic) result is an array of: ``` { id: string; description: string; parameters: { name: string; description: string; required: boolean; }[] } ``` ### POST /tools/{id}/call Add these URL parameters to the MCP CallTool API URL: | | | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `hide_sensitive` | Hides sensitive (ie. PII) data from results. These fields include name, emails, telephones, ... | | `permissions` | A comma-delimited list of permissions from [Unified.to](https://docs.unified.to/guides/how_to_configure_webhooks_in_hubspot#select-permission-scopes-on-unifiedto) | | `aliases` | A list of words and their aliases to create additional descriptions for tools. In the format of `word1:alias1,alias2;word2:alias1,alias2`. eg. `?aliases=employee:user,person;employees:users,people` | | `include_external_tools` | Include an integration's tools from all of its API and not just the default supported unified API | | | | The `payload` includes an object with parameters, each key is a parameter name. Call that tools and return the result. ``` { content: { type: 'text'; text: string; }[], structuredContent: JSON-object }[] ``` ## authentication URL: https://docs.unified.to/mcp/authentication # Unified MCP Server ## Authentication There are two authentication options; ### 1. Private LLM API Authentication for 1 Connection: Use this option when your application connects directly to the Unified MCP Server or it gives the LLM API the Unified MCP Server remote URL. This token is NOT safe to give out publically as it is a Unified.to workspace API key and will grant access to all of your connections and Unified.to account. | | | | ------------ | ----------------------------------------------------------------------------------------- | | `connection` | An end-customer’s connection ID from Unified | | `token` | Exactly the same as a Unified.to [workspace API key](https://app.unified.to/settings/api) | | | | ### 2. Private Workspace-only Authentication: Use this option to engage with data in your workspace such as connections, webhooks, API calls, and issues/tickets. This data is configuration data and not your end-customer's data from connections. | | | | ------- | ----------------------------------------------------------------------------------------- | | `token` | Exactly the same as a Unified.to [workspace API key](https://app.unified.to/settings/api) | | | | ### Notes You can provide the `token` either as a `token` URL parameter (eg. `?token={token}`) or in the Authorization header as a `bearer token` (eg. `Authorization: bearer {token}`) All other parameters must be sent as URL parameters. ## changelog URL: https://docs.unified.to/mcp/changelog # Unified MCP Server ## Changelog ### July 22, 2026 - Total tool count is now 43,456, including 8,242 "unified" tools. ### June 30, 2026 - Total tool count is now 27,825, including 7,609 "unified" tools. ### January 25, 2026 - Total tool count is now 22,566, including 5,324 "unified" tools. ### December 23, 2025 - Total tool count is now 22,319, including 4,798 "unified" tools. - Introduced `defer_tools` parameter that uses Anthropic's new feature to lower tool token usage ### December 17, 2025 - Removed public authorization options ### October 19, 2025 - Added a new "workspace" mode that allows our customers to interact with their Unified data, including connections, webhooks, API calls, and issues/tickets. - Total tool count is now 21,993, including 4,425 "unified" tools. ### October 2, 2025 - Total tool count is now 21,874, including 4,322 "unified" tools. ### August 31, 2025 - Added x.ai Grok ### August 29, 2025 - Total tool count is now 20,135, including 4,046 "unified" tools. - Added support for Groq ### August 26, 2025 - Total tool count is now 19,872. - Added a new `tools` parameter to specify exactly which tools to allow in the MCP server. - Aliases no longer create seperate tools, but now generate extended tool descriptions. This will help with LLM tool matching. We removed the `default_aliases` parameter, as that is now the defaul behaviour. ### August 24, 2025 - Total tool count is now at 17,569; 3,963 "unified" tools across 317 integrations over 20 categories. 13,606 "non-unified" tools across 93 integrations. ### August 22, 2025 - New data-region in Europe. Change the MCP URL to https://mcp-api-eu.unified.to. Data accessed by the MCP will be transfered directly from the EU data-region of Unified.to. - Added support to expand an integration's tool over all of its available API endpoints, and not just the unified API's endpoints, which expand the number of tools by an additional 12,979. Use `&include_external_tools=true`. ### August 7, 2025 - Added support for all Unified.to API data-centers; US, EU, AU. The MCP will figure it out by itself. ### July 18, 2025 - Added `aliases` parameter to add additional descriptions to tools - Added support for Cohere's `chat` API and its tools structure ### July 10, 2025 - Added `structuredContent` output when the MCP-Protocol-Version is `2025-06-18` or more recent - Added `type` parameter to `GET /tools` to return tools in a specific LLM data-model - Added `hide_sensitive` parameter to `POST /tools/{id}/call` and the MCP server URL to remove PII/sensitive data from results. eg. `hide_sensitive=true` - Added `permissions` parameter to `GET /tools`, `POST /tools/{id}/call` and MCP server URL to restrict tools - Added additional authentication mechanism `token` and `connection` to be ONLY used with LLM APIs ### June 1, 2025 - Initial deploy ## core URL: https://docs.unified.to/mcp/core # Core Unified API The Unified MCP Server has two personalities: - **Connection mode** — point it at a single end-customer connection and it exposes that connection's integration-specific tools (CRM contacts, accounting invoices, HRIS employees, and so on). - **Core Unified API mode** — authenticate with only a workspace API key (no connection ID) and it exposes the [Core Unified API](https://docs.unified.to/unified/overview) management tools for your **own** workspace configuration data. This page covers Core Unified API mode. > **Note:** Core Unified API mode operates on your workspace configuration data (connections, webhooks, integrations, API-call logs, issues, environments). It does **not** read or write your end-customers' data inside their connections — use a per-connection URL for that (see [`get_unified_connection_mcp_url`](#get_unified_connection_mcp_url)). ## Authentication Provide a Unified.to [workspace API key](https://app.unified.to/settings/api) as the only credential. This is the "Private Workspace-only Authentication" method described in [Authentication](https://docs.unified.to/mcp/authentication). The `token` may be supplied either way: | Location | Example | | -------------------- | ---------------------------------------------------------- | | URL parameter | `https://mcp-api.unified.to/mcp?token=` | | Authorization header | `Authorization: bearer ` | All other options must be passed as URL parameters. > **Warning:** A workspace API key grants access to **all** of your connections and your entire Unified.to account. Treat it as a secret and never expose it in a shareable URL. To hand out access to a single connection, generate a connection-scoped URL with [`get_unified_connection_mcp_url`](#get_unified_connection_mcp_url). ## Endpoints | Transport | US | EU | | ---------------- | -------------------------------- | ----------------------------------- | | Streamable HTTP | `https://mcp-api.unified.to/mcp` | `https://mcp-api-eu.unified.to/mcp` | | SSE (deprecated) | `https://mcp-api.unified.to/sse` | `https://mcp-api-eu.unified.to/sse` | ### Client configuration Streamable HTTP: ```json { "mcpServers": { "unified-core": { "type": "streamable-http", "url": "https://mcp-api.unified.to/mcp?token=" } } } ``` Via `mcp-remote` (for clients that only speak stdio): ```json { "mcpServers": { "unified-core": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp-api.unified.to/mcp?token="] } } } ``` ## Tools Core Unified API mode exposes the following tools. ### Search API Docs | Tool | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `search_docs` | Search the API documentation at docs.unified.to and provide an answer to a query or question. | ### Connections | Tool | Description | | -------------------------------- | ------------------------------------------------------------------------------------------ | | `list_unified_connections` | List the connections in your workspace | | `get_unified_connection` | Retrieve a single connection by ID | | `create_unified_connection` | Create a connection | | `update_unified_connection` | Update a connection | | `remove_unified_connection` | Delete a connection | | `get_unified_connection_mcp_url` | Generate a connection-scoped MCP remote URL (see [below](#get_unified_connection_mcp_url)) | ### Webhooks | Tool | Description | | -------------------------------- | ------------------------ | | `list_unified_webhooks` | List webhooks | | `get_unified_webhook` | Retrieve a webhook by ID | | `create_unified_webhook` | Create a webhook | | `update_unified_webhook` | Update a webhook | | `remove_unified_webhook` | Delete a webhook | | `update_unified_webhook_trigger` | Trigger a webhook | ### Integrations | Tool | Description | | --------------------------- | ---------------------------------------------------------------- | | `list_unified_integrations` | List the available integrations and their supported capabilities | ### Issues | Tool | Description | | --------------------- | --------------------------------------------------- | | `list_unified_issues` | List issues / tickets raised against your workspace | | `get_unified_issue` | Retrieve a single issue by ID | ### API calls | Tool | Description | | ----------------------- | ------------------------------------ | | `list_unified_apicalls` | List recent API-call logs | | `get_unified_apicall` | Retrieve a single API-call log by ID | ### Environments | Tool | Description | | ---------------------------- | -------------------------------- | | `list_unified_environments` | List authentication environments | | `create_unified_environment` | Create an environment | | `remove_unified_environment` | Delete an environment | ## `get_unified_connection_mcp_url` This is the bridge from Core Unified API mode into connection mode: manage your connections with your workspace API key, then generate a ready-to-use MCP remote URL for any individual connection. The URL exposes that connection's integration-specific tools over both Streamable HTTP (`/mcp`) and SSE (`/sse`). ### Parameters | Parameter | Required | Description | | ------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------- | | `connection_id` | yes | The ID of the connection to generate an MCP remote URL for | | `mode` | no | Authentication method for the generated URL — `api` (default) or `user`. See [Modes](#modes) | | `permissions` | no | Comma-separated permission scopes to limit the tools the URL exposes (e.g. `crm_contact_read,crm_company_read`) | | `tools` | no | Comma-separated tool IDs or wildcard patterns to limit which tools the URL exposes (e.g. `list_*,*invoices*`) | | `aliases` | no | Custom description aliases (`key:alt1,alt2;key2:alt3`) to improve tool matching (e.g. `employee:user;employees:users`) | | `defer_tools` | no | Defer tool loading to reduce context usage — `all` or a comma-separated list of tool IDs | | `dc` | no | Preferred data center / region (`us`, `eu`, `au`, `dev`) | | `hide_sensitive` | no | When `true`, strips PII and other sensitive fields from tool responses before they reach the LLM | | `include_external_tools` | no | When `true`, exposes the vendor's raw passthrough endpoints as additional callable MCP tools | Any option supplied is baked into the returned URLs as a query parameter. See [Server options](https://docs.unified.to/mcp/server-options) for the full behavior of each one. ### Modes The `mode` parameter selects the [authentication method](https://docs.unified.to/mcp/authentication) embedded in the generated URL: | `mode` | Generated URL | Sharing | | --------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `api` (default) | `…/mcp?connection=&token=` | Embeds your workspace API key — grants full-account access. **Do not share.** The key can instead be moved out of the URL and sent via the `Authorization: bearer ` header. | | `user` | `…/mcp?token=--` | Uses a connection-scoped **signed** token. Safe to share — it grants access only to that single connection and never exposes your workspace API key. | ### Response ```json { "connection_id": "682e20be9ca303793060370f", "integration_type": "hubspot", "mode": "user", "mcp_url": "https://mcp-api.unified.to/mcp?token=682e20be9ca303793060370f--", "sse_url": "https://mcp-api.unified.to/sse?token=682e20be9ca303793060370f--", "note": "USER mode: this URL uses a connection-scoped signed token. It is safe to share and grants access only to this single connection." } ``` ### Example Generate a shareable, read-only CRM URL for one connection by calling `get_unified_connection_mcp_url` with: - `connection_id`: `682e20be9ca303793060370f` - `mode`: `user` - `permissions`: `crm_contact_read,crm_company_read` - `hide_sensitive`: `true` The returned `mcp_url` can be dropped straight into any MCP client and only exposes the read-only CRM tools for that connection, with PII stripped from responses. ## installation URL: https://docs.unified.to/mcp/installation # Unified MCP Server ## Installation & Usage [OpenAI](#openai-api) · [Anthropic](#anthropic-api) · [Gemini](#google-gemini-api) · [Cohere](#cohere) · [Grok & Groq](#grok-groq) · [Claude.ai](#claudeai-online) · [Claude desktop](#claude-desktop-client) · [Cursor](#cursor) ### OpenAI API: OpenAI's chat-completion API supports remote MCP directly. Enter in our Streamable HTTP URL. ``` resp = client.responses.create( model="gpt-4.1", tools=[{ "type": "mcp", "server_label": "unifiedMCP", "server_url": "https://mcp-api.unified.to/sse?token=XXXXXXXX&connection=YYYYYYY", "require_approval": "never", "allowed_tools": [], }], input="list the candidates and then analyse the resumes from their applications", ) ``` OpenAI also supports sending in a list of MCP tools and having their API request that you call a specific tool with specific parameters. You would first call our MCP `/tools` endpoint, then take the output and include it in your prompt API call: ``` resp = client.responses.create( model="gpt-4.1", tools=$TOOLS, input="list the candidates and then analyse the resumes from their applications", ) ``` Once you call that MCP tool (using our `/tools/{id}/call` endpoint), you would create a new prompt and reference the original responce with a `previous_response_id` value. Please see [this article](https://platform.openai.com/docs/guides/tools-remote-mcp) for more information. ### Anthropic API: Anthropic's chat-completion API allows for the addition of MCP tools and will return back an intermediate response asking you to call that MCP tool and then continue the request by providing its output. You would first call our MCP `/tools` endpoint, then take the output and include it in your prompt API call: ``` resp = client.messages.create( model="claude-3-5-sonnet-20241022", tools=$TOOLS, input="list the candidates and then analyse the resumes from their applications", ) ``` Anthropic's API will then return a response with a `tool_use` content blocks: ``` [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "list_candidates", "input": { "limit": "100" } } ] ``` Once you call that MCP tool (using our `/tools/{id}/call` endpoint), you would return the following back to the model in a subsequent user message: ``` [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "..." } ] ``` Please see [this article](https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/overview) for more information. When creating a chat completion message, include the `tools` field with a list of tools. Anthropic recently released a `beta` version that does support remote MCP server. ``` const completion = await anthropic.beta.messages.create({ model: latestModel, max_tokens: 1024, messages: [ { role: 'user', content: message, }, ], stream: false, mcp_servers: [ { type: 'url', url: "https://mcp-api.unified.to/mcp?token=XXXXXXXX&connection=YYYYYYY", name: 'unifiedMCP', }, ], betas: ['mcp-client-2025-04-04'], }); ``` ### Google Gemini API: Google Gemini uses a similar concept that Anthropic use for tools, but they call it `function_declarations`. First you will request a list of tools with a `GET /tools?type=gemini`, and then give those tools to the chat completion API request. ``` const completion = await gemini.models.generateContent({ model: latestModel, contents: message, config: { tools: $TOOLS }, }); ``` Please see [this article](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling) for more information. Gemini will return a request for you to call that specific tool: ``` content { role: "model" parts { function_call { name: "list_candidates" args { fields { key: "limit" value { string_value: "100" } } } } } } ``` When you respond back with another message creation, you can include the MCP's response in the `content` array: ``` { "role": "user", "parts": [ { "functionResponse": { "name": "list_candidates", "response": { ... } } } ] } ``` ### Cohere: Cohere's chat API uses the same concept as Anthropic and Google Gemini. First get a list of tools from our `GET /tools?type=cohere` endpoint, then add that result to your chat API command: ``` response = co.chat( model="command-a-03-2025", messages=messages, tools=tools ) ``` Then call the `POST /call/{id}/tool` endpoint when requested to call a tool by Cohere's responce. Use the `arguments` as the `properties`. ### Grok & Groq: Both x.ai's Grok and Groq (different company) work the same way, which is similar to Anthropic and Google Gemini. First get a list of tools from our `GET /tools?type=grok` or `GET /tools?type=groq` endpoint, then add that result to your chat API command: ```typescript response = client.chat.completions.create({ // ... tools: tools, tool_choice: 'auto', }); ``` Respond to `tool_calls` in the Grok/Groq response: ```typescript const response_message = response.choices[0].message; const tool_calls = response_message.tool_calls; for (tool_call in tool_calls) { const tool_id = tool_Call.id; const function_args = tool_call.function.arguments; const function_name = tool_call.function.name; // call our POST /tools/${tool_id} with function_args in the POST payload const messages = [ { tool_call_id: tool_id, role: 'tool', name: function_name, content: result_content, }, ]; const second_message = client.chat.completions.create({ // ... messages, }); } ``` ### Claude.ai (online): Go to [claude.ai](https://claude.ai/), then navigate to Settings > Integrations. Click on "Add custom integration". Enter the MCP URL: `https://mcp-api.unified.to/sse?token={connectionID}-{nonce}-{signature}` Make sure to provide your end-customer the appropriate `token` value. ### Claude (desktop client): Edit the `claude_desktop_config.json` file: ```json { "mcpServers": { "unified-mcp": { "command": "npx", "args": [ "-y", "mcp-remote", "https://mcp-api.unified.to/sse?token=XXXXXXXX&connection=YYYYYYY", "--allow-http" ] } } } ``` Make sure to provide your end-customer the appropriate `token` value. ### Cursor: Navigate to Cursor > Settings > Cursor Settings > MCP and edit the MCP configuration. Replace `unified-mcp` with the name of your own application and then make sure to provide your end-customer the appropriate `token` value. ```json { "mcpServers": { "unified-mcp": { "url": "https://mcp-api.unified.to/sse?token=XXXXXXXX&connection=YYYYYYY" } } } ``` MCP is a new protocol and it is moving fast. We expect more LLM & agent clients to support its newer Streamable HTTP transport protocol. We also expect that the MCP protocol will continue to expand quickly. Stay tuned as we also keep up. ## overview URL: https://docs.unified.to/mcp/overview # Unified MCP Server Unified has launched an MCP server that connects any Unified connection to LLM (Large Language Model) providers supporting the newest MCP protocols. The available MCP `tools` will be determined by the integration’s feature support and the connection’s requested permissions. The Unified MCP server sits on top of our Unified API (which calls individual APIs), and hides all of the complexity of API calling. Each call to a tool will count as 1 API request on your plan. ![Unified MCP Server](/images/MCP_V2.png) Warning: Most current LLM models have severe limits on how many tools they can handle. Groq's models can only handle 10 tools, while most of OpenAI's models can handle only 20 available tools. Cohere's recent models seem to work with 50 models. Make sure that you limit the tools with the `permissions` or `tools` parameters. Check out our sample code (in nodeJS Typscript) at [unified-mcp-typescript](https://github.com/unified-to/unified-mcp-typescript) ## URLs - Streamable HTTP: https://mcp-api.unified.to/mcp or https://mcp-api-eu.unified.to/mcp - SSE: https://mcp-api.unified.to/sse or https://mcp-api-eu.unified.to/sse (SSE has been deprecated in the MCP protocol) - stdin: `for real? it's 2025...` ## server options URL: https://docs.unified.to/mcp/server-options # Unified MCP Server ## MCP Server Options Add these URL parameters to the MCP URL: | | | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `connection` | The [connection ID](https://app.unified.to/connections) to access. Only used when `token` is a workspace API key. | | `token` | Either the public generated token or the workspace API key | | `hide_sensitive` | Hides sensitive (ie. PII) data from results. These fields include name, emails, telephones, ... | | `permissions` | A comma-delimited list of permissions from [Unified.to](https://docs.unified.to/guides/how_to_configure_webhooks_in_hubspot#select-permission-scopes-on-unifiedto) | | `tools` | A comma-delimited list of tool IDs to restrict the MCP server. | | `defer_tools` | A comma-delimited list of tool IDs (or partial IDs) to signal to the LLM to "defer" loading/tokenizing this tool. You can also include `all` to designate that all tools are to be deferred. Only works with the /tools endpoint. | | `aliases` | A list of words and their aliases to create additional descriptions for tools. In the format of `word1:alias1,alias2;word2:alias1,alias2`. eg. `?aliases=employee:user,person;employees:users,people` | | `include_external_tools` | Include an integration's tools from all of its API and not just the default supported unified API | | | | --- # Glossary ## Access Token URL: https://docs.unified.to/concepts/glossary/access_token An **access token** is a security credential in OAuth 2 that acts as proof that an application has the authorization to access an API. They are often issued to an application by the authorization server after its end user has successfully authenticated (logged in). Tokens typically contain _claims_, which are information about the token and the end user. In an access token, claims contain information about: - The token itself, such as the URL for the authorization server that issued the token, the client ID of the token's audience (the application for which the token is to be used), the date and time when the token expires, etc. - The _scopes_ that specify the resources that holder is authorized to access, and the type of access granted (read or read/write). Access tokens are usually _bearer tokens_, which means that anyone who possesses an access token can use it to access the resources it's associated with. For security reasons, access tokens usually have a limited lifetime. Once past its end-of-life date and time, the token is invalid and can no longer be used to access the associated resources. To continue accessing those resources, the application will need to acquire a new access token (see _Refresh Token_ for details). In OIDC, authorization and authentication are often performed in the same workflow. When an application requests authorization from a authorization server for the first time in a session, the end user logs in and if authenticated, the authorization server provides the application with both an ID token and an access token. ## API URL: https://docs.unified.to/concepts/glossary/api An **API** is a set of rules and protocols for two applications to communicate with each other, allowing them to interact and share data using a defined set of methods and data formats. APIs provide an interface made of endpoints — URLs or URIs, each of which correspond to a specific operation to be performed on a specific resource, such as retrieving data, updating data, and deleting data. The client application makes requests to the API to perform an operation or provide data; the server application performs the API's services and issues responses to the client's requests. Requests are made using HTTP methods, with each method specifying a different kind of action to be performed the resource at the endpoint: - GET: Retrieve one or more resources from the server. - POST: Send data to the server to create a new resource. - PUT: Update an existing resource on the server. - DELETE: Remove a resource from the server. ## API Key URL: https://docs.unified.to/concepts/glossary/api_key An **API key** is a single text string that both identifies an API's client application and authorizes that client to access the API. Using an API key is the simplest form of API authorization. It avoids the credentialing problems that arise with changes in staff, but unlike more complex authorization systems like OAuth 2, it is simpler to compromise. ## API Request / Call URL: https://docs.unified.to/concepts/glossary/api_request_call An **API request** — also known as an **API call** — is a message sent by a client application to an API to retrieve, update, or interact with the resources provided by the API. The request is typically made over HTTP or HTTPS and follows a predefined format and protocol. APIs (Application Programming Interfaces) enable communication between different software systems by exposing endpoints that the client can interact with. ## ATS URL: https://docs.unified.to/concepts/glossary/ats An **applicant tracking system (ATS)** is an application used by recruiters and employers to help them manage their recruitment and hiring processes. ATS applications automate and manage a number of recruitment and hiring tasks, including: - **Job posting and distribution:** Providing a system for creating and formatting job postings, then sending those postings to job boards, social media platforms, and other places where prospective candidates can find them. - **Resume parsing and storage:** Extracting relevant information from resumes (e.g., contact details, work experience, education, and skills) and storing it in a centralized repository. - **Candidate screening and filtering:** Screening applications based on predefined criteria (e.g., qualifications, experience, and skills) and using keywords and filters to identify qualified candidates from the applicant pool. - **Candidate communication and management:** Facilitates communication with candidates through email templates, notifications, and messaging systems, as well as scheduling interviews and managing interview logistics. - **Collaboration and feedback:** Enabling hiring teams to collaborate by consolidating and sharing candidate profiles, notes, and feedback. ## Authentication URL: https://docs.unified.to/concepts/glossary/authentication **Authentication** is the process of verifying the identity of a client, such as an end user, application, or device. It answers the question "Who are you?" These are the most common ways a client can authenticate itself with an API: - **API key:** The client provides an API key — a simple text string — as proof of its identity. - **API key + secret:** This is a more secure variant of API key authentication, where the client provides both an API key and a secret to authenticate itself. Like the API key, the secret is also a text string, but it is known only to the client. The API applies some transformation to the secret — usually hashing — in order to confirm that it is valid. - **OpenID Connect (OIDC):** An authentication protocol built on the OAuth 2 authorization protocol and used in conjunction with it. In OIDC, the clientsend credentials to an OAuth 2 authorization server to confirm its identity. If successful, the authorization server sends the client an ID token, which acts as proof that the client has been authenticated. ## Authorization URL: https://docs.unified.to/concepts/glossary/authorization **Authorization** is the process of determining if a client — an end user, application, or device — has permission to access a specific resource or perform a particular action. It answers the question "What are you allowed to do?" The authorization process usually takes place along with authentication. The client's identity is verified, after which their permissions are determined. These are the most common authorization mechanisms used by APIs: - **API key / API key _ secret:** These authentication methods can also act as authorization methods; the assumption is that if a client is known to the API, it has permission to access the API. This approach doesn't have any granularity of permissions unless the API uses a mechanism like an access control list. - **OAuth 2:** An open standard for authorization that grants client applications limited access to an end user's information without exposing their passwords. It is widely adopted for securing API access and is particularly popular in scenarios involving third-party integrations, where an end user grants a third-party application access to their resources hosted by another service. ## Bearer Token URL: https://docs.unified.to/concepts/glossary/bearer_token A **bearer token** is a type of security token used in authentication and authorization that indicates that any party possesses it (the "bearer") is allowed to access the associated resources. Here are some characteristics of bearer tokens: - **They simplify authorization.** Using a bearer token involves sending the token with each request to access the resources protected by the token, typically in the HTTP Authorization header. - **They are not bound to specific users.** The possessor of a bearer token, regardless of who they are, has access to the specific resource or resources that the token protects. Care must be taken to secure bearer tokens and ensure that they are not acquired by unauthorized parties. - **They are short-lived.** To minimize security risks, bearer tokens have short lifetimes. After a date and time specified within the token, the token becomes invalid and can no longer be used to access the resources it protects. - **They are revocable.** As an additional security measure, a bearer token can be revoked if necessary, rendering it invalid. When this happens, the token can no longer be used to access the resources it protects. Bearer tokens are a key part of the OAuth 2 protocol. OAuth 2's access tokens are bearer tokens, and they specify what the bearer is allowed to do with specified resources. ## Candidate URL: https://docs.unified.to/concepts/glossary/candidate A **candidate** or **applicant** is a person who has applied for a job position or is being considered for employment by an organization. In an ATS, candidates are the central focus of the recruiting process. Many of the objects managed by an ATS represent either candidates or things related to their journey through the recruitment process (such as jobs, resumes, interviews, scorecards, and so on). ## Client ID / Secret URL: https://docs.unified.to/concepts/glossary/client_id_secret A **client ID** and **client secret** are part of the OAuth 2 protocol, which protects resources by limiting access to authorized parties. The client ID is a value that uniquely identifies an application making an API request. This value is public. This value is supplied by the API provider, typically when the application's adminsitrators or developers register the application with the provider. The provider uses the client ID to track and manage the application's interactions with the API. The client secret is a value that proves the identity of the application making an API request. This value is confidential, and should be kept secure. It is supplied by the API provider along with the client ID. The client ID and client secret are used together during the OAuth 2 flow to obtain an access token, which authorizes an application to access resources via the API. ## Connection URL: https://docs.unified.to/concepts/glossary/connection A **connection** represents a specific authentication of an integration. In Unified.to, a connection represents the permission granted by an end user for an application to access their account on an integration via Unified.to's unified API. End users grant this permission by clicking on the integration's icon in the Embedded Authorization component and confirming that they would like to grant the application access to their account on that integration — usually by entering their credentials. Once the end user does that, they are returned to the application, and a connection representing the user's permission is created within Unified.to. You can see a list of all your end users' connections by selecting **Integrations** → **User Connections** in the Unified.to dashboard. ## Connection ID URL: https://docs.unified.to/concepts/glossary/connection_id {} ## CRM URL: https://docs.unified.to/concepts/glossary/crm **Customer Relationship Management (CRM)** applications help businesses manage their interactions and relationships with current and prospective customers. They are used primarily by salespeople, but are also used by marketing and customer support/success staff. CRM applications include the following functionalities: - **Contact/company management**: Tracking of customer/prospective customer and company information. - **Deal management:** Tracking sales opportunities with contacts or companies. - **Event/interaction management:** Tracking interactions with customers over various channels — emails, calls, meetings — as well as things related to those interactions, such as notes and tasks. ## Data Model URL: https://docs.unified.to/concepts/glossary/data_model A **data model** is a representation of data elements describing how that data is structured, related, and used. It provides a framework for defining the data elements and their relationships, which are crucial for designing databases, data warehouses, and other data systems. The key components of a data model are: - **Entities:** Representations of objects or concepts that are important to an organization or process, such as a customer, business transaction, email or chat message, or generative AI prompt. - **Attributes:** Properties or characteristics of entities. For example, a customer entity would have properties like a name, email address, and phone number. - **Relationships:** These describe how entities are related or connected to each other, or how they interact with each other. Relationships can be one-to-one, one-to-many, or many-to-many. For example, a customer can place multiple orders, which would be represented by a one-to-many relationship between the corresponding customer entity and many order entities. - **Keys:** Unique identifiers for entities. There are _primary keys_, which unique identify an entity, and _foreign keys_, which link entities of different types. ## Endpoint URL: https://docs.unified.to/concepts/glossary/endpoint An **endpoint** is a specific URL or URI where a particular resource or service can be accessed by the client. They define the points of interaction between the client and the server, enabling the client to perform various operations such as retrieving, creating, updating, or deleting resources. Here are the key characteristics of an endpoint: - **URL structure:** Endpoints are made up of the API's base URL, followed by the path of that specifies the resource. For example, an endpoint that provides information about a customer whose id value is 123 could be [`https://api.example.com/`](https://api.example.com/)`customer/123`. `https://api.example.com/` is the base URL, and `customer/123` is the path to the resource. - **Resource identifiers:** These are identifiers at the end of the path that specify a specific resource or collection of resources that the client wants to interact with. For the endpoint [`https://api.example.com/`](https://api.example.com/)`customer/123`, the resource identifier is `123`. - **HTTP method:** Endpoints are accessed using various HTTP methods, which determine the type of operation to be performed: - `GET`: Retrieve a resource or a list of resources from the API. - `POST`: Submit data to the API to create a new resource. - `PUT`: Update an existing resource. - `DELETE`: Remove an existing resource. - **Parameters:** Endpoints can include query parameters or path parameters to filter, modify, or specify the resource further. These are key-value pairs added after the URL For example, the following request asks for a list of only the customers who are active: `https://api.example.com/v2/customer?status=active`. ## Environment URL: https://docs.unified.to/concepts/glossary/environment An **environment** is a specific configuration and context where an application runs, namely the hardware, operating system, other applications, utilities, dependencies, libraries, and configurations and settings. What an application does and how it performs depends on the environment in which it runs. Here are the general categories of environment in an ideal software development scenario: - **Development environment:** An environment where developers build the application. This environment is optimized for writing software, typically running on a developer's computer and has the presence of developer tools, dependencies, and libraries. - **Sandbox environment:** An environment explicitly designed for isolated and safe experimentation during development. They use dummy data and do not impact any real systems or data. - **Testing environment:** A testing server where automated tests (such as unit and integration tests) are run to verify the functionality. Test data is used to avoid any impact on real users. - **Staging environment:** A staging server where the setup mimics the production environment as closely as possible in order to simulate the actual conditions under which the application will be run. This is used for fine-tuning and user acceptance testing (UAT). - **Production environment:** The live system where real users make use of the application. ## ETL URL: https://docs.unified.to/concepts/glossary/etl **ETL** — short for extract, transform, and load — is a process where data is moves from multiple sources into a centralized repository, such as a data warehouse. ETL, as its name implies, involves three steps: 1. **Extract:** The data is pulled from a various sources — databases, flat files, APIs, and other data repositories — with the goal of gathering all relevant data needed for analysis. 2. **Transform:** In this step, the extracted data is cleaned, transformed, formatted, and prepared for loading. This can involve various operations such as filtering out duplicate or invalid data, sorting, aggregating, and applying business rules to ensure the data is in the correct format and quality for analysis. 3. **Load:** The transformed data is loaded into the destination data repository, such as a data warehouse or data lake. The end result of ETL is a more complete collection of data gathered from a wide array of sources that provides a unified view in order to provide more effective data analysis, insights, and decision-making. ## GDPR URL: https://docs.unified.to/concepts/glossary/gdpr The **GDPR (General Data Protection Regulation)**, is a regulation in the European Union (EU) that sets out how personal data of individuals in the EU can be collected, used, and stored. It's considered one of the most comprehensive data privacy laws in the world, and it has had a significant impact on how organizations around the globe handle personal data. Here are some key points about the GDPR: • **Who it applies to:** The GDPR applies to any organization that processes the personal data of individuals in the EU, regardless of where the organization is located. This includes businesses, government agencies, and non-profit organizations. • **What it protects:** The GDPR protects a wide range of personal data, including names, addresses, email addresses, phone numbers, IP addresses, and health information. • **Key rights for individuals:** Under the GDPR, individuals have a number of rights in relation to their personal data, including the right to access their data, the right to rectification (correction), the right to erasure (deletion), the right to restrict processing, the right to data portability, and the right to object to automated decision-making. • **Obligations for organizations:** Organizations that are subject to the GDPR have a number of obligations, including: ◦ **Lawful basis for processing:** Organizations must have a legal basis for processing personal data, such as consent, contractual necessity, or a legitimate interest. ◦ **Data minimization:** Organizations should only collect and process the personal data that is necessary for the specific purposes for which it is being processed. ◦ **Security measures:** Organizations must implement appropriate technical and organizational measures to protect personal data from unauthorized access, disclosure, alteration, or destruction. ◦ **Data breach notification:** Organizations must notify individuals and the relevant authorities in the event of a data breach. Here are some resources where you can learn more about the GDPR: • **Official GDPR website:** [https://gdpr.eu/what-is-gdpr/](https://gdpr.eu/what-is-gdpr/) • **European Commission website on GDPR:** [https://gdpr.eu/what-is-gdpr/](https://gdpr.eu/what-is-gdpr/) ## Generative AI URL: https://docs.unified.to/concepts/glossary/generative_ai **Generative AI** is a class of artificial intelligence models and algorithms designed to create new content. Unlike discriminative or "traditional" AI, which typically focuses on recognizing patterns and making predictions based on existing data or placing data into categories, generative AI generates new, original data that is not explicitly present in the training set. ## HRIS URL: https://docs.unified.to/concepts/glossary/hris An **HRIS (Human Resources Information System)** is an application or service that manages and streamlines various human resource (HR) functions within an organization. HRIS systems integrate multiple HR processes, employee data management, payroll, benefits administration, performance management, and compliance reporting. Some HRIS applications also provide recruitment functionality similar to that offered by an applicant tracking system (ATS). ## ID Token URL: https://docs.unified.to/concepts/glossary/id_token An **ID token** is a security credential in OIDC (OpenID Connect, an authentication protocol built on OAuth 2) that acts as proof that the end user has been authenticated. They are issued to an application by the authorization server as the end result of successful authentication (logging in). Tokens typically contain _claims_, which are information about the token and the end user. In an ID token, claims contain information about: - The token itself, such as the URL for the authorization server that issued the token, the client ID of the token's audience (the application for which the token is to be used), the date and time when the token expires, etc. - Information about the end user, typically concerning their profile and identity, such as names, email address, URL for their profile photo, etc. In OIDC, authorization and authentication are often performed in the same workflow. When an application requests authorization from a authorization server for the first time in a session, the end user logs in and if authenticated, the authorization server provides the application with both an ID token and an access token. ## Integration URL: https://docs.unified.to/concepts/glossary/integration An **integration** is a connection between a unified API and a SaaS API. In Unified.to, an integration a connection between Unified.to's unified API and one of the SaaS applications that the unified API can send messages to and receive messages from. In order to be able to send messages to and receive messages from a specific SaaS application, you must _activate_ that SaaS application's integration. You do this by doing the following: - Select **Integrations** → **Active Integrations** in the Unified.to dashboard. - Find the SaaS application whose integration you want to activate. Click on that SaaS application's entry. - On the page that appears, make sure the **Authorizations** tab is selected, then click the **Activate** button. ## iPaaS URL: https://docs.unified.to/concepts/glossary/ipaas An evolution of an ETL solution that moves data from customer accounts from one source to another destination. ## Item / Product URL: https://docs.unified.to/concepts/glossary/item_product An **item** is a product or service that is sold in an online or physical store. ## KMS URL: https://docs.unified.to/concepts/glossary/kms A **knowledge management system (KMS)** is an application designed to facilitate the collection, organization, sharing, and retrieval of knowledge within an organization. ## OAuth 2 URL: https://docs.unified.to/concepts/glossary/oauth_2 OAuth 2, short for Open Authorization Protocol, version 2, is the industry standard method for authorization defined by the Internet Engineering Task Force (IETF). It enables a third-party application to obtain limited access to a user's resources hosted on a server, without exposing the user's credentials to the third-party application. It is the preferred method for allowing secure and controlled access to web APIs. It is widely used for applications to grant access to user information from various services such as Google, Facebook, and Twitter, and used by applications that use Unified.to's unified API to access end users' account on SaaS applications. OAuth 2 is based on these roles and objects: - **Resource owner:** This is the user or application who owns the data or services — the resources — and wants to grant access to them to a third-party application. - **Client:** This is the third-party application requesting access to the protected resources on behalf of the resource owner. - **Authorization server:** This is a server responsible for authenticating the resource owner and issuing permission in the form of access tokens (see below) to the third-party application (the Client) to access the resources after the Resource Owner grants authorization. It performs two important security tasks: verifying the identity of the Resource Owner and confirming that the Client is authorized to access the requested resources. - **Resource server:** This is the server hosting the resources that the Client wants to access; in other words, it houses the API. The Resource Server validates the access tokens presented by the Client, granting access to the requested resources if the tokens are valid. - **Access token:** An access token is a credential representing the authorization granted to the client by the resource owner. The client presents this token to the resource server to access the protected resources. Access tokens are short-lived and typically have limited scopes, which are permissions for access to specific resources and actions granted to the Client. - **Refresh token:** A refresh token is used to obtain a new access token when the current one expires, without requiring the user to re-authenticate. OAuth 2 offers the following benefits: - **Security**: It allows applications to access resources without exposing user credentials, reducing the risk of credential theft. - **User Experience**: It delivers a seamless user experience by allowing users to authorize applications without having to repeatedly entering their credentials. - **Scalability**: It supports various authorization flows to accommodate different types of applications and use cases. - **Interoperability**: As an open standard, OAuth 2.0 is widely adopted and supported by numerous services and platforms, enabling integration across different systems. ## Passthrough URL: https://docs.unified.to/concepts/glossary/passthrough A **passthrough** is a way to forward requests directly to an integration without going through the unified API. Unified.to's unified API is designed to handle the most common use cases for several API categories. For example, our ATS (application tracking system) API provides a unified model for working with entities that are common to ATS SaaS applications, such as managing candidates, jobs, and companies. Each of the ATS SaaS applications that we integrate with may have different names for those entities and different endpoints for creating, reading, updating, and deleting them, but with our unified API, you always use the Unified.to **candidate**, **job**, and **company** models and our endpoints for working with them, regardless of the SaaS integration you're working with. Our unified approach abstracts away the differences between SaaS applications for the majority of the tasks that your application will perform. However, there may be times when you need to access functionality or data that is specific to a particular SaaS application that isn't covered by our unified API. In these edge cases, your application can bypass the unified API and send a platform-specific request directly to an endpoint in that SaaS application's API. This request _passes through_ the unified API and directly to the endpoint without being altered, hence the name "passthrough." Unified.to's passthrough supports the following HTTP methods: - `POST`: Sends a POST request to the specified endpoint, complete with payload specific to the SaaS API. - `PUT`: Sends a PUT request to the specified endpoint, complete with payload specific to the SaaS API. - `GET`: Sends a GET request to the specified endpoint, complete with payload specific to the SaaS API. - `DELETE`: Sends a DELETE request to the specified endpoint, complete with payload specific to the SaaS API. ## Refresh Token URL: https://docs.unified.to/concepts/glossary/refresh_token A **refresh token** is security credential that allows an application to acquire a new access token to replace one that has expired without requiring the end user to re-authenticate (log in). In OAuth 2, when the authorization server issues an access token to an application, it often issues an accompanying refresh token. When the access token expires, the application submits the refresh token to get a new access token. Like access tokens, refresh tokens usually have a limited lifetime. This lifetime is often longer than an access token's lifetime; this longer lifetime means that the end user doesn't have to re-authenticate as often. Since refresh tokens are generally longer-lived and are used to obtain new access tokens, they need to be stored and handled with greater care than access tokens. ## Request URL: https://docs.unified.to/concepts/glossary/request A **request**, in the context of an API, is a message sent by a client to an API server to perform a specific action or retrieve data. API requests follow a defined protocol, preferably HTTPS but sometimes HTTP, and consist of the various components listed below that convey the necessary information for the server to process the request and return an appropriate response: - **HTTP method:** Specifies the type of action the request is asking the server to perform. Common HTTP methods include: - `GET`: Retrieve a resource or a list of resources from the API. - `POST`: Submit data to the API to create a new resource. - `PUT`: Update an existing resource. - `DELETE`: Remove an existing resource. - **URL (uniform resource locator):** The endpoint or address to which the request is sent. It typically includes the server address and the specific resource path, e.g. `https://api.example.com/v2/customer`. - **Headers:** Metadata included as part of the request that provide additional information. Common headers include: - **`Content-Type`**: Specifies the media type of the request body. For example, the content type of JSON requests is **`application/json`**. - **`Authorization`**: Contains credentials for authenticating the request. In requests made using the OAuth 2 protocol, the value of this header is the string **`Bearer token`** followed by the token string. - **Query parameters:** Key-value pairs added after the URL to pass additional data. They are typically used to filter or modify the response. For example, the following request asks for a list of only the customers who are active: `https://api.example.com/v2/customer?status=active`. - **Body:** The request's payload — the data to be sent to the API. This is mainly used with the `POST` and `PUT` methods. Here's an example of a JSON body: ```javascript { "id" : "01234", "name" : "Jane Doe", "email" : "jane.doe@example.com" } ``` ## Response URL: https://docs.unified.to/concepts/glossary/response A **response**, in the context of an API, is a message sent by the server back to the client after processing an API request. Responses contain information about the result of the request, including whether it was successful, any data requested, and details about any errors that occurred. They onsist of the various components listed below: - **Status code:** An HTTP response code, which is a three-digit numeric code indicating the result of the request. Common status codes include: - **`200 OK`**: The request was successful. - **`201 Created`**: A new resource was successfully created. - **`400 Bad Request`**: The request was malformed or invalid. - **`401 Unauthorized`**: Authentication is required or has failed. - **`404 Not Found`**: The requested resource could not be found. - **`500 Internal Server Error`**: An error occurred on the server. - **Headers:** Metadata included as part of the response that provide additional information. Common headers include: - **`Content-Type`**: The media type of the response body. One common type is JSON, represented by a content-type value of **`application/json`**. - **`Content-Length`**: The length of the response body in bytes. - **`Set-Cookie`**: Used to send cookies from the server to the client. - **Body:** The main content of the response, often containing the data requested by the client or details about the result of the request. The body is usually formatted in a standard data interchange format such as JSON or XML. ## An API Response Example Consider an example where a client application has made a **`GET`** request to retrieve a user's profile. Here's what the request might look like: ```javascript GET /v2/user/123 HTTP/1.1 Host: api.example.com Authorization: Bearer [ACCESS_TOKEN_GOES_HERE] ``` Here's what the corresponding response might look like: - Status code: `200 OK` - Headers: - **`Content-Type`**: **`application/json`** - **`Content-Length`**: `150` - Body: ```javascript { "id": "01234", "name": "Jane Doe", "email": "jane.doe@example.com", "created_at": "2024-05-25T12:34:56Z" } ``` ## REST URL: https://docs.unified.to/concepts/glossary/rest A REST API, also known as a RESTful API, is a specific type of application programming interface (API) that follows the Representational State Transfer (REST) architectural style. Here's a breakdown of the key points: **Concept:** - API: Think of it as a messenger between different software systems. It defines how programs can request and receive data from each other. - REST: A set of guidelines for designing APIs that favor simplicity, interoperability, and scalability. Think of it as best practices for API design. **Functioning:** - REST APIs use standard HTTP methods: GET to retrieve information, POST to create new data, PUT to update data, and DELETE to remove data. This makes them familiar and easy to use for developers. - Data is typically exchanged in JSON format, which is human-readable and widely supported. - Each resource (like a product or user) has a unique identifier (like a URL) and is accessed through specific endpoints. **Benefits:** - **Standardized**: Easier for developers to understand and integrate with. - **Interoperable**: Works across different platforms and programming languages. - **Scalable**: Can handle large amounts of data and traffic efficiently. - **Flexible**: Can be adapted to various use cases. **Examples:** - Many popular websites and services have REST APIs, like Twitter, Facebook, Google Maps, and Amazon. - Developers can use these APIs to integrate their own applications with these services, adding new features and functionality. **Additional points:** - Not all APIs are RESTful, but REST APIs are very common due to their advantages. - There are specific guidelines and constraints for building RESTful APIs. - Security is important when using any API, including REST APIs. ## Sandbox URL: https://docs.unified.to/concepts/glossary/sandbox A **sandbox** is an isolated environment where code can be executed safely, without affecting the system or real data. Sandboxes are used to test, run, and analyze code in a way that limits its potential impact on the wider system and data, allowing developers to learn and experiment without having adverse effects on systems, applications, or data that people and organizations rely on. Some APIs provide a sandbox environment — a testing environment that acts like the production environment but is isolated and controlled and uses dummy data. This allows developers to safely experiment, test, and integrate with the API without affecting the live data or system. ## Schema URL: https://docs.unified.to/concepts/glossary/schema A **schema** is a structured framework that defines the organization, structure, and constraints of data or information. Schemas are used in various fields to standardize and organize information, making it easier to manage, understand, and manipulate data. In the context of APIs, a schema defines the structure, format, and types of data that an API can accept and return. It defines both the request and response data formats, ensuring that clients and servers can communicate. Schemas are particularly important for maintaining consistency and validating data in API interactions. Key aspects of API schemas include: - **Structure:** What data is included in a given data structure, and how it is organized — scalars (single values), collections (objects or arrays), and nesting. - **Data types:** The specific type for each data element, which includes numbers, strings, booleans, arrays, and objects. - **Constraints:** Restrictions on the data, such as required fields, default values, minimum and maximum values, and specific formats (e.g., date, email). ## SCIM URL: https://docs.unified.to/concepts/glossary/scim SCIM (System for Cross-domain Identity Management) is an open standard protocol designed to simplify the management of user identities in cloud-based applications and services. It is primarily used for automating the exchange of user identity information between identity providers (such as companies with multiple individual users) and service providers (such as enterprise SaaS applications). SCIM makes it easier to provision, de-provision, and manage user accounts across various systems. It has the following features: - **Unified schema:** SCIM defines a common user schema that includes attributes such as username, email, phone number, and group membership. This standardized schema ensures consistency in how user information is represented across different applications. - **RESTful API:** SCIM uses a RESTful API, which allows for CRUD (Create, Read, Update, Delete) operations on user identities and groups. - **JSON payloads:** SCIM typically uses the JSON format for request and response payloads. - **Interoperability:** SCIM is designed to work across different domains and platforms, providing interoperability between various identity management systems and cloud services. ## Scopes URL: https://docs.unified.to/concepts/glossary/scopes **Scopes** are a mechanism specifying the access privileges or permissions that a client application is requesting on behalf of the end user. They define the range of resources and operations that an application is permitted to perform on behalf of the end user. By specifying scopes, an application can request only the permissions it needs. Scopes are a key part of access tokens in OAuth 2. When an end user authorizes an application in OAuth 2, they are granting the application access to specific parts of their account as defined by these scopes. Each scope represents a specific level or type of access, allowing granular control over what the application can and cannot do. Scopes are human-readable strings included within the access token that specify one or more types of access that an application is requesting. They are defined by the resource server (i.e., the API) and represent the permissions that the application calling the API can request. Here are some examples of OAuth 2 scopes and the privileges or permissions they grant to an application: - `profile`: Allows the application to access an end user's basic profile information (typically their names and photo URL). - `email`: Allows the application to access the end user's email address. - `https://www.googleapis.com/auth/drive`: Specific to Google Drive; allows the application to create, read, update, delete, and download files on the end user's Google Drive account. - `https://www.googleapis.com/auth/drive.readonly`: Specific to Google Drive; allows the application to only read and download files on the end user's Google Drive account. - `repo`: Specific to GitHub; allows the application full read and write access to the end user's public and private repositories, including code, commit statuses, repository invitations, collaborators, deployment statuses, and repository webhooks. - `public_repo`: Specific to GitHubl; similar to `repo`, except full read and write access is limited to the end user's public repositories. ## Unified API URL: https://docs.unified.to/concepts/glossary/unified_api A **unified API** (also sometimes called a **universal API**) is a single API interface that provides access to multiple APIs, presenting a consistent and standardized way to interact with them. They are essentially "middlemen" that simplify interactions with many different individual APIs. A unified API acts as a single, standardized interface that allows an application to access data and functionality from multiple sources using a single consistent set of methods. Key features of unified APIs include: - **Aggregation**: Combining APIs from multiple providers within a specific software category (e.g., accounting, HR). - **Abstraction**: Hiding the complexities of individual APIs and presenting a single point of access with consistent authentication, data formats, and syntax, regardless of the underlying APIs. Unified APIs offer the following benefits: - **Simplified integration**: Developers can connect to numerous software products with less effort and code compared to individual integrations. - **Faster development**: Reduced complexity and standardized procedures lead to quicker integration times. - **Improved consistency**: Provides a uniform user experience across different platforms and applications. - **Reduced costs**: Less development effort translates to lower overall costs. - **Streamlined workflows**: Data exchange between various systems becomes simpler and more efficient. While unified APIs provide the benefits listed above, there are some considerations developers must take into account before using them: - Unified APIs might not offer the full functionality of individual APIs. - They may have their own limitations and data models. - Choosing the right unified API depends on your specific needs and the software category you're interested in. **Example Use Cases for Unified APIs** - A unified API for HR systems might allow access to employee data from different providers like BambooHR and Hibob through a single interface. - A unified API for payment gateways could offer developers a consistent way to integrate with various payment processors like Stripe and PayPal. ## Unified Data Model URL: https://docs.unified.to/concepts/glossary/unified_data_model A **unified data model** is a comprehensive and standardized data model combining data from multiple sources into a single format and place. ## User ID URL: https://docs.unified.to/concepts/glossary/user_id {} ## Webhook URL: https://docs.unified.to/concepts/glossary/webhook A **webhook** is an HTTP callback function triggered by specific events. When such an event occurs in a service, the webhook makes an HTTP POST request to the URL configured when the webhook was created, with the POST request containing information about the event. This allows one application to notify another application when something of interest happens, enabling automated workflows and integrations. For example, when a candidate's information in an ATS (application tracking system) is updated or when a new deal in a CRM (customer relationship management) application is created, a webhook can notify a client application about it. Webhooks make it simpler for an application to stay up-to-date with changes in the data or state of a SaaS provider. Before webhooks, a client application would have to continually poll a SaaS provider to keep up with any changes to its data or state. Webhooks work in the following fashion: - The developer configures a webhook by specifying the URL of the endpoint in their application that will receive an HTTP POST when a certain event occurs. - When the specified event occurs in the SaaS application, it sends an HTTP POST request containing information about the event in its payload to the URL defined in the configuration step above. - The application receiving the HTTP POST request processes the data in the request's payload and performs any necessary actions in response. These actions could include updating its records, sending notifications to end users, or triggering further processes. ## Workspace URL: https://docs.unified.to/concepts/glossary/workspace A workspace contains all of your product team, their environments, configuration, activated integrations, and customer connections. Basically, each company on [Unified.to](http://unified.to/) has 1 workspace. --- # Accounting API – Endpoints & Data Models ## Accounting API data models URL: https://docs.unified.to/accounting/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /accounting/{connection_id}/account | Create an account | | GET | /accounting/{connection_id}/account | List all accounts | | GET | /accounting/{connection_id}/account/{id} | Retrieve an account | | PUT | /accounting/{connection_id}/account/{id} | Update an account | | DELETE | /accounting/{connection_id}/account/{id} | Remove an account | | POST | /accounting/{connection_id}/journal | Create a journal | | GET | /accounting/{connection_id}/journal | List all journals | | GET | /accounting/{connection_id}/journal/{id} | Retrieve a journal | | PUT | /accounting/{connection_id}/journal/{id} | Update a journal | | DELETE | /accounting/{connection_id}/journal/{id} | Remove a journal | | POST | /accounting/{connection_id}/transaction | Create a transaction | | GET | /accounting/{connection_id}/transaction | List all transactions | | GET | /accounting/{connection_id}/transaction/{id} | Retrieve a transaction | | PUT | /accounting/{connection_id}/transaction/{id} | Update a transaction | | DELETE | /accounting/{connection_id}/transaction/{id} | Remove a transaction | | POST | /accounting/{connection_id}/contact | Create a contact | | GET | /accounting/{connection_id}/contact | List all contacts | | GET | /accounting/{connection_id}/contact/{id} | Retrieve a contact | | PUT | /accounting/{connection_id}/contact/{id} | Update a contact | | DELETE | /accounting/{connection_id}/contact/{id} | Remove a contact | | POST | /accounting/{connection_id}/invoice | Create an invoice | | GET | /accounting/{connection_id}/invoice | List all invoices | | GET | /accounting/{connection_id}/invoice/{id} | Retrieve an invoice | | PUT | /accounting/{connection_id}/invoice/{id} | Update an invoice | | DELETE | /accounting/{connection_id}/invoice/{id} | Remove an invoice | | POST | /accounting/{connection_id}/bill | Create a bill | | GET | /accounting/{connection_id}/bill | List all bills | | GET | /accounting/{connection_id}/bill/{id} | Retrieve a bill | | PUT | /accounting/{connection_id}/bill/{id} | Update a bill | | DELETE | /accounting/{connection_id}/bill/{id} | Remove a bill | | POST | /accounting/{connection_id}/creditmemo | Create a creditmemo | | GET | /accounting/{connection_id}/creditmemo | List all creditmemoes | | GET | /accounting/{connection_id}/creditmemo/{id} | Retrieve a creditmemo | | PUT | /accounting/{connection_id}/creditmemo/{id} | Update a creditmemo | | DELETE | /accounting/{connection_id}/creditmemo/{id} | Remove a creditmemo | | POST | /accounting/{connection_id}/vendorcredit | Create a vendorcredit | | GET | /accounting/{connection_id}/vendorcredit | List all vendorcredits | | GET | /accounting/{connection_id}/vendorcredit/{id} | Retrieve a vendorcredit | | PUT | /accounting/{connection_id}/vendorcredit/{id} | Update a vendorcredit | | DELETE | /accounting/{connection_id}/vendorcredit/{id} | Remove a vendorcredit | | POST | /accounting/{connection_id}/taxrate | Create a taxrate | | GET | /accounting/{connection_id}/taxrate | List all taxrates | | GET | /accounting/{connection_id}/taxrate/{id} | Retrieve a taxrate | | PUT | /accounting/{connection_id}/taxrate/{id} | Update a taxrate | | DELETE | /accounting/{connection_id}/taxrate/{id} | Remove a taxrate | | GET | /accounting/{connection_id}/organization/{id} | Retrieve an organization | | GET | /accounting/{connection_id}/organization | List all organizations | | POST | /accounting/{connection_id}/order | Create an order | | GET | /accounting/{connection_id}/order | List all orders | | GET | /accounting/{connection_id}/order/{id} | Retrieve an order | | PUT | /accounting/{connection_id}/order/{id} | Update an order | | DELETE | /accounting/{connection_id}/order/{id} | Remove an order | | POST | /accounting/{connection_id}/purchaseorder | Create a purchaseorder | | GET | /accounting/{connection_id}/purchaseorder | List all purchaseorders | | GET | /accounting/{connection_id}/purchaseorder/{id} | Retrieve a purchaseorder | | PUT | /accounting/{connection_id}/purchaseorder/{id} | Update a purchaseorder | | DELETE | /accounting/{connection_id}/purchaseorder/{id} | Remove a purchaseorder | | POST | /accounting/{connection_id}/salesorder | Create a salesorder | | GET | /accounting/{connection_id}/salesorder | List all salesorders | | GET | /accounting/{connection_id}/salesorder/{id} | Retrieve a salesorder | | PUT | /accounting/{connection_id}/salesorder/{id} | Update a salesorder | | DELETE | /accounting/{connection_id}/salesorder/{id} | Remove a salesorder | | GET | /accounting/{connection_id}/report/{id} | Retrieve a report | | GET | /accounting/{connection_id}/report | List all reports | | GET | /accounting/{connection_id}/balancesheet/{id} | Retrieve a balancesheet | | GET | /accounting/{connection_id}/balancesheet | List all balancesheets | | GET | /accounting/{connection_id}/trialbalance/{id} | Retrieve a trialbalance | | GET | /accounting/{connection_id}/trialbalance | List all trialbalances | | GET | /accounting/{connection_id}/profitloss/{id} | Retrieve a profitloss | | GET | /accounting/{connection_id}/profitloss | List all profitlosses | | GET | /accounting/{connection_id}/cashflow/{id} | Retrieve a cashflow | | GET | /accounting/{connection_id}/cashflow | List all cashflows | | POST | /accounting/{connection_id}/category | Create a category | | GET | /accounting/{connection_id}/category | List all categories | | GET | /accounting/{connection_id}/category/{id} | Retrieve a category | | PUT | /accounting/{connection_id}/category/{id} | Update a category | | DELETE | /accounting/{connection_id}/category/{id} | Remove a category | | POST | /accounting/{connection_id}/expense | Create an expense | | GET | /accounting/{connection_id}/expense | List all expenses | | GET | /accounting/{connection_id}/expense/{id} | Retrieve an expense | | PUT | /accounting/{connection_id}/expense/{id} | Update an expense | | DELETE | /accounting/{connection_id}/expense/{id} | Remove an expense | #### Data Models ### AccountingAccount Chart of accounts | Field | Type | Required | Description | |---|---|---|---| | id | string | | Identifier used by the SaaS application to uniquely identify the Account | | created_at | string (date) | | Date and time when the Account was created, in ISO 8601 format and UTC (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | Date and time when the Account was last updated, in ISO 8601 format and UTC (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | Account name | | description | string | | Account description | | type | string enum: ACCOUNTS_PAYABLE, ACCOUNTS_RECEIVABLE, BANK, CREDIT_CARD, FIXED_ASSET, LIABILITY, EQUITY, EXPENSE, REVENUE, OTHER | | Account type, such as BANK. EQUITY, ASSET, ... | | status | string enum: ACTIVE, ARCHIVED | | Account status, such as ACTIVE or ARCHIVED | | balance | number | | Balance of the account. | | currency | string | | Account’s currency, in ISO 4217 format (e.g. U.S. dollars is USD) | | customer_defined_code | string | | Identifier for tracking and categorizing accounts for reporting or analysis purposes that is defined by the end-customer | | is_payable | boolean | | True if the account is an “accounts payable” account | | section | string | | @deprecated; use taxonomy | | subsection | string | | @deprecated; use taxonomy | | group | string | | @deprecated; use taxonomy | | subgroup | string | | @deprecated; use taxonomy | | parent_id | string | | The parent account ID for this account | | taxonomy | AccountingAccountTaxonomy[] | | | | organization_id | string | | | | raw | any | | The original data from the integration's API | ### AccountingBalancesheet | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | currency | string | | | | net_assets_amount | number | | | | assets | AccountingBalancesheetItem[] | | | | liabilities | AccountingBalancesheetItem[] | | | | equity | AccountingBalancesheetItem[] | | | | raw | any | | | ### AccountingBill | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | bill_number | string | | External identifier for this invoice | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | due_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | paid_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | refunded_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | cancelled_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | posted_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_amount | number | | | | paid_amount | number | | | | refund_amount | number | | | | tax_amount | number | | | | discount_amount | number | | | | balance_amount | number | | | | contact_id | string | | (reference to AccountingContact) | | currency | string | | | | notes | string | | | | refund_reason | string | | | | lineitems | AccountingLineitem[] | | | | status | string enum: DRAFT, VOIDED, AUTHORIZED, PAID, PARTIALLY_PAID, PARTIALLY_REFUNDED, REFUNDED, SUBMITTED, DELETED, OVERDUE | | | | url | string | | The public URL for the invoice to send to a customer to view or pay. | | payment_collection_method | string enum: send_invoice, charge_automatically | | When set to charging_automatically, an automated attempt will occur to pay this invoice using the default payment source attached to the contactcustomer. When set to send_invoice, an will email will be sent with this invoice to the contact/customer with payment instructions. | | attachments | AccountingAttachment[] | | | | send | boolean | | | | organization_id | string | | | | term | string enum: ON_RECEIPT, NET_7, NET_10, NET_15, NET_20, NET_25, NET_30, NET_45, NET_60, NET_90, OTHER | | @deprecated use payment_terms instead | | payment_terms | string enum: ON_RECEIPT, NET_7, NET_10, NET_15, NET_20, NET_25, NET_30, NET_45, NET_60, NET_90, OTHER | | | | payments | AccountingPaymentReference[] | | read-only reciprocal of PaymentPayment.allocations; payments applied to this invoice | | category_ids | AccountingBill_category_ids | | (reference to AccountingCategory) | | raw | any | | | ### AccountingCashflow Sections | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | category_ids | AccountingCashflow_category_ids | | (reference to AccountingCategory) | | contact_id | string | | (reference to AccountingContact) | | name | string | | e.g. "Cash Flow Statement Q1 2020" | | currency | string | | ISO 4217, e.g. "USD" | | cash_beginning_amount | number | | Cash at beginning of period | | cash_ending_amount | number | | Cash at end of period | | net_change_in_cash_amount | number | | Usually ending - beginning | | operating_sections | AccountingCashflowSection[] | | | | investing_sections | AccountingCashflowSection[] | | | | financing_sections | AccountingCashflowSection[] | | | | raw | any | | Original data from the integration | ### AccountingCategory | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | is_active | boolean | | | | parent_id | string | | | | type | string enum: CLASS, DEPARTMENT, LOCATION, PROJECT, TASK, CUSTOM, EXPENSE, INCOME | | The kind of dimension | | code | string | | platform-side tracking code | | organization_id | string | | | | raw | any | | | ### AccountingContact | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | first_name | string | | | | last_name | string | | | | emails | AccountingEmail[] | | | | telephones | AccountingTelephone[] | | | | currency | string | | | | billing_address | AccountingContact_billing_address | | | | shipping_address | AccountingContact_shipping_address | | | | is_active | boolean | | | | tax_exemption | string enum: FEDERAL_GOV, REGION_GOV, LOCAL_GOV, TRIBAL_GOV, CHARITABLE_ORG, RELIGIOUS_ORG, EDUCATIONAL_ORG, MEDICAL_ORG, RESALE, FOREIGN, OTHER | | | | tax_number | string | | The ID/number of the customer's tax number. This is also known as the ABN (Australia), GST Number (New Zealand), VAT Number (UK) or Tax ID Number (US and global). | | is_customer | boolean | | | | is_supplier | boolean | | | | portal_url | string | | URL for the contact’s portal | | payment_methods | AccountingContactPaymentMethod[] | | | | company_name | string | | | | identification | string | | contact account numbers such as registration, A membership or identification reference to help to identify and search customers | | associated_contacts | AccountingAssociatedContact[] | | | | organization_id | string | | | | payment_terms | string enum: ON_RECEIPT, NET_7, NET_10, NET_15, NET_20, NET_25, NET_30, NET_45, NET_60, NET_90, OTHER | | | | raw | any | | | ### AccountingCreditmemo | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | due_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | paid_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | refunded_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | cancelled_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | posted_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_amount | number | | | | paid_amount | number | | | | refund_amount | number | | | | tax_amount | number | | | | discount_amount | number | | | | balance_amount | number | | | | creditmemo_number | string | | External identifier for this invoice | | contact_id | string | | (reference to AccountingContact) | | invoice_id | string | | (reference to AccountingInvoice) | | currency | string | | | | notes | string | | | | refund_reason | string | | | | lineitems | AccountingLineitem[] | | | | status | string enum: DRAFT, VOIDED, AUTHORIZED, PAID, PARTIALLY_PAID, PARTIALLY_REFUNDED, REFUNDED, SUBMITTED, DELETED, OVERDUE | | | | url | string | | The public URL for the invoice to send to a customer to view or pay. | | payment_collection_method | string enum: send_invoice, charge_automatically | | When set to charging_automatically, an automated attempt will occur to pay this invoice using the default payment source attached to the contactcustomer. When set to send_invoice, an will email will be sent with this invoice to the contact/customer with payment instructions. | | attachments | AccountingAttachment[] | | | | send | boolean | | | | organization_id | string | | | | apply_amount | number | | | | applications | AccountingCreditApplication[] | | What this credit memo was applied to (invoices/bills). Writable inline on create/update. | | raw | any | | | ### AccountingExpense | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | user_id | string | | references a HR employee/user (reference to HrisEmployee) | | contact_id | string | | (reference to AccountingContact) | | account_id | string | | (reference to AccountingAccount) | | name | string | | | | payment_method | string | | | | posted_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_amount | number | | | | currency | string | | | | tax_amount | number | | | | reimbursed_amount | number | | | | reimbursed_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | approved_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | approver_user_id | string | | references HR employee/user (reference to HrisEmployee) | | lineitems | AccountingLineitem[] | | | | attachments | AccountingAttachment[] | | | | organization_id | string | | | | users | AccountingReference[] | | | | approver_users | AccountingReference[] | | expense approver(s); id is HR employee/user when resolved | | status | string enum: DRAFT, SUBMITTED, PENDING, APPROVED, REJECTED, PAID | | | | external_number | string | | | | category_ids | AccountingExpense_category_ids | | (reference to AccountingCategory) | | raw | any | | | ### AccountingInvoice | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | due_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | paid_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | refunded_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | cancelled_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | posted_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_amount | number | | | | paid_amount | number | | | | refund_amount | number | | | | tax_amount | number | | | | discount_amount | number | | | | balance_amount | number | | | | invoice_number | string | | External identifier (ie. reference) for this invoice | | reference | string | | | | contact_id | string | | (reference to AccountingContact) | | currency | string | | | | notes | string | | | | refund_reason | string | | | | term | string enum: ON_RECEIPT, NET_7, NET_10, NET_15, NET_20, NET_25, NET_30, NET_45, NET_60, NET_90, OTHER | | @deprecated use payment_terms instead | | payment_terms | string enum: ON_RECEIPT, NET_7, NET_10, NET_15, NET_20, NET_25, NET_30, NET_45, NET_60, NET_90, OTHER | | | | lineitems | AccountingLineitem[] | | | | status | string enum: DRAFT, VOIDED, AUTHORIZED, PAID, PARTIALLY_PAID, PARTIALLY_REFUNDED, REFUNDED, SUBMITTED, DELETED, OVERDUE | | | | url | string | | The public URL for the invoice to send to a customer to view or pay. | | payment_collection_method | string enum: send_invoice, charge_automatically | | When set to charging_automatically, an automated attempt will occur to pay this invoice using the default payment source attached to the contactcustomer. When set to send_invoice, an will email will be sent with this invoice to the contact/customer with payment instructions. | | type | string enum: BILL, INVOICE, CREDITMEMO | | @deprecated; for bills, use AccountingBill instead | | attachments | AccountingAttachment[] | | | | send | boolean | | | | organization_id | string | | | | payments | AccountingPaymentReference[] | | ead-only reciprocal of PaymentPayment.allocations; payments applied to this invoice | | category_ids | AccountingInvoice_category_ids | | (reference to AccountingCategory) | | raw | any | | | ### AccountingJournal | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | reference | string | | | | tax_amount | number | | | | currency | string | | | | lineitems | AccountingJournalLineitem[] | | new field name | | taxrate_id | string | | | | description | string | | | | posted_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | source | string | | | | organization_id | string | | | | category_ids | AccountingJournal_category_ids | | (reference to AccountingCategory) | | attachments | AccountingAttachment[] | | | | raw | any | | | ### AccountingOrder | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | posted_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | contact_id | string | | Customer, Supplier (reference to AccountingContact) | | account_id | string | | (reference to AccountingAccount) | | type | string enum: SALES, PURCHASE | | | | currency | string | | | | total_amount | number | | | | shipping_address | AccountingOrder_shipping_address | | | | billing_address | AccountingOrder_billing_address | | | | status | string enum: DRAFT, VOIDED, AUTHORIZED, PAID, PARTIALLY_PAID, PARTIALLY_REFUNDED, REFUNDED, SUBMITTED, DELETED | | | | lineitems | AccountingLineitem[] | | | | organization_id | string | | | | raw | any | | | ### AccountingOrganization | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | legal_name | string | | | | currency | string | | Currency primarily used by the organization, in ISO 4217 format (e.g. U.S. dollars is USD). | | address | AccountingOrganization_address | | | | tax_number | string | | Organization's tax number. For example, in the U.S., this is an Employer Identification Number (EIN). | | timezone | string | | | | website | string | | | | parent_id | string | | | | fiscal_year_end_month | number | | Month of the year when the organization’s fiscal year ends (1 - 12, where January is 1) | | organization_code | string | | Identifier for tracking and categorizing organizations for reporting or analysis purposes that is defined by the end-customer | | type | string enum: COMPANY, SUBSIDIARY, DIVISION, LOCATION | | Entity type within a consolidation hierarchy | | is_elimination | boolean | | True for consolidation/elimination entities | | raw | any | | | ### AccountingProfitloss | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | category_ids | AccountingProfitloss_category_ids | | (reference to AccountingCategory) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | currency | string | | | | income | AccountingProfitlossCategory[] | | @deprecated – use income_sections instead | | expenses | AccountingProfitlossCategory[] | | @deprecated – use expenses_sections instead | | cost_of_goods_sold | AccountingProfitlossCategory[] | | @deprecated – use cost_of_goods_sold_sections instead | | gross_profit_amount | number | | @deprecated – compute using income_total_amount - cost_of_goods_sold_total_amount | | net_profit_amount | number | | @deprecated – use net_income_amount instead | | income_total_amount | number | | | | net_income_amount | number | | | | expenses_total_amount | number | | | | cost_of_goods_sold_total_amount | number | | | | income_sections | AccountingProfitlossSection[] | | | | expenses_sections | AccountingProfitlossSection[] | | | | cost_of_goods_sold_sections | AccountingProfitlossSection[] | | | | raw | any | | | ### AccountingPurchaseorder | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | posted_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | contact_id | string | | Customer, Supplier (reference to AccountingContact) | | account_id | string | | (reference to AccountingAccount) | | currency | string | | | | total_amount | number | | | | shipping_address | AccountingPurchaseorder_shipping_address | | | | billing_address | AccountingPurchaseorder_billing_address | | | | status | string enum: DRAFT, VOIDED, AUTHORIZED, PAID, PARTIALLY_PAID, PARTIALLY_REFUNDED, REFUNDED, SUBMITTED, DELETED | | | | lineitems | AccountingLineitem[] | | | | organization_id | string | | | | category_ids | AccountingPurchaseorder_category_ids | | (reference to AccountingCategory) | | raw | any | | | ### AccountingReport @deprecated; use either AccountingProfitandloss, AccountingTrialbalance, AccountingBalancesheet, or AccountingCashflow instead | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | type | string enum: TRIAL_BALANCE, BALANCE_SHEET, PROFIT_AND_LOSS | | | | name | string | | | | currency | string | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | balance_sheet | AccountingReport_balance_sheet | | | | profit_and_loss | AccountingReport_profit_and_loss | | | | trial_balance | AccountingReport_trial_balance | | | | raw | any | | | ### AccountingSalesorder | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | posted_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | contact_id | string | | Customer, Supplier (reference to AccountingContact) | | account_id | string | | (reference to AccountingAccount) | | currency | string | | | | total_amount | number | | | | shipping_address | AccountingSalesorder_shipping_address | | | | billing_address | AccountingSalesorder_billing_address | | | | status | string enum: DRAFT, VOIDED, AUTHORIZED, PAID, PARTIALLY_PAID, PARTIALLY_REFUNDED, REFUNDED, SUBMITTED, DELETED | | | | lineitems | AccountingLineitem[] | | | | sales_channel | string | | | | organization_id | string | | | | fees | AccountingFee[] | | | | category_ids | AccountingSalesorder_category_ids | | (reference to AccountingCategory) | | raw | any | | | ### AccountingTaxrate | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | rate | number | | % | | is_active | boolean | | | | organization_id | string | | | | raw | any | | | ### AccountingTransaction | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | memo | string | | | | total_amount | number | | negative for CREDIT, positive for DEBIT | | tax_amount | number | | negative for CREDIT, positive for DEBIT | | account_id | string | | (reference to AccountingAccount) | | reference | string | | | | sub_total_amount | number | | | | split_account_id | string | | | | payment_method | string | | | | payment_terms | string | | | | customer_message | string | | | | type | string | | eg. CreditCardCharge, Check, Invoice, ReceivePayment, JournalEntry, Bill, CreditCardCredit, VendorCredit, Credit, BillPaymentCheck, BillPaymentCreditCard, Charge, Transfer, Deposit, BANK_DEPOSIT, BANK_TRANSFER, Statement, BillableCharge, TimeActivity, CashPurchase, SalesReceipt, CreditMemo, CreditRefund, Estimate, InventoryQuantityAdjustment, PurchaseOrder, GlobalTaxPayment, GlobalTaxAdjustment, Service Tax Refund, Service Tax Gross Adjustment, Service Tax Reversal, Service Tax Defer, Service Tax Partial Utilisation | | lineitems | AccountingTransactionLineItem[] | | | | currency | string | | | | contacts | AccountingTransactionContact[] | | | | organization_id | string | | | | category_ids | AccountingTransaction_category_ids | | (reference to AccountingCategory) | | raw | any | | | ### AccountingTrialbalance | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | currency | string | | | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_debit_amount | number | | | | total_credit_amount | number | | | | sub_items | AccountingTrialbalanceSubItem[] | | | | raw | any | | | ### AccountingVendorcredit | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | due_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | posted_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_amount | number | | | | balance_amount | number | | | | account_id | string | | (reference to AccountingAccount) | | currency | string | | | | contact_id | string | | (reference to AccountingContact) | | bill_id | string | | | | notes | string | | | | lineitems | AccountingLineitem[] | | | | status | string enum: DRAFT, VOIDED, AUTHORIZED, PAID, PARTIALLY_PAID, PARTIALLY_REFUNDED, REFUNDED, SUBMITTED, DELETED, OVERDUE | | | | organization_id | string | | | | apply_amount | number | | | | applications | AccountingCreditApplication[] | | What this vendor credit was applied to (invoices/bills). Writable inline on create/update. | | raw | any | | | ### AccountingAccountTaxonomy | Field | Type | Required | Description | |---|---|---|---| | type | string enum: CLASSIFICATION, GROUP, SUBGROUP, SYSTEM_ROLE, OTHER | Yes | | | original_type | string | | | | value | string | Yes | | ### AccountingAssociatedContact | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | | emails | AccountingEmail[] | | | ### AccountingAttachment | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | download_url | string | | | | name | string | | | | mime_type | string | | | ### AccountingBalancesheetItem | Field | Type | Required | Description | |---|---|---|---| | account_id | string | | (reference to AccountingAccount) | | name | string | | | | amount | number | | | | sub_items | AccountingBalancesheetItem_sub_items | | | ### AccountingBill_category_ids ### AccountingCashflowItem | Field | Type | Required | Description | |---|---|---|---| | account_id | string | | If attributable to a specific GL account (reference to AccountingAccount) | | name | string | | e.g. "Net Income", "Depreciation", "Equipment" | | amount | number | | Positive = inflow, Negative = outflow | | transaction_ids | AccountingCashflowItem_transaction_ids | | Optional linkage to transactions | | sub_items | AccountingCashflowItem_sub_items | | | ### AccountingCashflowItem_transaction_ids Optional linkage to transactions ### AccountingCashflowSection | Field | Type | Required | Description | |---|---|---|---| | section_name | string | | e.g. "Operating Activities" | | total_amount | number | | Net cash providedused by this section | | items | AccountingCashflowItem[] | | | ### AccountingCashflow_category_ids ### AccountingContactPaymentMethod | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | type | string enum: ACH, ALIPAY, CARD, GIROPAY, IDEAL, OTHER, PAYPAL, WIRE, CHECK | Yes | | | name | string | | | | default | boolean | | | ### AccountingContact_billing_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AccountingContact_shipping_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AccountingCreditApplication | Field | Type | Required | Description | |---|---|---|---| | object_type | string enum: INVOICE, BILL | | The type of object this credit was applied to | | object_id | string | | The id of the object this credit was applied to | | amount | number | | The amount of credit applied to this object | | applied_at | string (date) | | Date and time when the credit was applied, in ISO 8601 format and UTC (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### AccountingEmail | Field | Type | Required | Description | |---|---|---|---| | email | string | | | | type | string enum: WORK, HOME, OTHER | | | ### AccountingExpense_category_ids ### AccountingFee | Field | Type | Required | Description | |---|---|---|---| | type | string enum: TAX, DISCOUNT, PROMOTION, SHIPPING, GIFT_WRAP, COD, SURCHARGE, OTHER | Yes | Unified fee category | | original_type | string | | The provider's original fee label, e.g. "VAT" or a shipping carrier name | | amount | number | Yes | | | currency | string | | ISO 4217 currency code, e.g. USD | ### AccountingInvoice_category_ids ### AccountingJournalLineitem | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | tax_amount | number | | | | total_amount | number | | will be a positive value for a debit and negative for a credit | | debit_amount | number | | will replace total_amount (absolute value) | | credit_amount | number | | | | description | string | | | | account_id | string | | (reference to AccountingAccount) | | contact_id | string | | (reference to AccountingContact) | | payment_id | string | | link to PaymentPayment (reference to PaymentPayment) | | invoice_id | string | | (reference to AccountingInvoice) | | category_ids | AccountingJournalLineitem_category_ids | | (reference to AccountingCategory) | | group_id | string | | points to a HRIS Group (reference to HrisGroup) | | project_id | string | | points to a Task Project (reference to TaskProject) | | organization_id | string | | points to a AccountingOrganization | ### AccountingJournalLineitem_category_ids ### AccountingJournal_category_ids ### AccountingLineitem | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | refunded_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_amount | number | | unit_quantity * unit_amount + tax_amount | | refund_amount | number | | | | discount_amount | number | | | | tax_amount | number | | | | item_id | string | | (reference to CommerceItem) | | unit_amount | number | | | | unit_quantity | number | | | | item_sku | string | | | | item_name | string | | | | item_description | string | | | | notes | string | | | | taxrate_id | string | | | | account_id | string | | (reference to AccountingAccount) | | category_ids | AccountingLineitem_category_ids | | (reference to AccountingCategory) | | locations | AccountingReference[] | | | | item_variants | AccountingReference[] | | | | fees | AccountingFee[] | | | | contact_id | string | | (reference to AccountingContact) | ### AccountingLineitem_category_ids ### AccountingOrder_billing_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AccountingOrder_shipping_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AccountingOrganization_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AccountingPaymentReference | Field | Type | Required | Description | |---|---|---|---| | payment_id | string | | references a PaymentPayment that was applied to this object (reference to PaymentPayment) | | amount | number | | amount of the payment applied to this object | | allocated_at | string (date) | | Date and time when the payment was applied, in ISO 8601 format and UTC (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### AccountingProfitlossAccount | Field | Type | Required | Description | |---|---|---|---| | account_id | string | | (reference to AccountingAccount) | | account_name | string | | | | total_amount | number | | | | transaction_ids | AccountingProfitlossAccount_transaction_ids | | | ### AccountingProfitlossAccount_transaction_ids ### AccountingProfitlossCategory @deprecated | Field | Type | Required | Description | |---|---|---|---| | name | string | | | | amount | number | | | | sub_items | AccountingProfitlossSubcategory[] | | | ### AccountingProfitlossSection | Field | Type | Required | Description | |---|---|---|---| | section_type | string | | | | section_name | string | | | | total_amount | number | | | | accounts | AccountingProfitlossAccount[] | | | ### AccountingProfitlossSubcategory @deprecated | Field | Type | Required | Description | |---|---|---|---| | name | string | | | | amount | number | | | | transaction_ids | AccountingProfitlossSubcategory_transaction_ids | | | ### AccountingProfitlossSubcategory_transaction_ids ### AccountingProfitloss_category_ids ### AccountingPurchaseorder_billing_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AccountingPurchaseorder_category_ids ### AccountingPurchaseorder_shipping_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AccountingReference | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | ### AccountingReport_balance_sheet | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | currency | string | | | | net_assets_amount | number | | | | assets | AccountingBalancesheetItem[] | | | | liabilities | AccountingBalancesheetItem[] | | | | equity | AccountingBalancesheetItem[] | | | | raw | any | | | ### AccountingReport_profit_and_loss | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | category_ids | AccountingReport_profit_and_loss_category_ids | | (reference to AccountingCategory) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | currency | string | | | | income | AccountingProfitlossCategory[] | | @deprecated – use income_sections instead | | expenses | AccountingProfitlossCategory[] | | @deprecated – use expenses_sections instead | | cost_of_goods_sold | AccountingProfitlossCategory[] | | @deprecated – use cost_of_goods_sold_sections instead | | gross_profit_amount | number | | @deprecated – compute using income_total_amount - cost_of_goods_sold_total_amount | | net_profit_amount | number | | @deprecated – use net_income_amount instead | | income_total_amount | number | | | | net_income_amount | number | | | | expenses_total_amount | number | | | | cost_of_goods_sold_total_amount | number | | | | income_sections | AccountingProfitlossSection[] | | | | expenses_sections | AccountingProfitlossSection[] | | | | cost_of_goods_sold_sections | AccountingProfitlossSection[] | | | | raw | any | | | ### AccountingReport_profit_and_loss_category_ids ### AccountingReport_trial_balance | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | currency | string | | | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_debit_amount | number | | | | total_credit_amount | number | | | | sub_items | AccountingTrialbalanceSubItem[] | | | | raw | any | | | ### AccountingSalesorder_billing_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AccountingSalesorder_category_ids ### AccountingSalesorder_shipping_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AccountingTelephone | Field | Type | Required | Description | |---|---|---|---| | telephone | string | | | | type | string enum: WORK, HOME, OTHER, FAX, MOBILE | | | ### AccountingTransactionContact | Field | Type | Required | Description | |---|---|---|---| | id | string | Yes | | | is_customer | boolean | | | | is_supplier | boolean | | | ### AccountingTransactionLineItem | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | unit_quantity | number | | | | unit_amount | number | | | | total_amount | number | | will be a positive value for a debit and negative for a credit | | account_id | string | | (reference to AccountingAccount) | | object_type | string | | | | name | string | | | | description | string | | | | category_ids | AccountingTransactionLineItem_category_ids | | (reference to AccountingCategory) | ### AccountingTransactionLineItem_category_ids ### AccountingTransaction_category_ids ### AccountingTrialbalanceSubItem | Field | Type | Required | Description | |---|---|---|---| | amount | number | | | | account_id | string | | (reference to AccountingAccount) | | account_name | string | | | --- # Ads API – Endpoints & Data Models ## Ads API data models URL: https://docs.unified.to/ads/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /ads/{connection_id}/organization | Create an organization | | GET | /ads/{connection_id}/organization | List all organizations | | GET | /ads/{connection_id}/organization/{id} | Retrieve an organization | | PUT | /ads/{connection_id}/organization/{id} | Update an organization | | DELETE | /ads/{connection_id}/organization/{id} | Remove an organization | | POST | /ads/{connection_id}/campaign | Create a campaign | | GET | /ads/{connection_id}/campaign | List all campaigns | | GET | /ads/{connection_id}/campaign/{id} | Retrieve a campaign | | PUT | /ads/{connection_id}/campaign/{id} | Update a campaign | | DELETE | /ads/{connection_id}/campaign/{id} | Remove a campaign | | POST | /ads/{connection_id}/creative | Create a creative | | GET | /ads/{connection_id}/creative | List all creatives | | GET | /ads/{connection_id}/creative/{id} | Retrieve a creative | | PUT | /ads/{connection_id}/creative/{id} | Update a creative | | DELETE | /ads/{connection_id}/creative/{id} | Remove a creative | | POST | /ads/{connection_id}/asset | Create an asset | | GET | /ads/{connection_id}/asset | List all assets | | GET | /ads/{connection_id}/asset/{id} | Retrieve an asset | | POST | /ads/{connection_id}/insertionorder | Create an insertionorder | | GET | /ads/{connection_id}/insertionorder | List all insertionorders | | GET | /ads/{connection_id}/insertionorder/{id} | Retrieve an insertionorder | | PUT | /ads/{connection_id}/insertionorder/{id} | Update an insertionorder | | DELETE | /ads/{connection_id}/insertionorder/{id} | Remove an insertionorder | | POST | /ads/{connection_id}/group | Create a group | | GET | /ads/{connection_id}/group | List all groups | | GET | /ads/{connection_id}/group/{id} | Retrieve a group | | PUT | /ads/{connection_id}/group/{id} | Update a group | | DELETE | /ads/{connection_id}/group/{id} | Remove a group | | POST | /ads/{connection_id}/ad | Create an ad | | GET | /ads/{connection_id}/ad | List all ads | | GET | /ads/{connection_id}/ad/{id} | Retrieve an ad | | PUT | /ads/{connection_id}/ad/{id} | Update an ad | | DELETE | /ads/{connection_id}/ad/{id} | Remove an ad | | GET | /ads/{connection_id}/report | List all reports | | GET | /ads/{connection_id}/target/{id} | Retrieve a target | | GET | /ads/{connection_id}/target | List all targets | | GET | /ads/{connection_id}/promoted/{id} | Retrieve a promoted | | GET | /ads/{connection_id}/promoted | List all promoteds | #### Data Models ### AdsAd | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | campaign_id | string | | | | group_id | string | | (reference to AdsGroup) | | organization_id | string | | | | status | string enum: UNSPECIFIED, ACTIVE, PAUSED, ARCHIVED, DRAFT, SCHEDULED_FOR_DELETION, PROCESSING, PROCESSING_FAILED | | | | ad_type | string enum: TEXT, IMAGE, VIDEO, RESPONSIVE, SHOPPING, APP, CALL, CAROUSEL, SOCIAL, DISPLAY, SEARCH, AUDIO, YOUTUBE, NATIVE, CTV, DOOH | | | | advertiser_name | string | | | | creative_ids | AdsAd_creative_ids | | | | creative_asset_url | string | | | | ad_copy | string | | | | headline | string | | | | description | string | | | | cta | string | | | | final_url | string | | | | display_url | string | | | | path1 | string | | | | path2 | string | | | | promoted | AdsPromoted[] | | | | logo_creative_id | string | | | | raw | any | | | ### AdsAsset | Field | Type | Required | Description | |---|---|---|---| | id | string | | provider asset resource name/id (e.g. Google Ads customers/{cid}/assets/{id}, TikTok image/video id, DV360 mediaId) | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | organization_id | string | | | | name | string | | | | type | string enum: IMAGE, VIDEO, YOUTUBE_VIDEO, MEDIA_BUNDLE, TEXT | | | | url | string | | outbound: public asset URL; inbound (create): source URL to fetch bytes from when content is not supplied | | content | string | | inbound create-only: base64-encoded image/media bytes to upload | | mime_type | string | | | | width | number | | | | height | number | | | | file_size | number | | | | raw | any | | | ### AdsCampaign | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | organization_id | string | | | | status | string enum: UNSPECIFIED, ACTIVE, PAUSED, ARCHIVED, DRAFT, SCHEDULED_FOR_DELETION, PROCESSING, PROCESSING_FAILED | | | | effective_status | string enum: UNSPECIFIED, SERVING, LIMITED, LEARNING, PAUSED, PENDING, ENDED, MISCONFIGURED, NOT_ELIGIBLE, ARCHIVED, REMOVED | | read-only; controlled by the provider (whether/why the campaign can serve), unlike the user-managed status | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | budget_amount | number | | | | budget_period | string enum: DAILY, MONTHLY, TOTAL, LIFETIME | | | | budget_unit | string enum: UNSPECIFIED, CURRENCY, IMPRESSIONS | | | | total_spend_amount | number | | | | targeting | AdsCampaign_targeting | | | | goal | string enum: UNSPECIFIED, BRAND_AWARENESS, ENGAGEMENT, REACH, WEBSITE_TRAFFIC, LEADS, SALES, APP_PROMOTION | | | | planned_spend_amount | number | | | | frequency_cap | AdsCampaign_frequency_cap | | | | advertising_channel_type | string enum: TEXT, IMAGE, VIDEO, RESPONSIVE, SHOPPING, APP, CALL, CAROUSEL, SOCIAL, DISPLAY, SEARCH, AUDIO, YOUTUBE, NATIVE, CTV, DOOH | | | | campaign_budget_identifier | string | | Resource name for existing/shared budget (Google) | | currency | string | | | | category | string | | Housing, employment, credit, NONE (Meta) | | has_eu_political_ads | boolean | | | | raw | any | | | ### AdsCreative | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | campaign_id | string | | | | group_id | string | | (reference to AdsGroup) | | item_id | string | | references Commerce Item ID (reference to CommerceItem) | | organization_id | string | | | | status | string enum: UNSPECIFIED, ACTIVE, PAUSED, ARCHIVED, DRAFT, SCHEDULED_FOR_DELETION, PROCESSING, PROCESSING_FAILED | | | | creative_type | string enum: UNSPECIFIED, STANDARD, EXPANDABLE, VIDEO, NATIVE, AUDIO, PUBLISHER_HOSTED, ASSET_BASED, IMAGE, DOCUMENT | | | | hosting_source | string enum: UNSPECIFIED, CM, THIRD_PARTY, HOSTED, RICH_MEDIA, PUBLISHER_HOSTED | | | | width | number | | | | height | number | | | | asset_urls | AdsCreative_asset_urls | | Hosted asset mode (best-effort, provider-specific | | link_url | string | | Destination URL for creatives with links | | body | string | | | | title | string | | | | cta | string | | | | third_party_tag | string | | | | vast_tag_url | string | | | | external_creative_reference | string | | | | external_placement_reference | string | | | | external_ad_reference | string | | | | promoted | AdsPromoted[] | | | | path1 | string | | Display URL path 1 (Microsoft RSA Path1) | | path2 | string | | Display URL path 2 (Microsoft RSA Path2) | | data | string | | | | raw | any | | | ### AdsGroup | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | parent_id | string | | | | campaign_id | string | | | | organization_id | string | | | | insertionorder_id | string | | | | status | string enum: UNSPECIFIED, ACTIVE, PAUSED, ARCHIVED, DRAFT, SCHEDULED_FOR_DELETION, PROCESSING, PROCESSING_FAILED | | | | effective_status | string enum: UNSPECIFIED, SERVING, LIMITED, LEARNING, PAUSED, PENDING, ENDED, MISCONFIGURED, NOT_ELIGIBLE, ARCHIVED, REMOVED | | | | targeting | AdsGroup_targeting | | | | bid_amount | number | | | | bid_strategy | AdsGroup_bid_strategy | | YOUTUBE_AND_PARTNERS | | budget_amount | number | | | | budget_period | string enum: DAILY, MONTHLY, TOTAL, LIFETIME | | | | budget_allocation_type | string enum: UNSPECIFIED, AUTOMATIC, FIXED, UNLIMITED | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | budget_unit | string enum: UNSPECIFIED, CURRENCY, IMPRESSIONS | | | | budget_max_amount | number | | | | type | string enum: TEXT, IMAGE, VIDEO, RESPONSIVE, SHOPPING, APP, CALL, CAROUSEL, SOCIAL, DISPLAY, SEARCH, AUDIO, YOUTUBE, NATIVE, CTV, DOOH | | | | has_eu_political_ads | boolean | | | | pacing | AdsGroup_pacing | | | | frequency_cap | AdsGroup_frequency_cap | | | | creative_ids | AdsGroup_creative_ids | | | | optimization_goal | string enum: REACH, IMPRESSIONS, LINK_CLICKS, LANDING_PAGE_VIEWS, CONVERSIONS, LEAD_GENERATION, APP_INSTALLS, APP_ENGAGEMENT, VIDEO_VIEWS, ENGAGEMENT, PAGE_LIKES, MESSAGES | | Optimization goals for ads_group (cross-platform; platform-specific values allowed as pass-through) | | billing_event | string enum: IMPRESSIONS, LINK_CLICKS, VIDEO_VIEWS, APP_INSTALLS, ENGAGEMENT, PAGE_LIKES, MESSAGES, POST_ENGAGEMENT, PURCHASE, NONE | | Billing events for ads_group (what you pay for; cross-platform; platform-specific values allowed as pass-through) | | currency | string | | | | promoted | AdsPromoted[] | | | | raw | any | | | ### AdsInsertionorder | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | organization_id | string | | | | campaign_id | string | | | | status | string enum: UNSPECIFIED, ACTIVE, PAUSED, ARCHIVED, DRAFT, SCHEDULED_FOR_DELETION, PROCESSING, PROCESSING_FAILED | | | | pacing | AdsInsertionorder_pacing | | | | frequency_cap | AdsInsertionorder_frequency_cap | | | | kpi | AdsInsertionorder_kpi | | | | budget_unit | string enum: UNSPECIFIED, CURRENCY, IMPRESSIONS | | | | budget_segments | AdsInsertionorderBudgetSegment[] | | | | bid_strategy | AdsInsertionorder_bid_strategy | | YOUTUBE_AND_PARTNERS | | reference | string | | client's reference ID for the insertion order | | raw | any | | | ### AdsOrganization | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | currency | string | | | | timezone | string | | | | parent_id | string | | | | raw | any | | | ### AdsPromoted Promoted entity (e.g. page, app, product, tweet) for ads create | Field | Type | Required | Description | |---|---|---|---| | id | string | Yes | | | name | string | | | | type | string enum: PAGE_ID, APP_ID, STORE_URL, PIXEL_ID, CUSTOM_CONVERSION_ID, CATALOG_ID, PRODUCT_SET_ID, PRIORITIZED_SET_ID, EVENT_ID, OFFER_ID, LEAD_FORM_ID, MESSAGING_CHANNEL_ID, PRODUCT_ID, TWEET_ID, AD_GROUP_TYPE | Yes | Promoted entity types for ads_promoted list endpoint | | raw | any | | | ### AdsTarget Targeting search result (for ads_target list endpoint) | Field | Type | Required | Description | |---|---|---|---| | id | string | Yes | | | name | string | | | | type | string enum: INTEREST, BEHAVIOR, LOCALE, COUNTRY, REGION, CITY, ZIP, US_DMA, TOPIC, USER_LIST, CARRIER, DEVICE_MODEL, OS_VERSION | | Targeting search types for ads_target list endpoint (Meta: adinterest, adbehavior, adlocale, adgeolocation; Google: geoTargetConstants, user_interest, topic_constant, language_constant) | | is_active | boolean | | | | parent_id | string | | | | audience_count_min | number | | | | audience_count_max | number | | | | raw | any | | | ### AdSchedule | Field | Type | Required | Description | |---|---|---|---| | day_of_week | string enum: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY | Yes | | | start_hour | number | Yes | 0-23 | | start_minute | number | | 0, 15, 30, 45 | | end_hour | number | Yes | 0-24 (24 = end of day) | | end_minute | number | | 0, 15, 30, 45 | | bid_modifier | number | | 0.1-10.0 | ### AdsAd_creative_ids ### AdsCampaign_frequency_cap | Field | Type | Required | Description | |---|---|---|---| | is_unlimited | boolean | | | | time_unit | string enum: UNSPECIFIED, LIFETIME, MONTHS, WEEKS, DAYS, HOURS, MINUTES | | | | time_unit_count | number | | | | max_impressions | number | | | | max_views | number | | | ### AdsCampaign_targeting | Field | Type | Required | Description | |---|---|---|---| | geographic | AdsCampaign_targeting_geographic | | | | demographic | AdsCampaign_targeting_demographic | | Demographic targeting (Meta: age_min, age_max, genders) | | audience | AdsCampaign_targeting_audience | | | | placement | AdsCampaign_targeting_placement | | | | device | AdsCampaign_targeting_device | | | | language | TargetRef[] | | | | content | AdsCampaign_targeting_content | | | | brand_safety | AdsCampaign_targeting_brand_safety | | Brand safety (Meta: excluded_publisher_categories, etc.; Google | | schedule | AdSchedule[] | | | | optimization | AdsCampaign_targeting_optimization | | Optimization (Meta: targeting_automation; Google: observation vs targeting mode) | ### AdsCampaign_targeting_audience | Field | Type | Required | Description | |---|---|---|---| | custom_audiences | TargetRef[] | | | | excluded_custom_audiences | TargetRef[] | | | | lookalike_audiences | LookalikeAudience[] | | | | interests | TargetRef[] | | | | excluded_interests | TargetRef[] | | | | behaviors | TargetRef[] | | | | excluded_behaviors | TargetRef[] | | | | combination_spec | AudienceCombination[] | | | ### AdsCampaign_targeting_brand_safety Brand safety (Meta: excluded_publisher_categories, etc.; Google | Field | Type | Required | Description | |---|---|---|---| | excluded_publisher_categories | AdsCampaign_targeting_brand_safety_excluded_publisher_categories | | | | excluded_content_labels | AdsCampaign_targeting_brand_safety_excluded_content_labels | | Google Ads ContentLabelType | | brand_safety_content_filter_levels | AdsCampaign_targeting_brand_safety_brand_safety_content_filter_levels | | | | publisher_visibility_categories | AdsCampaign_targeting_brand_safety_publisher_visibility_categories | | | | block_list_ids | AdsCampaign_targeting_brand_safety_block_list_ids | | | ### AdsCampaign_targeting_brand_safety_block_list_ids ### AdsCampaign_targeting_brand_safety_brand_safety_content_filter_levels ### AdsCampaign_targeting_brand_safety_excluded_content_labels Google Ads ContentLabelType ### AdsCampaign_targeting_brand_safety_excluded_publisher_categories ### AdsCampaign_targeting_brand_safety_publisher_visibility_categories ### AdsCampaign_targeting_content | Field | Type | Required | Description | |---|---|---|---| | keywords | AdsKeyword[] | | | | excluded_keywords | AdsKeyword[] | | | | topics | TargetRef[] | | | | excluded_topics | TargetRef[] | | | | urls | AdsCampaign_targeting_content_urls | | | | excluded_urls | AdsCampaign_targeting_content_excluded_urls | | | | video | AdsCampaign_targeting_content_video | | | ### AdsCampaign_targeting_content_excluded_urls ### AdsCampaign_targeting_content_urls ### AdsCampaign_targeting_content_video | Field | Type | Required | Description | |---|---|---|---| | youtube_videos | AdsCampaign_targeting_content_video_youtube_videos | | | | excluded_youtube_videos | AdsCampaign_targeting_content_video_excluded_youtube_videos | | | | youtube_channels | AdsCampaign_targeting_content_video_youtube_channels | | | | excluded_youtube_channels | AdsCampaign_targeting_content_video_excluded_youtube_channels | | | | positions | AdsCampaign_targeting_content_video_positions | | | | player_sizes | AdsCampaign_targeting_content_video_player_sizes | | | | durations | AdsCampaign_targeting_content_video_durations | | | ### AdsCampaign_targeting_content_video_durations ### AdsCampaign_targeting_content_video_excluded_youtube_channels ### AdsCampaign_targeting_content_video_excluded_youtube_videos ### AdsCampaign_targeting_content_video_player_sizes ### AdsCampaign_targeting_content_video_positions ### AdsCampaign_targeting_content_video_youtube_channels ### AdsCampaign_targeting_content_video_youtube_videos ### AdsCampaign_targeting_demographic Demographic targeting (Meta: age_min, age_max, genders) | Field | Type | Required | Description | |---|---|---|---| | age_min | number | | | | age_max | number | | | | male | boolean | | | | female | boolean | | | ### AdsCampaign_targeting_device | Field | Type | Required | Description | |---|---|---|---| | types | TargetRef[] | | | | user_device | TargetRef[] | | | | user_os | TargetRef[] | | | | carriers | TargetRef[] | | | ### AdsCampaign_targeting_geographic | Field | Type | Required | Description | |---|---|---|---| | countries | TargetRef[] | | | | regions | TargetRef[] | | | | cities | CityTarget[] | | | | postal_codes | TargetRef[] | | | | us_dmas | TargetRef[] | | | | excluded_countries | TargetRef[] | | | | excluded_regions | TargetRef[] | | | | excluded_cities | CityTarget[] | | | | excluded_postal_codes | TargetRef[] | | | | excluded_us_dmas | TargetRef[] | | | | location_types | AdsCampaign_targeting_geographic_location_types | | | | presence_type | string enum: PRESENCE, PRESENCE_OR_INTEREST | | | ### AdsCampaign_targeting_geographic_location_types ### AdsCampaign_targeting_optimization Optimization (Meta: targeting_automation; Google: observation vs targeting mode) | Field | Type | Required | Description | |---|---|---|---| | mode | string enum: TARGETING, OBSERVATION | | Google: bid_only falsetrue | | advantage_audience | boolean | | | | advantage_placements | boolean | | | | targeting_optimization_expansion_all | boolean | | | ### AdsCampaign_targeting_placement | Field | Type | Required | Description | |---|---|---|---| | platforms | AdsCampaign_targeting_placement_platforms | | | | facebook_positions | AdsCampaign_targeting_placement_facebook_positions | | | | instagram_positions | AdsCampaign_targeting_placement_instagram_positions | | | | messenger_positions | AdsCampaign_targeting_placement_messenger_positions | | | | audience_network_positions | AdsCampaign_targeting_placement_audience_network_positions | | | ### AdsCampaign_targeting_placement_audience_network_positions ### AdsCampaign_targeting_placement_facebook_positions ### AdsCampaign_targeting_placement_instagram_positions ### AdsCampaign_targeting_placement_messenger_positions ### AdsCampaign_targeting_placement_platforms ### AdsCreative_asset_urls Hosted asset mode (best-effort, provider-specific ### AdsGroup_bid_strategy YOUTUBE_AND_PARTNERS | Field | Type | Required | Description | |---|---|---|---| | type | string enum: FIXED_BID, MAXIMIZE_SPEND, PERFORMANCE_GOAL, YOUTUBE_AND_PARTNERS | Yes | | | fixed_bid_amount | number | | | | performance_goal_type | string enum: UNSPECIFIED, CPA, CPC, VIEWABLE_CPM, CUSTOM_ALGO, CIVA, IVO_TEN, AV_VIEWED, REACH | | | | performance_goal_amount | number | | | | max_average_cpm_bid_amount | number | | | | custom_bidding_algorithm_id | string | | | | raise_bid_for_deals | boolean | | | | youtube_and_partners_type | string enum: UNSPECIFIED, MANUAL_CPV, MANUAL_CPM, TARGET_CPA, TARGET_CPM, RESERVE_CPM, MAXIMIZE_LIFT, MAXIMIZE_CONVERSIONS, TARGET_CPV, TARGET_ROAS, MAXIMIZE_CONVERSION_VALUE | | | | youtube_and_partners_value | string | | | | target_roas | number | | Target ROAS e.g. 2.5 = 250% (Google) | ### AdsGroup_creative_ids ### AdsGroup_frequency_cap | Field | Type | Required | Description | |---|---|---|---| | is_unlimited | boolean | | | | time_unit | string enum: UNSPECIFIED, LIFETIME, MONTHS, WEEKS, DAYS, HOURS, MINUTES | | | | time_unit_count | number | | | | max_impressions | number | | | | max_views | number | | | ### AdsGroup_pacing | Field | Type | Required | Description | |---|---|---|---| | period | string enum: UNSPECIFIED, DAILY, FLIGHT | | | | type | string | | | | daily_max_amount | number | | | | daily_max_impressions | number | | | ### AdsGroup_targeting | Field | Type | Required | Description | |---|---|---|---| | geographic | AdsGroup_targeting_geographic | | | | demographic | AdsGroup_targeting_demographic | | Demographic targeting (Meta: age_min, age_max, genders) | | audience | AdsGroup_targeting_audience | | | | placement | AdsGroup_targeting_placement | | | | device | AdsGroup_targeting_device | | | | language | TargetRef[] | | | | content | AdsGroup_targeting_content | | | | brand_safety | AdsGroup_targeting_brand_safety | | Brand safety (Meta: excluded_publisher_categories, etc.; Google | | schedule | AdSchedule[] | | | | optimization | AdsGroup_targeting_optimization | | Optimization (Meta: targeting_automation; Google: observation vs targeting mode) | ### AdsGroup_targeting_audience | Field | Type | Required | Description | |---|---|---|---| | custom_audiences | TargetRef[] | | | | excluded_custom_audiences | TargetRef[] | | | | lookalike_audiences | LookalikeAudience[] | | | | interests | TargetRef[] | | | | excluded_interests | TargetRef[] | | | | behaviors | TargetRef[] | | | | excluded_behaviors | TargetRef[] | | | | combination_spec | AudienceCombination[] | | | ### AdsGroup_targeting_brand_safety Brand safety (Meta: excluded_publisher_categories, etc.; Google | Field | Type | Required | Description | |---|---|---|---| | excluded_publisher_categories | AdsGroup_targeting_brand_safety_excluded_publisher_categories | | | | excluded_content_labels | AdsGroup_targeting_brand_safety_excluded_content_labels | | Google Ads ContentLabelType | | brand_safety_content_filter_levels | AdsGroup_targeting_brand_safety_brand_safety_content_filter_levels | | | | publisher_visibility_categories | AdsGroup_targeting_brand_safety_publisher_visibility_categories | | | | block_list_ids | AdsGroup_targeting_brand_safety_block_list_ids | | | ### AdsGroup_targeting_brand_safety_block_list_ids ### AdsGroup_targeting_brand_safety_brand_safety_content_filter_levels ### AdsGroup_targeting_brand_safety_excluded_content_labels Google Ads ContentLabelType ### AdsGroup_targeting_brand_safety_excluded_publisher_categories ### AdsGroup_targeting_brand_safety_publisher_visibility_categories ### AdsGroup_targeting_content | Field | Type | Required | Description | |---|---|---|---| | keywords | AdsKeyword[] | | | | excluded_keywords | AdsKeyword[] | | | | topics | TargetRef[] | | | | excluded_topics | TargetRef[] | | | | urls | AdsGroup_targeting_content_urls | | | | excluded_urls | AdsGroup_targeting_content_excluded_urls | | | | video | AdsGroup_targeting_content_video | | | ### AdsGroup_targeting_content_excluded_urls ### AdsGroup_targeting_content_urls ### AdsGroup_targeting_content_video | Field | Type | Required | Description | |---|---|---|---| | youtube_videos | AdsGroup_targeting_content_video_youtube_videos | | | | excluded_youtube_videos | AdsGroup_targeting_content_video_excluded_youtube_videos | | | | youtube_channels | AdsGroup_targeting_content_video_youtube_channels | | | | excluded_youtube_channels | AdsGroup_targeting_content_video_excluded_youtube_channels | | | | positions | AdsGroup_targeting_content_video_positions | | | | player_sizes | AdsGroup_targeting_content_video_player_sizes | | | | durations | AdsGroup_targeting_content_video_durations | | | ### AdsGroup_targeting_content_video_durations ### AdsGroup_targeting_content_video_excluded_youtube_channels ### AdsGroup_targeting_content_video_excluded_youtube_videos ### AdsGroup_targeting_content_video_player_sizes ### AdsGroup_targeting_content_video_positions ### AdsGroup_targeting_content_video_youtube_channels ### AdsGroup_targeting_content_video_youtube_videos ### AdsGroup_targeting_demographic Demographic targeting (Meta: age_min, age_max, genders) | Field | Type | Required | Description | |---|---|---|---| | age_min | number | | | | age_max | number | | | | male | boolean | | | | female | boolean | | | ### AdsGroup_targeting_device | Field | Type | Required | Description | |---|---|---|---| | types | TargetRef[] | | | | user_device | TargetRef[] | | | | user_os | TargetRef[] | | | | carriers | TargetRef[] | | | ### AdsGroup_targeting_geographic | Field | Type | Required | Description | |---|---|---|---| | countries | TargetRef[] | | | | regions | TargetRef[] | | | | cities | CityTarget[] | | | | postal_codes | TargetRef[] | | | | us_dmas | TargetRef[] | | | | excluded_countries | TargetRef[] | | | | excluded_regions | TargetRef[] | | | | excluded_cities | CityTarget[] | | | | excluded_postal_codes | TargetRef[] | | | | excluded_us_dmas | TargetRef[] | | | | location_types | AdsGroup_targeting_geographic_location_types | | | | presence_type | string enum: PRESENCE, PRESENCE_OR_INTEREST | | | ### AdsGroup_targeting_geographic_location_types ### AdsGroup_targeting_optimization Optimization (Meta: targeting_automation; Google: observation vs targeting mode) | Field | Type | Required | Description | |---|---|---|---| | mode | string enum: TARGETING, OBSERVATION | | Google: bid_only falsetrue | | advantage_audience | boolean | | | | advantage_placements | boolean | | | | targeting_optimization_expansion_all | boolean | | | ### AdsGroup_targeting_placement | Field | Type | Required | Description | |---|---|---|---| | platforms | AdsGroup_targeting_placement_platforms | | | | facebook_positions | AdsGroup_targeting_placement_facebook_positions | | | | instagram_positions | AdsGroup_targeting_placement_instagram_positions | | | | messenger_positions | AdsGroup_targeting_placement_messenger_positions | | | | audience_network_positions | AdsGroup_targeting_placement_audience_network_positions | | | ### AdsGroup_targeting_placement_audience_network_positions ### AdsGroup_targeting_placement_facebook_positions ### AdsGroup_targeting_placement_instagram_positions ### AdsGroup_targeting_placement_messenger_positions ### AdsGroup_targeting_placement_platforms ### AdsInsertionorderBudgetSegment | Field | Type | Required | Description | |---|---|---|---| | budget_amount | number | | | | description | string | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### AdsInsertionorder_bid_strategy YOUTUBE_AND_PARTNERS | Field | Type | Required | Description | |---|---|---|---| | type | string enum: FIXED_BID, MAXIMIZE_SPEND, PERFORMANCE_GOAL, YOUTUBE_AND_PARTNERS | Yes | | | fixed_bid_amount | number | | | | performance_goal_type | string enum: UNSPECIFIED, CPA, CPC, VIEWABLE_CPM, CUSTOM_ALGO, CIVA, IVO_TEN, AV_VIEWED, REACH | | | | performance_goal_amount | number | | | | max_average_cpm_bid_amount | number | | | | custom_bidding_algorithm_id | string | | | | raise_bid_for_deals | boolean | | | | youtube_and_partners_type | string enum: UNSPECIFIED, MANUAL_CPV, MANUAL_CPM, TARGET_CPA, TARGET_CPM, RESERVE_CPM, MAXIMIZE_LIFT, MAXIMIZE_CONVERSIONS, TARGET_CPV, TARGET_ROAS, MAXIMIZE_CONVERSION_VALUE | | | | youtube_and_partners_value | string | | | | target_roas | number | | Target ROAS e.g. 2.5 = 250% (Google) | ### AdsInsertionorder_frequency_cap | Field | Type | Required | Description | |---|---|---|---| | is_unlimited | boolean | | | | time_unit | string enum: UNSPECIFIED, LIFETIME, MONTHS, WEEKS, DAYS, HOURS, MINUTES | | | | time_unit_count | number | | | | max_impressions | number | | | | max_views | number | | | ### AdsInsertionorder_kpi | Field | Type | Required | Description | |---|---|---|---| | type | string enum: UNSPECIFIED, CPM, CPC, CPA, CTR, VIEWABILITY, CPIAVC, CPE, CPV, CLICK_CVR, IMPRESSION_CVR, VCPM, VTR, AUDIO_COMPLETION_RATE, VIDEO_COMPLETION_RATE, CPCL, CPCV, TOS10, MAXIMIZE_PACING, CUSTOM_IMPRESSION_VALUE_OVER_COST, OTHER | | | | amount | number | | | | percentage | number | | | | string_value | string | | | | algorithm_id | string | | | ### AdsInsertionorder_pacing | Field | Type | Required | Description | |---|---|---|---| | period | string enum: UNSPECIFIED, DAILY, FLIGHT | | | | type | string | | | | daily_max_amount | number | | | | daily_max_impressions | number | | | ### AdsKeyword Content targeting (keywords, topics, placements, video) | Field | Type | Required | Description | |---|---|---|---| | text | string | Yes | | | match_type | string enum: BROAD, PHRASE, EXACT | | | ### AudienceCombination | Field | Type | Required | Description | |---|---|---|---| | interests | TargetRef[] | | | | behaviors | TargetRef[] | | | | references | TargetRef[] | | | | life_events | TargetRef[] | | | ### CityTarget | Field | Type | Required | Description | |---|---|---|---| | id | string | Yes | | | name | string | | | | radius | number | | | | radius_unit | string enum: MILES, KILOMETERS | | | ### LookalikeAudience | Field | Type | Required | Description | |---|---|---|---| | id | string | Yes | | | name | string | | | | similarity | number | | | | source_audience_id | string | | | ### TargetRef | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | --- # Analytics API – Endpoints & Data Models ## Analytics API data models URL: https://docs.unified.to/analytics/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /analytics/{connection_id}/property | Create a property | | GET | /analytics/{connection_id}/property | List all properties | | GET | /analytics/{connection_id}/property/{id} | Retrieve a property | | PUT | /analytics/{connection_id}/property/{id} | Update a property | | DELETE | /analytics/{connection_id}/property/{id} | Remove a property | | POST | /analytics/{connection_id}/event | Create an event | | GET | /analytics/{connection_id}/event | List all events | | GET | /analytics/{connection_id}/event/{id} | Retrieve an event | | GET | /analytics/{connection_id}/session/{id} | Retrieve a session | | GET | /analytics/{connection_id}/session | List all sessions | | POST | /analytics/{connection_id}/visitor | Create a visitor | | GET | /analytics/{connection_id}/visitor | List all visitors | | GET | /analytics/{connection_id}/visitor/{id} | Retrieve a visitor | | PUT | /analytics/{connection_id}/visitor/{id} | Update a visitor | | DELETE | /analytics/{connection_id}/visitor/{id} | Remove a visitor | | GET | /analytics/{connection_id}/report | List all reports | #### Data Models ### AnalyticsEvent | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | event_type | string enum: PAGE_VIEW, SCREEN_VIEW, CLICK, FORM_SUBMIT, PURCHASE, SIGN_UP, LOGIN, LOGOUT, SEARCH, VIDEO_PLAY, VIDEO_COMPLETE, FILE_DOWNLOAD, SCROLL, SESSION_START, FIRST_VISIT, CUSTOM | | | | visitor_id | string | | points to AnalyticsVisitor | | property_id | string | | points to AnalyticsProperty | | session_id | string | | points to AnalyticsSession | | page_url | string | | | | page_title | string | | | | page_referrer | string | | | | device | string | | | | browser | string | | | | os | string | | | | country | string | | | | country_code | string | | | | region | string | | | | city | string | | | | metadata | AnalyticsEvent_metadata | | | | value | number | | | | currency | string | | | | utm_source | string | | | | utm_medium | string | | | | utm_campaign | string | | | | utm_term | string | | | | utm_content | string | | | | raw | any | | | ### AnalyticsProperty | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | timezone | string | | | | currency | string | | | | industry | string | | | | parent_id | string | | points to AnalyticsProperty | | raw | any | | | ### AnalyticsSession | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | property_id | string | | points to AnalyticsProperty | | visitor_id | string | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | duration_seconds | number | | | | landing_page | string | | Session attributes | | exit_page | string | | Session attributes | | page_views | number | | Session attributes | | events_count | number | | Session attributes | | source | string | | Acquisition | | medium | string | | | | campaign | string | | | | device | string | | | | browser | string | | | | os | string | | | | country | string | | | | city | string | | | | is_bounce | boolean | | Engagement | | is_conversion | boolean | | Engagement | | raw | any | | | ### AnalyticsVisitor | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | property_id | string | | points to AnalyticsProperty | | email | string | | | | name | string | | | | age | number | | | | gender | string enum: MALE, FEMALE, INTERSEX, TRANS, NON_BINARY | | | | country | string | | | | country_code | string | | | | city | string | | | | language | string | | | | timezone | string | | | | first_seen_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | last_seen_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_sessions | number | | | | total_page_views | number | | | | total_events | number | | | | metadata | AnalyticsVisitor_metadata | | | | raw | any | | | ### AnalyticsEvent_metadata ### AnalyticsVisitor_metadata --- # Assessment API – Endpoints & Data Models ## Assessment API data models URL: https://docs.unified.to/assessment/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /assessment/{connection_id}/package | Create an assessment package | | GET | /assessment/{connection_id}/package | List assessment packages | | GET | /assessment/{connection_id}/package/{id} | Get an assessment package | | PUT | /assessment/{connection_id}/package/{id} | Update an assessment package | | DELETE | /assessment/{connection_id}/package/{id} | Delete an assessment package | | PUT | /assessment/{connection_id}/order/{id} | Update an order | #### Data Models ### AssessmentOrder | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | workspace_id | string | Yes | Workspace ID that this order belongs to (reference to KmsSpace) | | connection_id | string | Yes | Connection ID that this order belongs to | | webhook_id | string | | Webhook ID that this order belongs to | | package_id | string | | Assessment package ID | | parameters | AssessmentParameterInput[] | | Filled-in answers to the package's parameter questions | | target_url | string | | URL to redirect user to after assessment | | status | string enum: OPEN, IN_PROGRESS, COMPLETED, FAILED, REJECTED | | | | reference | string | | ATS-specific reference ID | | application_id | string | | ATS application ID | | job_id | string | | ATS job ID | | company_id | string | | ATS company ID (reference to AtsCompany) | | candidate_id | string | | ATS candidate ID | | employee_id | string | | ATS employee ID | | profile_name | string | | | | profile_first_name | string | | | | profile_last_name | string | | | | profile_resume_url | string | | | | profile_ip_address | string | | XXX.XXX.XXX.XXX | | profile_date_of_birth | string | | YYYY-MM-DD | | profile_addresses | AssessmentAddress[] | | | | profile_gender | string enum: MALE, FEMALE, INTERSEX, TRANS, NON_BINARY | | | | profile_emails | AssessmentOrder_profile_emails | | | | profile_telephones | AssessmentOrder_profile_telephones | | | | profile_national_identifier | string | | | | profile_social_media_urls | AssessmentOrder_profile_social_media_urls | | | | response_completed_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | response_expires_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | response_issued_at | string (date) | | Datetime that assessment was issued (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | response_status | string enum: OPEN, IN_PROGRESS, COMPLETED, FAILED, REJECTED | | | | response_score | number | | Assessment score (e.g., 0-100) | | response_max_score | number | | Maximum possible score | | response_url | string | | URL to view detailed results | | response_redirect_url | string | | URL to redirect the user to complete assessment | | response_download_urls | AssessmentOrder_response_download_urls | | Report download URLs | | response_details | AssessmentResponseDetail[] | | | | response_source | string | | | | response_attributes | AssessmentAttribute[] | | Additional result attributes | | raw | any | | | ### AssessmentPackage Used by assessment providers to SUBMIT packages to ATS systems | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for the assessment package | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | workspace_id | string | | Workspace ID that this package belongs to (reference to KmsSpace) (read-only) | | connection_id | string | | Connection ID that this package belongs to (read-only) | | integration_types | AssessmentPackage_integration_types | | Integration types that support this package | | name | string | | Name of the assessment package | | type | string enum: SKILLS_TEST, BEHAVIORAL_ASSESSMENT, VIDEO_INTERVIEW, BACKGROUND_CHECK, REFERENCE_CHECK, OTHER | Yes | | | aliases | AssessmentPackage_aliases | | Alternative namesidentifiers for this package | | tags | AssessmentPackage_tags | | Category tags (e.g., "Assessment", "Background Check") | | description | string | | Detailed description | | parameters | AssessmentParameter[] | | Questionsinputs needed for this assessment (aligned with verification) | | has_redirect_url | boolean | | Whether provider redirects user to complete assessment | | has_target_url | boolean | | Where provider redirects user after completion | | needs_ip_address | boolean | | Whether IP address is required | | max_score | number | | Maximum possible score if this assessment returns a score | | info_url | string | | URL to find additional information about this package | | regions | AssessmentPackageRegion[] | | | | raw | any | | | ### AssessmentAddress | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area (e.g., state in the U.S., province in Canada) | | region_code | string | | Short form for regional area (e.g., two-letter stateprovince abbreviation) | | postal_code | string | | | | country | string | | | | country_code | string | | Country code in ISO 3166 A-2 format | ### AssessmentAttribute | Field | Type | Required | Description | |---|---|---|---| | type | string enum: TEXT, NUMBER, SUB_RESULT | Yes | | | label | string | Yes | | | value | string | | For TEXT and NUMBER types | | reference | string | | For SUB_RESULT type | | score_value | number | | | | score_max | number | | | | status | string enum: OPEN, IN_PROGRESS, COMPLETED, FAILED, REJECTED | | For SUB_RESULT type | ### AssessmentOrder_profile_emails ### AssessmentOrder_profile_social_media_urls ### AssessmentOrder_profile_telephones ### AssessmentOrder_response_download_urls Report download URLs ### AssessmentPackageRegion | Field | Type | Required | Description | |---|---|---|---| | regions | AssessmentPackageRegion_regions | | Countryregion codes where this package is available ({country}-{state} or {country}) | | cost_amount | number | | | | currency | string | | ISO 4217 format | | processing_time | number | | Processing time in milliseconds | ### AssessmentPackageRegion_regions Countryregion codes where this package is available ({country}-{state} or {country}) ### AssessmentPackage_aliases Alternative namesidentifiers for this package ### AssessmentPackage_integration_types Integration types that support this package ### AssessmentPackage_tags Category tags (e.g., "Assessment", "Background Check") ### AssessmentParameter | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | | public_question | string | | Question to ask the candidateuser | | type | string enum: TEXT, NUMBER, MULTIPLE_CHOICE, MULTIPLE_SELECT, DATE, FILE | | | | options | AssessmentParameter_options | | Options for MULTIPLE_CHOICE and MULTIPLE_SELECT | | file_types | AssessmentParameter_file_types | | Valid file MIME types for FILE type | | valid_regions | AssessmentParameter_valid_regions | | Regions where this parameter is valid ({country}-{state} or {country}) | | is_required | boolean | | | ### AssessmentParameterInput | Field | Type | Required | Description | |---|---|---|---| | parameter_id | string | | | | name | string | | Name of parameter | | inputs | AssessmentParameterInput_inputs | | | ### AssessmentParameterInput_inputs ### AssessmentParameter_file_types Valid file MIME types for FILE type ### AssessmentParameter_options Options for MULTIPLE_CHOICE and MULTIPLE_SELECT ### AssessmentParameter_valid_regions Regions where this parameter is valid ({country}-{state} or {country}) ### AssessmentResponseDetail | Field | Type | Required | Description | |---|---|---|---| | title | string | | | | text | string | | | | is_private | boolean | | | | is_failed_reason | boolean | | | | parameter_id | string | | In reference to the parameter input | | download_url | string | | | --- # Ats API – Endpoints & Data Models ## Ats API data models URL: https://docs.unified.to/ats/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /ats/{connection_id}/activity | Create an activity | | GET | /ats/{connection_id}/activity | List all activities | | GET | /ats/{connection_id}/activity/{id} | Retrieve an activity | | PUT | /ats/{connection_id}/activity/{id} | Update an activity | | DELETE | /ats/{connection_id}/activity/{id} | Remove an activity | | POST | /ats/{connection_id}/candidate | Create a candidate | | GET | /ats/{connection_id}/candidate | List all candidates | | GET | /ats/{connection_id}/candidate/{id} | Retrieve a candidate | | PUT | /ats/{connection_id}/candidate/{id} | Update a candidate | | DELETE | /ats/{connection_id}/candidate/{id} | Remove a candidate | | POST | /ats/{connection_id}/job | Create a job | | GET | /ats/{connection_id}/job | List all jobs | | GET | /ats/{connection_id}/job/{id} | Retrieve a job | | PUT | /ats/{connection_id}/job/{id} | Update a job | | DELETE | /ats/{connection_id}/job/{id} | Remove a job | | POST | /ats/{connection_id}/interview | Create an interview | | GET | /ats/{connection_id}/interview | List all interviews | | GET | /ats/{connection_id}/interview/{id} | Retrieve an interview | | PUT | /ats/{connection_id}/interview/{id} | Update an interview | | DELETE | /ats/{connection_id}/interview/{id} | Remove an interview | | POST | /ats/{connection_id}/document | Create a document | | GET | /ats/{connection_id}/document | List all documents | | GET | /ats/{connection_id}/document/{id} | Retrieve a document | | PUT | /ats/{connection_id}/document/{id} | Update a document | | DELETE | /ats/{connection_id}/document/{id} | Remove a document | | GET | /ats/{connection_id}/applicationstatus | List all applicationstatuses | | POST | /ats/{connection_id}/application | Create an application | | GET | /ats/{connection_id}/application | List all applications | | GET | /ats/{connection_id}/application/{id} | Retrieve an application | | PUT | /ats/{connection_id}/application/{id} | Update an application | | DELETE | /ats/{connection_id}/application/{id} | Remove an application | | POST | /ats/{connection_id}/scorecard | Create a scorecard | | GET | /ats/{connection_id}/scorecard | List all scorecards | | GET | /ats/{connection_id}/scorecard/{id} | Retrieve a scorecard | | PUT | /ats/{connection_id}/scorecard/{id} | Update a scorecard | | DELETE | /ats/{connection_id}/scorecard/{id} | Remove a scorecard | | POST | /ats/{connection_id}/company | Create a company | | GET | /ats/{connection_id}/company | List all companies | | GET | /ats/{connection_id}/company/{id} | Retrieve a company | | PUT | /ats/{connection_id}/company/{id} | Update a company | | DELETE | /ats/{connection_id}/company/{id} | Remove a company | #### Data Models ### AtsActivity | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | candidate_id | string | | | | application_id | string | | | | job_id | string | | | | interview_id | string | | | | document_ids | AtsActivity_document_ids | | IDs for AtsDocument.get | | title | string | | | | description | string | | | | is_private | boolean | | | | user_ids | AtsActivity_user_ids | | id values of the recruiters associated with the activity. (reference to HrisEmployee) | | type | string enum: NOTE, TASK, EMAIL | | | | from | AtsActivity_from | | | | to | AtsEmail[] | | | | cc | AtsEmail[] | | | | bcc | AtsEmail[] | | | | sub_type | string | | | | company_id | string | | (reference to AtsCompany) | | metadata | AtsMetadata[] | | | | raw | any | | | ### AtsApplication | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | candidate_id | string | | | | job_id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | applied_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | hired_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | rejected_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | rejected_reason | string | | | | source | string | | | | status | string enum: NEW, REVIEWING, SCREENING, SUBMITTED, FIRST_INTERVIEW, SECOND_INTERVIEW, THIRD_INTERVIEW, BACKGROUND_CHECK, OFFERED, ACCEPTED, HIRED, REJECTED, DECLINED, WITHDRAWN | | | | original_status | string | | The integration's original status. Mostly used to have your customers chose the application status/stage so that your software can trigger an event. | | answers | AtsApplicationAnswer[] | | | | offers | AtsOffer[] | | | | user_id | string | | HR user/employee ID (reference to HrisEmployee) | | metadata | AtsMetadata[] | | | | original_substatus | string | | The provider's secondary/sub status, when distinct from original_status (e.g. UKG Ready hiring_stage) | | raw | any | | | ### AtsCandidate | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | first_name | string | | | | last_name | string | | | | emails | AtsEmail[] | | | | title | string | | Candidate’s current job title | | telephones | AtsTelephone[] | | | | company_name | string | | Name of company where candidate currently works | | image_url | string | | | | tags | AtsCandidate_tags | | | | address | AtsCandidate_address | | | | external_identifier | string | | | | link_urls | AtsCandidate_link_urls | | URLs for web pages containing additional material about the candidate (LinkedIn, other social media, articles, etc.) | | origin | string enum: AGENCY, APPLIED, INTERNAL, REFERRED, SOURCED, UNIVERSITY | | | | company_id | string | | (reference to AtsCompany) | | sources | AtsCandidate_sources | | | | date_of_birth | string (date) | | | | user_id | string | | Employee ID that owns the relationship for this candidate (reference to HrisEmployee) | | user_ids | AtsCandidate_user_ids | | references hris employees (reference to HrisEmployee) | | web_url | string | | | | experiences | AtsCandidateExperience[] | | | | education | AtsCandidateEducation[] | | | | skills | AtsCandidate_skills | | | | job_ids | AtsCandidate_job_ids | | | | metadata | AtsMetadata[] | | | | raw | any | | | ### AtsCompany | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | address | AtsCompany_address | | | | website_url | string | | | | phone | string | | | | parent_id | string | | | | recruiter_ids | AtsCompany_recruiter_ids | | | | metadata | AtsMetadata[] | | | | raw | any | | | ### AtsDocument | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | document_url | string | | This link expires after 1 hour. | | document_data | string | | base64 encoded file contents used for create/update actions. Use this field to send an attachment e.g. a resume, a profile, or other text content | | filename | string | | | | type | string enum: RESUME, COVER_LETTER, OFFER_PACKET, OFFER_LETTER, TAKE_HOME_TEST, OTHER | | | | candidate_id | string | | | | application_id | string | | | | job_id | string | | | | user_id | string | | (reference to HrisEmployee) | | raw | any | | | ### AtsInterview | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | candidate_id | string | | | | job_id | string | | | | application_id | string | | | | user_ids | AtsInterview_user_ids | | (reference to HrisEmployee) | | status | string enum: SCHEDULED, AWAITING_FEEDBACK, COMPLETE, CANCELED, NEEDS_SCHEDULING | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | location | string | | | | external_event_xref | string | | the ID of the event in a calendar | | raw | any | | | ### AtsJob | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | recruiter_ids | AtsJob_recruiter_ids | | | | hiring_manager_ids | AtsJob_hiring_manager_ids | | | | hiring_managers | AtsReference[] | | | | status | string enum: ARCHIVED, PENDING, DRAFT, OPEN, CLOSED | | | | closed_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | addresses | AtsAddress[] | | | | compensation | AtsCompensation[] | | | | employment_type | string enum: FULL_TIME, PART_TIME, CONTRACTOR, INTERN, CONSULTANT, VOLUNTEER, CASUAL, SEASONAL, FREELANCE, OTHER | | | | remote | boolean | | | | language_locale | string | | Preferred language for the job, in ISO 639-1 format (e.g., U.S. English is en-us) | | public_job_urls | AtsJob_public_job_urls | | URLs for pages containing public listings for the job | | number_of_openings | number | | Number of openings for the job | | company_id | string | | id value of the company associated with the job in the ATS (reference to AtsCompany) | | questions | AtsJobQuestion[] | | | | postings | AtsJobPosting[] | | Public job postings | | groups | AtsGroup[] | | The departments/divisions/teams that this job belongs to | | openings | AtsJobOpening[] | | | | minimum_experience_years | number | | | | minimum_degree | string | | | | skills | AtsJob_skills | | | | metadata | AtsMetadata[] | | | | user_id | string | | User id of the job owner in the ATS (references an HRIS employee) (reference to HrisEmployee) | | raw | any | | | ### AtsScorecard | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | application_id | string | | | | interviewer_id | string | | | | interview_id | string | | | | candidate_id | string | | | | job_id | string | | | | recommendation | string enum: DEFINITELY_NO, NO, YES, STRONG_YES | | | | comment | string | | | | questions | AtsScorecardQuestion[] | | | | raw | any | | | ### AtsActivity_document_ids IDs for AtsDocument.get ### AtsActivity_from | Field | Type | Required | Description | |---|---|---|---| | name | string | | | | email | string | Yes | | | type | string enum: WORK, HOME, OTHER | | | ### AtsActivity_user_ids id values of the recruiters associated with the activity. ### AtsAddress | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AtsApplicationAnswer | Field | Type | Required | Description | |---|---|---|---| | question_id | string | Yes | | | question | string | | | | answers | AtsApplicationAnswer_answers | Yes | | ### AtsApplicationAnswer_answers ### AtsCandidateEducation | Field | Type | Required | Description | |---|---|---|---| | institution | string | | | | level | string | | | | degree | string | | | | field_of_study | string | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### AtsCandidateExperience | Field | Type | Required | Description | |---|---|---|---| | company_name | string | | | | title | string | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### AtsCandidate_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AtsCandidate_job_ids ### AtsCandidate_link_urls URLs for web pages containing additional material about the candidate (LinkedIn, other social media, articles, etc.) ### AtsCandidate_skills ### AtsCandidate_sources ### AtsCandidate_tags ### AtsCandidate_user_ids references hris employees ### AtsCompany_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AtsCompany_recruiter_ids ### AtsCompensation | Field | Type | Required | Description | |---|---|---|---| | type | string enum: SALARY, BONUS, STOCK_OPTIONS, EQUITY, OTHER | | | | min | number | | | | max | number | | | | currency | string | | | | frequency | string enum: ONE_TIME, DAY, QUARTER, YEAR, HOUR, MONTH, WEEK | | | ### AtsEmail | Field | Type | Required | Description | |---|---|---|---| | name | string | | | | email | string | Yes | | | type | string enum: WORK, HOME, OTHER | | | ### AtsGroup | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | | type | string enum: TEAM, GROUP, DEPARTMENT, DIVISION, BUSINESS_UNIT, BRANCH, SUB_DEPARTMENT | | | ### AtsInterview_user_ids ### AtsJobOpening | Field | Type | Required | Description | |---|---|---|---| | status | string enum: OPEN, CLOSED | | | | opened_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | closed_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | application_id | string | | points to an application | | close_reason | string | | | ### AtsJobPosting | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | posting_url | string | | | | name | string | | job-post-specific name | | description | string | | job-post-specific description | | address | AtsJobPosting_address | | job-post-specific address | | location | string | | job-post-specific location | | is_active | boolean | | | ### AtsJobPosting_address job-post-specific address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### AtsJobQuestion | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | question | string | Yes | | | required | boolean | | | | description | string | | | | prompt | string | | | | type | string enum: TEXT, NUMBER, DATE, BOOLEAN, MULTIPLE_CHOICE, FILE, TEXTAREA, MULTIPLE_SELECT, UNIVERSITY, YES_NO, CURRENCY, URL | Yes | | | options | AtsJobQuestion_options | | | ### AtsJobQuestion_options ### AtsJob_hiring_manager_ids ### AtsJob_public_job_urls URLs for pages containing public listings for the job ### AtsJob_recruiter_ids ### AtsJob_skills ### AtsMetadata | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | slug | string | | Actual textual value of the slug | | value | any | | | | namespace | string | | | | format | string enum: TEXT, NUMBER, DATE, BOOLEAN, FILE, TEXTAREA, SINGLE_SELECT, MULTIPLE_SELECT, MEASUREMENT, PRICE, YES_NO, CURRENCY, URL | | | | extra_data | any | | | ### AtsOffer | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | creator_user_id | string | | user id of the recruiter who created the offer (reference to HrisEmployee) | | employee_user_id | string | | newly hired employee id (reference to HrisEmployee) | | sent_at | string (date) | | date the offer was sent (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | accepted_at | string (date) | | date the offer was accepted (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | rejected_at | string (date) | | date the offer was rejected (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | start_at | string (date) | | date the employee starts (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | compensation | AtsCompensation[] | | compensation details for the offer | | status | string enum: CREATED, SENT, ACCEPTED, REJECTED | | | ### AtsReference | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | ### AtsScorecardQuestion | Field | Type | Required | Description | |---|---|---|---| | text | string | Yes | The question to ask the interviewer | | description | string | | | | answer | string | | Candidate’s answer to the question | ### AtsTelephone | Field | Type | Required | Description | |---|---|---|---| | telephone | string | Yes | | | type | string enum: WORK, HOME, OTHER, FAX, MOBILE | | | --- # Auth API – Endpoints & Data Models ## Auth API data models URL: https://docs.unified.to/auth/overview #### Endpoints | Method | Path | Description | |---|---|---| | GET | /unified/integration/login/{workspace_id}/{integration_type} | Sign in a user | | GET | /unified/integration/saml/{workspace_id}/{integration_type} | Sign in a user via SAML | #### Data Models --- # Calendar API – Endpoints & Data Models ## Calendar API data models URL: https://docs.unified.to/calendar/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /calendar/{connection_id}/calendar | Create a calendar | | GET | /calendar/{connection_id}/calendar | List all calendars | | GET | /calendar/{connection_id}/calendar/{id} | Retrieve a calendar | | PUT | /calendar/{connection_id}/calendar/{id} | Update a calendar | | DELETE | /calendar/{connection_id}/calendar/{id} | Remove a calendar | | POST | /calendar/{connection_id}/event | Create an event | | GET | /calendar/{connection_id}/event | List all events | | GET | /calendar/{connection_id}/event/{id} | Retrieve an event | | PUT | /calendar/{connection_id}/event/{id} | Update an event | | DELETE | /calendar/{connection_id}/event/{id} | Remove an event | | GET | /calendar/{connection_id}/busy | List all busies | | POST | /calendar/{connection_id}/link | Create a link | | GET | /calendar/{connection_id}/link | List all links | | GET | /calendar/{connection_id}/link/{id} | Retrieve a link | | PUT | /calendar/{connection_id}/link/{id} | Update a link | | DELETE | /calendar/{connection_id}/link/{id} | Remove a link | | GET | /calendar/{connection_id}/recording/{id} | Retrieve a recording | | GET | /calendar/{connection_id}/recording | List all recordings | | POST | /calendar/{connection_id}/webinar | Create a webinar | | GET | /calendar/{connection_id}/webinar | List all webinars | | GET | /calendar/{connection_id}/webinar/{id} | Retrieve a webinar | | PUT | /calendar/{connection_id}/webinar/{id} | Update a webinar | | DELETE | /calendar/{connection_id}/webinar/{id} | Remove a webinar | #### Data Models ### CalendarCalendar | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | timezone | string | | | | is_primary | boolean | | | | raw | any | | | ### CalendarEvent | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | calendar_id | string | | (reference to CalendarCalendar) | | subject | string | | | | start_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | is_all_day | boolean | | | | timezone | string | | | | notes | string | | | | location | string | | | | is_free | boolean | | | | is_private | boolean | | | | status | string enum: CANCELED, CONFIRMED, TENTATIVE | | | | organizer | CalendarEvent_organizer | | | | attendees | CalendarAttendee[] | | | | recurring_event_id | string | | (reference to CalendarEvent) | | recurrence | CalendarEventRecurrence[] | | | | web_url | string | | | | has_conference | boolean | | | | conference | CalendarConference[] | | | | attachments | CalendarAttachment[] | | | | send_notifications | boolean | | | | raw | any | | | ### CalendarLink | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | url | string | Yes | | | duration | number | | minutes | | description | string | | | | is_active | boolean | | | | price_amount | number | | | | price_currency | string | | | | raw | any | | | ### CalendarRecording | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | expires_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | event_id | string | | (reference to CalendarEvent) | | web_url | string | | | | media | CalendarRecordingMedia[] | | | | raw | any | | | ### CalendarWebinar | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | calendar_id | string | | (reference to CalendarCalendar) | | subject | string | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | timezone | string | | | | notes | string | | | | status | string enum: CANCELED, CONFIRMED, TENTATIVE | | | | organizer | CalendarWebinar_organizer | | | | join_url | string | | | | web_url | string | | information | | panelists | CalendarWebinarPanelist[] | | | | registrants | CalendarWebinarRegistrant[] | | | | panelist_password | string | | | | registrant_password | string | | | | conference | CalendarConference[] | | | | recurrence | CalendarEventRecurrence[] | | | | capacity | number | | | | is_webcast | boolean | | | | is_enabled | boolean | | | | is_auto_approve | boolean | | | | require_first_name | boolean | | | | require_last_name | boolean | | | | require_email | boolean | | | | require_company | boolean | | | | require_job_title | boolean | | | | require_address | boolean | | | | require_phone | boolean | | | | has_qa | boolean | | | | has_polls | boolean | | | | has_recording | boolean | | | | raw | any | | | ### CalendarAttachment | Field | Type | Required | Description | |---|---|---|---| | id | string | | ID for the storage_fileget endpoint | | name | string | | | | mime_type | string | | | | download_url | string | | | ### CalendarAttendee | Field | Type | Required | Description | |---|---|---|---| | user_id | string | | (reference to HrisEmployee) | | email | string | | | | name | string | | | | status | string enum: ACCEPTED, REJECTED, TENTATIVE | | | | required | boolean | | | | is_cohost | boolean | | | ### CalendarConference | Field | Type | Required | Description | |---|---|---|---| | conference_identifier | string | | | | url | string | | | | label | string | | | | telephone | string | | | | participant_access_code | string | | | | host_access_code | string | | | | notes | string | | | | country_code | string | | ISO 2-digit country code | | region_code | string | | ISO 2-digit region code | ### CalendarEventRecurrence | Field | Type | Required | Description | |---|---|---|---| | frequency | string enum: DAILY, WEEKLY, MONTHLY, YEARLY | | | | interval | number | | how many "units" between occurrences, defaults to 1 | | count | number | | how many occurrences, defaults to undefined (no limit) | | end_at | string (date) | | until date, defaults to undefined (no end date) (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | on_days | CalendarEventRecurrence_on_days | | days of the week to repeat on, defaults to undefined (every day), only used if frequency is WEEKLY | | on_months | CalendarEventRecurrence_on_months | | months of the year to repeat on, defaults to undefined (every month), only used if frequency is YEARLY, January is 1 | | on_month_days | CalendarEventRecurrence_on_month_days | | days of the month to repeat on, defaults to undefined (every day), only used if frequency is MONTHLY | | on_weeks | CalendarEventRecurrence_on_weeks | | week ordinals for BYDAY (e.g., -1 for last, -2 for second-to-last, 1 for first, 2 for second), only used with on_days. 0 is used for days without week ordinals. | | on_year_days | CalendarEventRecurrence_on_year_days | | days of the year to repeat on, defaults to undefined (every day), only used if frequency is YEARLY | | week_start | string enum: SU, MO, TU, WE, TH, FR, SA | | week start day, defaults to undefined (no week start day) | | excluded_dates | CalendarEventRecurrence_excluded_dates | | dates to exclude from the recurrence, defaults to undefined (no exclusions) | | included_dates | CalendarEventRecurrence_included_dates | | dates to include in the recurrence, defaults to undefined (no inclusions) | | timezone | string | | timezone, defaults to undefined (no timezone) | ### CalendarEventRecurrence_on_days days of the week to repeat on, defaults to undefined (every day), only used if frequency is WEEKLY ### CalendarEvent_organizer | Field | Type | Required | Description | |---|---|---|---| | user_id | string | | (reference to HrisEmployee) | | email | string | | | | name | string | | | | status | string enum: ACCEPTED, REJECTED, TENTATIVE | | | | required | boolean | | | | is_cohost | boolean | | | ### CalendarRecordingMedia | Field | Type | Required | Description | |---|---|---|---| | attendees | CalendarAttendee[] | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | language | string | | two digit ISO code | | transcripts | CalendarRecordingTranscript[] | | | | summary | string | | provider AI-generated meeting summary | | transcript_download_url | string | | | | summary_download_url | string | | download/export link for a provider AI summary document (e.g. Google Meet smart notes) | | recording_download_url | string | | | ### CalendarRecordingTranscript | Field | Type | Required | Description | |---|---|---|---| | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | text | string | Yes | | | attendee | CalendarRecordingTranscript_attendee | | | | language | string | | two digit ISO code | ### CalendarRecordingTranscript_attendee | Field | Type | Required | Description | |---|---|---|---| | user_id | string | | (reference to HrisEmployee) | | email | string | | | | name | string | | | | status | string enum: ACCEPTED, REJECTED, TENTATIVE | | | | required | boolean | | | | is_cohost | boolean | | | ### CalendarWebinarPanelist | Field | Type | Required | Description | |---|---|---|---| | email | string | | | | name | string | | | | is_required | boolean | | | | join_url | string | | | | join_password | string | | | | status | string enum: ACCEPTED, REJECTED, TENTATIVE | | | ### CalendarWebinarRegistrant | Field | Type | Required | Description | |---|---|---|---| | email | string | | | | name | string | | | | registration_reference | string | | | | registration_status | string enum: PENDING, APPROVED, REJECTED, CANCELLED | | | | registered_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### CalendarWebinar_organizer | Field | Type | Required | Description | |---|---|---|---| | user_id | string | | (reference to HrisEmployee) | | email | string | | | | name | string | | | | status | string enum: ACCEPTED, REJECTED, TENTATIVE | | | | required | boolean | | | | is_cohost | boolean | | | --- # Cdp API – Endpoints & Data Models ## Cdp API data models URL: https://docs.unified.to/cdp/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /cdp/{connection_id}/profile | Create a profile | | GET | /cdp/{connection_id}/profile | List all profiles | | GET | /cdp/{connection_id}/profile/{id} | Retrieve a profile | | PUT | /cdp/{connection_id}/profile/{id} | Update a profile | | DELETE | /cdp/{connection_id}/profile/{id} | Remove a profile | | POST | /cdp/{connection_id}/segment | Create a segment | | GET | /cdp/{connection_id}/segment | List all segments | | GET | /cdp/{connection_id}/segment/{id} | Retrieve a segment | | PUT | /cdp/{connection_id}/segment/{id} | Update a segment | | DELETE | /cdp/{connection_id}/segment/{id} | Remove a segment | | POST | /cdp/{connection_id}/event | Create an event | | GET | /cdp/{connection_id}/event | List all events | | GET | /cdp/{connection_id}/event/{id} | Retrieve an event | | PUT | /cdp/{connection_id}/event/{id} | Update an event | | DELETE | /cdp/{connection_id}/event/{id} | Remove an event | | POST | /cdp/{connection_id}/source | Create a source | | GET | /cdp/{connection_id}/source | List all sources | | GET | /cdp/{connection_id}/source/{id} | Retrieve a source | | PUT | /cdp/{connection_id}/source/{id} | Update a source | | DELETE | /cdp/{connection_id}/source/{id} | Remove a source | | POST | /cdp/{connection_id}/destination | Create a destination | | GET | /cdp/{connection_id}/destination | List all destinations | | GET | /cdp/{connection_id}/destination/{id} | Retrieve a destination | | PUT | /cdp/{connection_id}/destination/{id} | Update a destination | | DELETE | /cdp/{connection_id}/destination/{id} | Remove a destination | | POST | /cdp/{connection_id}/activation | Create an activation | | GET | /cdp/{connection_id}/activation | List all activations | | GET | /cdp/{connection_id}/activation/{id} | Retrieve an activation | | PUT | /cdp/{connection_id}/activation/{id} | Update an activation | | DELETE | /cdp/{connection_id}/activation/{id} | Remove an activation | #### Data Models ### CdpActivation | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this activation object | | created_at | string (date) | | The date that this activation object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this activation object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the activation/sync job | | segment_id | string | | The cdp_segment being activated; points to CdpSegment | | destination_id | string | | The cdp_destination the data is sent to; points to CdpDestination | | source_id | string | | The cdp_source the data originates from; points to CdpSource | | schedule | string | | The activation schedule or mode (eg. cron, interval, realtime, batch) | | status | string enum: ACTIVE, PAUSED, ERROR, PENDING | | The current status of the activation | | last_run_at | string (date) | | When the activation last ran (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | is_enabled | boolean | | Whether the activation is active/enabled | | raw | any | | The raw data returned by the integration for this activation | ### CdpDestination | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this destination object | | created_at | string (date) | | The date that this destination object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this destination object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the outbound activation destination | | slug | string | | A stable handle/key for the destination | | type | string | | The connector/destination type (eg. the provider's connector slug or plugin id) | | direction | string enum: SOURCE, DESTINATION, BIDIRECTIONAL | | DESTINATION, or BIDIRECTIONAL for import+export connectors | | is_enabled | boolean | | Whether the destination is active/enabled | | raw | any | | The raw data returned by the integration for this destination | ### CdpEvent | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this event object | | created_at | string (date) | | The date that this event object was ingested (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The specific event name (eg. "Product Purchased") | | type | string enum: TRACK, PAGE, SCREEN, IDENTIFY, GROUP, ALIAS | | The unified structural category of the event | | occurred_at | string (date) | | When the event actually happened (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | profile_id | string | | The profile this event is associated with; points to CdpProfile | | identifiers | CdpIdentifier[] | | The identifiers that link this event to a profile | | metadata | CdpMetadata[] | | The event payload/properties | | source | string | | The source that produced this event | | source_id | string | | The cdp_source this event originated from; points to CdpSource | | raw | any | | The raw data returned by the integration for this event | ### CdpProfile | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this profile object | | created_at | string (date) | | The date that this profile object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this profile object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The full name of the profile | | first_name | string | | The first name of the profile | | last_name | string | | The last name of the profile | | email | string | | The primary email address of the profile | | telephone | string | | The primary telephone number of the profile | | gender | string enum: MALE, FEMALE, INTERSEX, TRANS, NON_BINARY | | The gender of the profile | | birthdate | string | | The birth date of the profile | | title | string | | The job title of the profile | | company | string | | The company/organization name of the profile | | address | CdpProfile_address | | The address of the profile | | identifiers | CdpIdentifier[] | | The typed identifiers stitched to this profile | | metadata | CdpMetadata[] | | The traits/attributes of the profile | | segments | CdpProfileSegment[] | | The segments/audiences this profile currently belongs to | | is_anonymous | boolean | | Whether the profile is anonymous (no known identity) | | consent | CdpConsent[] | | The consent/privacy preferences of the profile | | source_id | string | | The cdp_source this profile originated from; points to CdpSource | | raw | any | | The raw data returned by the integration for this profile | ### CdpSegment | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this segment object | | created_at | string (date) | | The date that this segment object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this segment object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the segment/audience | | slug | string | | The data-plane handle/key used to identify segment membership in profiles and destinations | | description | string | | The description of the segment/audience | | definition | string | | The membership rule/query that defines the segment | | type | string enum: USERS, ACCOUNTS, LINKED | | The type of entity the segment targets (USERS, ACCOUNTS, LINKED) | | compute_mode | string enum: REALTIME, BATCH | | How the segment membership is computed (REALTIME or BATCH) | | size | number | | The number of profiles currently in the segment | | is_active | boolean | | Whether the segment is active/enabled | | source_id | string | | The cdp_source this segment originated from; points to CdpSource | | raw | any | | The raw data returned by the integration for this segment | ### CdpSource | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this source object | | created_at | string (date) | | The date that this source object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this source object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the inbound data source | | slug | string | | A stable handle/key for the source | | type | string | | The connector/source type (eg. the provider's connector slug or plugin id) | | direction | string enum: SOURCE, DESTINATION, BIDIRECTIONAL | | SOURCE, or BIDIRECTIONAL for import+export connectors | | is_enabled | boolean | | Whether the source is active/enabled | | raw | any | | The raw data returned by the integration for this source | ### CdpConsent | Field | Type | Required | Description | |---|---|---|---| | purpose | string | | The consent purpose or objective (eg. marketing, analytics) | | status | string enum: GRANTED, DENIED, PENDING | | The consent status for this purpose | | updated_at | string (date) | | When the consent status was last updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### CdpIdentifier | Field | Type | Required | Description | |---|---|---|---| | type | string enum: EMAIL, USER_ID, ANONYMOUS_ID, DEVICE_ID, PHONE, CRM_ID, LOYALTY_ID, OTHER | | The type of the identifier (eg. EMAIL, USER_ID, ANONYMOUS_ID) | | value | string | Yes | The identifier value that resolves to the profile | | is_primary | boolean | | Whether this is the primary identifier for the profile | | encoding | string | | The encoding of the identifier value (eg. raw, sha256) | | source | string | | The source that provided this identifier | ### CdpMetadata | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | slug | string | | The attribute/trait key (eg. lifetime_value) | | value | any | | The attribute/trait value | | namespace | string | | The scope or origin of the attribute (eg. computed, event, visitor) | | format | string enum: TEXT, NUMBER, DATE, BOOLEAN, ARRAY, OBJECT | | The data type of the attribute value | | extra_data | any | | | ### CdpProfileSegment | Field | Type | Required | Description | |---|---|---|---| | segment_id | string | | The segment/audience this profile belongs to; points to CdpSegment | | name | string | | The name of the segment/audience | | joined_at | string (date) | | When the profile joined the segment (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | expires_at | string (date) | | When the profile's membership expires (if applicable) (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### CdpProfile_address The address of the profile | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | | --- # Clubs API – Endpoints & Data Models ## Clubs API data models URL: https://docs.unified.to/clubs/overview #### Endpoints | Method | Path | Description | |---|---|---| | GET | /clubs/{connection_id}/group/{id} | Retrieve a group | | GET | /clubs/{connection_id}/group | List all groups | | GET | /clubs/{connection_id}/member/{id} | Retrieve a member | | GET | /clubs/{connection_id}/member | List all members | | GET | /clubs/{connection_id}/activity/{id} | Retrieve an activity | | GET | /clubs/{connection_id}/activity | List all activities | | GET | /clubs/{connection_id}/event/{id} | Retrieve an event | | GET | /clubs/{connection_id}/event | List all events | | GET | /clubs/{connection_id}/location/{id} | Retrieve a location | | GET | /clubs/{connection_id}/location | List all locations | #### Data Models ### ClubsActivity ClubActivity (getClubActivitiesById, NO id) and the full DetailedActivity (getActivityById). | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this activity (not provided by ClubActivity) | | created_at | string (date) | | The date that this activity was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this activity was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | group_id | string | | The group this activity was listed under (reference to HrisGroup) | | name | string | | The name of the activity | | description | string | | The description of the activity (DetailedActivity only) | | type | string enum: RUNNING, CYCLING, SWIMMING, TRIATHLON, WALKING, HIKING, OTHER | | The activity type | | athlete_id | string | | The id of the athlete who performed the activity (DetailedActivity only) | | athlete_name | string | | The full name of the athlete | | distance | number | | The activity's distance, in meters | | moving_time | number | | The activity's moving time, in seconds | | elapsed_time | number | | The activity's elapsed time, in seconds | | total_elevation_gain | number | | The activity's total elevation gain, in meters | | elevation_high | number | | The activity's highest elevation, in meters (DetailedActivity only) | | elevation_low | number | | The activity's lowest elevation, in meters (DetailedActivity only) | | start_at | string (date) | | When the activity started (UTC) (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | timezone | string | | The timezone of the activity | | achievement_count | number | | The number of achievements gained during this activity | | kudos_count | number | | The number of kudos given for this activity | | comment_count | number | | The number of comments for this activity | | athlete_count | number | | The number of athletes that took part in this activity | | photo_count | number | | The number of Instagram photos for this activity | | average_speed | number | | The activity's average speed, in meters per second | | max_speed | number | | The activity's max speed, in meters per second | | average_cadence | number | | The activity's average cadence | | average_heartrate | number | | The activity's average heart rate, in beats per minute | | max_heartrate | number | | The activity's max heart rate, in beats per minute | | average_watts | number | | The activity's average power output, in watts | | max_watts | number | | The activity's max power output, in watts | | weighted_average_watts | number | | The activity's weighted average power output, in watts | | kilojoules | number | | The total work done, in kilojoules | | calories | number | | The number of kilocalories consumed during this activity | | has_heartrate | boolean | | Whether the activity has heart rate data | | is_trainer | boolean | | Whether this activity was recorded on a training machine | | is_commute | boolean | | Whether this activity is a commute | | is_manual | boolean | | Whether this activity was created manually | | is_private | boolean | | Whether this activity is private | | map_polyline | string | | The detailed polyline of the activity's map | | map_summary_polyline | string | | The summary polyline of the activity's map | | workout_type | number | | The activity's workout type | | suffer_score | number | | The activity's relative effort score | | pr_count | number | | The number of personal records set during this activity | | raw | any | | The raw data returned by the integration for this activity | ### ClubsEvent (home-vs-away competitors with scores). Distinct from the fitness-oriented IClubsActivity. | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this event | | created_at | string (date) | | The date that this event was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this event was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | group_id | string | | The group this event belongs to (reference to HrisGroup) | | name | string | | The event name (or a "Home vs Away" matchup label) | | description | string | | A description or notes for the event | | type | string enum: GAME, PRACTICE, OTHER | | The event type | | status | string enum: SCHEDULED, CANCELED, TBD, FINAL | | The event status | | start_at | string (date) | | When the event starts (UTC) (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | When the event ends (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | timezone | string | | The timezone of the event | | duration_minutes | number | | The scheduled duration, in minutes | | location_id | string | | The id of the location for this event (reference to ClubsLocation) | | location_name | string | | The name of the location for this event | | opponent_id | string | | The id of the opposing team (single-opponent providers) | | opponent_name | string | | The name of the opposing team | | home_team_name | string | | The home team's name (homeaway providers) | | away_team_name | string | | The away team's name (homeaway providers) | | home_score | number | | The home team's score | | away_score | number | | The away team's score | | score_for | number | | The connected groupteam's score (team-vs-opponent providers) | | score_against | number | | The opponent's score (team-vs-opponent providers) | | is_home | boolean | | Whether the connected groupteam is the home side | | url | string | | A URL to the event | | raw | any | | The raw data returned by the integration for this event | ### ClubsGroup SummaryClub / DetailedClub, but generic across providers (TeamSnap team, PlayHQ org, etc.). | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this group | | created_at | string (date) | | The date that this group was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this group was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The group's name | | description | string | | The group's description | | sport_type | string enum: RUNNING, CYCLING, SWIMMING, TRIATHLON, WALKING, HIKING, OTHER | | The primary sport of the group | | group_type | string enum: CASUAL_CLUB, RACING_TEAM, SHOP, COMPANY, OTHER | | The group type | | address | ClubsGroup_address | | The group's address | | member_count | number | | The number of members in the group | | following_count | number | | The number of athletes the group follows | | post_count | number | | The number of posts in the group | | is_private | boolean | | Whether the group is private | | is_verified | boolean | | Whether the group is verified | | is_featured | boolean | | Whether the group is featured | | is_admin | boolean | | Whether the authenticated user is an admin of the group | | is_owner | boolean | | Whether the authenticated user is the owner of the group | | membership_status | string enum: MEMBER, PENDING, NONE | | The authenticated user's membership status | | url | string | | The group's vanity URL | | profile_image_url | string | | URL to the group's profile picture | | cover_image_url | string | | URL to the group's cover photo | | activity_types | ClubsGroup_activity_types | | The activity types the group focuses on | | raw | any | | The raw data returned by the integration for this group | ### ClubsLocation formatted address string + lat/long) and PlayHQ venues (structured address parts). | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this location | | created_at | string (date) | | The date that this location was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this location was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | group_id | string | | The group this location is associated with (reference to HrisGroup) | | name | string | | The location name | | address | ClubsLocation_address | | | | latitude | number | | The latitude | | longitude | number | | The longitude | | telephone | string | | A contact phone number for the location | | url | string | | A URL for the location | | raw | any | | The raw data returned by the integration for this location | ### ClubsMember `lastname` is only a single initial. We do not synthesize an id, so `id` is typically absent. | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this member (not provided by Strava) | | created_at | string (date) | | The date that this member was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this member was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The full name of the member (first name + last initial for Strava) | | first_name | string | | The member's first name | | last_name | string | | The member's last name (a single initial for Strava) | | group_id | string | | The group this member belongs to (reference to HrisGroup) | | is_admin | boolean | | Whether the member is an admin of the group | | is_owner | boolean | | Whether the member is the owner of the group | | membership_status | string enum: MEMBER, PENDING, NONE | | The member's membership status | | raw | any | | The raw data returned by the integration for this member | ### ClubsGroup_activity_types The activity types the group focuses on ### ClubsGroup_address The group's address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | ### ClubsLocation_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | --- # Commerce API – Endpoints & Data Models ## Commerce API data models URL: https://docs.unified.to/commerce/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /commerce/{connection_id}/item | Create an item | | GET | /commerce/{connection_id}/item | List all items | | GET | /commerce/{connection_id}/item/{id} | Retrieve an item | | PUT | /commerce/{connection_id}/item/{id} | Update an item | | DELETE | /commerce/{connection_id}/item/{id} | Remove an item | | POST | /commerce/{connection_id}/collection | Create a collection | | GET | /commerce/{connection_id}/collection | List all collections | | GET | /commerce/{connection_id}/collection/{id} | Retrieve a collection | | PUT | /commerce/{connection_id}/collection/{id} | Update a collection | | DELETE | /commerce/{connection_id}/collection/{id} | Remove a collection | | POST | /commerce/{connection_id}/inventory | Create an inventory | | GET | /commerce/{connection_id}/inventory | List all inventories | | GET | /commerce/{connection_id}/inventory/{id} | Retrieve an inventory | | PUT | /commerce/{connection_id}/inventory/{id} | Update an inventory | | DELETE | /commerce/{connection_id}/inventory/{id} | Remove an inventory | | POST | /commerce/{connection_id}/location | Create a location | | GET | /commerce/{connection_id}/location | List all locations | | GET | /commerce/{connection_id}/location/{id} | Retrieve a location | | PUT | /commerce/{connection_id}/location/{id} | Update a location | | DELETE | /commerce/{connection_id}/location/{id} | Remove a location | | POST | /commerce/{connection_id}/review | Create a review | | GET | /commerce/{connection_id}/review | List all reviews | | GET | /commerce/{connection_id}/review/{id} | Retrieve a review | | PUT | /commerce/{connection_id}/review/{id} | Update a review | | DELETE | /commerce/{connection_id}/review/{id} | Remove a review | | POST | /commerce/{connection_id}/reservation | Create a reservation | | GET | /commerce/{connection_id}/reservation | List all reservations | | GET | /commerce/{connection_id}/reservation/{id} | Retrieve a reservation | | PUT | /commerce/{connection_id}/reservation/{id} | Update a reservation | | DELETE | /commerce/{connection_id}/reservation/{id} | Remove a reservation | | GET | /commerce/{connection_id}/availability | List all availabilities | | POST | /commerce/{connection_id}/saleschannel | Create a saleschannel | | GET | /commerce/{connection_id}/saleschannel | List all saleschannels | | GET | /commerce/{connection_id}/saleschannel/{id} | Retrieve a saleschannel | | PUT | /commerce/{connection_id}/saleschannel/{id} | Update a saleschannel | | DELETE | /commerce/{connection_id}/saleschannel/{id} | Remove a saleschannel | | POST | /commerce/{connection_id}/itemvariant | Create an itemvariant | | GET | /commerce/{connection_id}/itemvariant | List all itemvariants | | GET | /commerce/{connection_id}/itemvariant/{id} | Retrieve an itemvariant | | PUT | /commerce/{connection_id}/itemvariant/{id} | Update an itemvariant | | DELETE | /commerce/{connection_id}/itemvariant/{id} | Remove an itemvariant | #### Data Models ### CommerceCollection A collection of items/products/services | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | Yes | | | public_name | string | | | | description | string | | | | public_description | string | | | | media | CommerceItemMedia[] | | | | is_visible | boolean | | | | is_active | boolean | | | | is_featured | boolean | | | | tags | CommerceCollection_tags | | | | type | string enum: COLLECTION, SAVED_SEARCH, CATEGORY | | | | parent_id | string | | | | metadata | CommerceMetadata[] | | | | item_metadata | CommerceMetadata[] | | | | raw | any | | | ### CommerceInventory | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | item_id | string | | (reference to CommerceItem) | | item_variant_id | string | | (reference to CommerceCommerceItemvariant) | | item_option_id | string | | | | location_id | string | | (reference to CommerceLocation) | | available | number | | | | raw | any | | | ### CommerceItem | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | public_name | string | | | | slug | string | | | | description | string | | | | global_code | string | | | | public_description | string | | | | is_active | boolean | | | | is_taxable | boolean | | | | vendor_name | string | | | | type | string | | product, service, digital-download, ... | | is_visible | boolean | | | | is_featured | boolean | | | | weight | number | | | | weight_unit | string enum: g, kg, oz, lb | | | | requires_shipping | boolean | | | | prices | CommerceItemPrice[] | | | | inventory_id | string | | | | total_stock | number | | | | variants | CommerceItemvariant[] | | first variant is the default variant | | tags | CommerceItem_tags | | | | media | CommerceItemMedia[] | | | | collection_ids | CommerceItem_collection_ids | | @deprecated; use collections instead (reference to CommerceCollection) | | account_id | string | | Reference to Accounting Account (reference to AccountingAccount) | | metadata | CommerceMetadata[] | | | | collections | CommerceReference[] | | points to Collection with id, name, and type fields | | taxrate_id | string | | references AccountingTaxrate | | location_id | string | | (reference to CommerceLocation) | | duration | number | | minutes | | raw | any | | | ### CommerceItemvariant | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | public_name | string | | | | description | string | | | | public_description | string | | | | sku | string | | barcode, UPC, isbn, etc | | is_active | boolean | | | | is_visible | boolean | | | | is_featured | boolean | | | | media | CommerceItemMedia[] | | | | available_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | tags | CommerceItemvariant_tags | | | | width | number | | | | height | number | | | | length | number | | | | weight | number | | | | size_unit | string enum: cm, inch | | | | weight_unit | string enum: g, kg, oz, lb | | | | total_stock | number | | | | prices | CommerceItemPrice[] | | | | options | CommerceItemOption[] | | | | inventory_id | string | | | | requires_shipping | boolean | | | | metadata | CommerceMetadata[] | | | | items | CommerceReference[] | | references CommerceItem | | raw | any | | | ### CommerceLocation | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | address | CommerceLocation_address | | | | description | string | | | | is_active | boolean | | | | language_locale | string | | | | parent_id | string | | | | currency | string | | | | location_type | string enum: RESTAURANT, SALON, WAREHOUSE, STORE, OTHER | | | | telephones | CommerceTelephone[] | | | | rating | number | | | | review_count | number | | | | price_level | string | | | | latitude | number | | | | longitude | number | | | | image_url | string | | | | web_url | string | | | | media | CommerceItemMedia[] | | | | raw | any | | | | categories | CommerceLocation_categories | | | ### CommerceReservation | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | location_id | string | | (reference to CommerceLocation) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | size | number | | | | status | string enum: PENDING, CONFIRMED, CANCELLED, NO_SHOW, COMPLETED | | | | guest_name | string | | | | guest_phone | string | | | | guest_email | string | | | | notes | string | | | | item_id | string | | (reference to CommerceItem) | | item_name | string | | | | staff_user_id | string | | (reference to HrisEmployee) | | staff_name | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | url | string | | | | raw | any | | | ### CommerceReview | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | item_id | string | | Reference to the product being reviewed (reference to CommerceItem) | | item_variant_id | string | | Optional reference to specific variant if applicable (reference to CommerceCommerceItemvariant) | | location_id | string | | (reference to CommerceLocation) | | rating | number | | 1-5 rating | | title | string | | | | content | string | | | | author_name | string | | | | author_email | string | | | | author_avatar_url | string | | | | author_location | string | | | | verified_purchase | boolean | | | | helpful_votes | number | | | | unhelpful_votes | number | | | | media | CommerceItemMedia[] | | Photosvideos attached to the review | | status | string enum: PENDING, APPROVED, REJECTED, SPAM | | | | is_verified | boolean | | | | is_featured | boolean | | | | is_public | boolean | | | | comments | CommerceReviewComment[] | | | | metadata | CommerceMetadata[] | | | | url | string | | | | raw | any | | Original review data from source platform | ### CommerceSaleschannel | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | slug | string | | maps to code (name) a stable identifier per channel | | description | string | | | | is_active | boolean | | | | collections | CommerceReference[] | | points to a CommerceCollection | | raw | any | | | ### CommerceCollection_tags ### CommerceItemMedia | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | url | string | Yes | | | alt | string | | | | type | string enum: image, video | | | | height | number | | | | width | number | | | | position | number | | 1 is the first | | metadata | CommerceMetadata[] | | | ### CommerceItemOption | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | Yes | | | position | number | | | | values | CommerceItemOption_values | Yes | | ### CommerceItemOption_values ### CommerceItemPrice | Field | Type | Required | Description | |---|---|---|---| | price | number | Yes | | | compare_at_price | number | | The original price of the item before an adjustment or a sale. | | currency | string | | | ### CommerceItem_collection_ids @deprecated; use collections instead ### CommerceItem_tags ### CommerceItemvariant_tags ### CommerceLocation_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | ### CommerceLocation_categories ### CommerceMetadata | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | value | any | | | | namespace | string | | | | format | string enum: TEXT, NUMBER, DATE, BOOLEAN, FILE, TEXTAREA, SINGLE_SELECT, MULTIPLE_SELECT, MEASUREMENT, PRICE, YES_NO, CURRENCY, URL | | | | extra_data | any | | | | slug | string | | | | description | string | | | | is_required | boolean | | | ### CommerceReference | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | | type | string | | | ### CommerceReviewComment | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | content | string | | | | author_name | string | | | | author_email | string | | | | author_avatar_url | string | | | | author_location | string | | | | helpful_votes | number | | | | unhelpful_votes | number | | | | status | string enum: PENDING, APPROVED, REJECTED, SPAM | | | | is_verified | boolean | | | | is_public | boolean | | | | metadata | CommerceMetadata[] | | | | raw | any | | Original review data from source platform | ### CommerceTelephone | Field | Type | Required | Description | |---|---|---|---| | telephone | string | Yes | | | type | string enum: WORK, HOME, FAX, MOBILE, OTHER | | | --- # Crm API – Endpoints & Data Models ## Crm API data models URL: https://docs.unified.to/crm/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /crm/{connection_id}/deal | Create a deal | | GET | /crm/{connection_id}/deal | List all deals | | GET | /crm/{connection_id}/deal/{id} | Retrieve a deal | | PUT | /crm/{connection_id}/deal/{id} | Update a deal | | DELETE | /crm/{connection_id}/deal/{id} | Remove a deal | | POST | /crm/{connection_id}/contact | Create a contact | | GET | /crm/{connection_id}/contact | List all contacts | | GET | /crm/{connection_id}/contact/{id} | Retrieve a contact | | PUT | /crm/{connection_id}/contact/{id} | Update a contact | | DELETE | /crm/{connection_id}/contact/{id} | Remove a contact | | POST | /crm/{connection_id}/company | Create a company | | GET | /crm/{connection_id}/company | List all companies | | GET | /crm/{connection_id}/company/{id} | Retrieve a company | | PUT | /crm/{connection_id}/company/{id} | Update a company | | DELETE | /crm/{connection_id}/company/{id} | Remove a company | | POST | /crm/{connection_id}/event | Create an event | | GET | /crm/{connection_id}/event | List all events | | GET | /crm/{connection_id}/event/{id} | Retrieve an event | | PUT | /crm/{connection_id}/event/{id} | Update an event | | DELETE | /crm/{connection_id}/event/{id} | Remove an event | | POST | /crm/{connection_id}/lead | Create a lead | | GET | /crm/{connection_id}/lead | List all leads | | GET | /crm/{connection_id}/lead/{id} | Retrieve a lead | | PUT | /crm/{connection_id}/lead/{id} | Update a lead | | DELETE | /crm/{connection_id}/lead/{id} | Remove a lead | | GET | /crm/{connection_id}/picklist | List all picklists | | POST | /crm/{connection_id}/pipeline | Create a pipeline | | GET | /crm/{connection_id}/pipeline | List all pipelines | | GET | /crm/{connection_id}/pipeline/{id} | Retrieve a pipeline | | PUT | /crm/{connection_id}/pipeline/{id} | Update a pipeline | | DELETE | /crm/{connection_id}/pipeline/{id} | Remove a pipeline | #### Data Models ### CrmCompany A company represents an organization that optionally is associated with a deal and/or contacts | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this company object | | created_at | string (date) | | The date that this company object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this company object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the company | | deal_ids | CrmCompany_deal_ids | | An array of deal IDs associated with this contact (reference to CrmDeal) | | contact_ids | CrmCompany_contact_ids | | An array of contact IDs associated with this company (reference to CrmContact) | | emails | CrmEmail[] | | | | telephones | CrmTelephone[] | | | | websites | CrmCompany_websites | | | | address | CrmCompany_address | | | | is_active | boolean | | | | tags | CrmCompany_tags | | | | description | string | | | | industry | string | | | | link_urls | CrmCompany_link_urls | | Additional URLs associated with the contact e.g., LinkedIn, website, etc | | employees | number | | | | timezone | string | | | | user_id | string | | (reference to HrisEmployee) | | metadata | CrmMetadata[] | | | | domains | CrmCompany_domains | | | | raw | any | | The raw data returned by the integration for this company | ### CrmContact A contact represents a person that optionally is associated with a deal and/or a company | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this contact object | | created_at | string (date) | | The date that this contact object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this contact object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the contact | | first_name | string | | | | last_name | string | | | | title | string | | The job title of the contact | | company | string | | The companyorganization name of the contact | | emails | CrmEmail[] | | An array of email addresses for this contact | | telephones | CrmTelephone[] | | An array of telephones for this contact | | deal_ids | CrmContact_deal_ids | | An array of deal IDs associated with this contact (reference to CrmDeal) | | company_ids | CrmContact_company_ids | | An array of company IDs associated with this contact (reference to CrmCompany) | | address | CrmContact_address | | | | user_id | string | | (reference to HrisEmployee) | | link_urls | CrmContact_link_urls | | Additional URLs associated with the contact e.g., LinkedIn, website, etc | | metadata | CrmMetadata[] | | | | department | string | | | | image_url | string | | | | raw | any | | The raw data returned by the integration for this contact | ### CrmDeal A deal represents an opportunity with companies and/or contacts | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this deal object | | created_at | string (date) | | The date that this deal object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this deal object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of this deal | | amount | number | | The potential amount that deal could be worth | | currency | string | | The currency for the deal amount (3 letter ISO code; eg. USD) | | closed_at | string (date) | | The date that this deal closed (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | closing_at | string (date) | | expected closing date (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | stage | string | | @deprecated Use stages array instead | | stage_id | string | | @deprecated Use stages array instead | | pipeline | string | | @deprecated Use pipelines array instead | | pipeline_id | string | | @deprecated Use pipelines array instead | | stages | CrmReference[] | | | | pipelines | CrmReference[] | | | | source | string | | The source for this deal | | probability | number | | | | tags | CrmDeal_tags | | | | lost_reason | string | | | | won_reason | string | | | | user_id | string | | (reference to HrisEmployee) | | contact_ids | CrmDeal_contact_ids | | (reference to CrmContact) | | company_ids | CrmDeal_company_ids | | (reference to CrmCompany) | | metadata | CrmMetadata[] | | | | raw | any | | The raw data returned by the integration for this deal | ### CrmEvent An event represents an event, activity, or engagement and is always associated with a deal, contact, or company | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this event object | | created_at | string (date) | | The date that this event object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this event object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | type | string enum: NOTE, EMAIL, TASK, MEETING, CALL, MARKETING_EMAIL, FORM, PAGE_VIEW | | The type of event | | note | CrmEvent_note | | The note object, when type = note | | meeting | CrmEvent_meeting | | The meeting object, when type = meeting | | email | CrmEvent_email | | The email object, when type = email | | call | CrmEvent_call | | The call object, when type = call | | task | CrmEvent_task | | The task object, when type = task | | marketing_email | CrmEvent_marketing_email | | | | form | CrmEvent_form | | | | page_view | CrmEvent_page_view | | | | deal_ids | CrmEvent_deal_ids | | An array of deal IDs associated with this event (reference to CrmDeal) | | company_ids | CrmEvent_company_ids | | An array of company IDs associated with this event (reference to CrmCompany) | | contact_ids | CrmEvent_contact_ids | | An array of contact IDs associated with this event (reference to CrmContact) | | lead_ids | CrmEvent_lead_ids | | (reference to CrmLead) | | user_id | string | | (reference to HrisEmployee) | | raw | any | | The raw data returned by the integration for this event. | ### CrmLead | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | first_name | string | | | | last_name | string | | | | user_id | string | | (reference to HrisEmployee) | | creator_user_id | string | | (reference to HrisEmployee) | | contact_id | string | | (reference to CrmContact) | | company_id | string | | (reference to CrmCompany) | | company_name | string | | | | is_active | boolean | | | | address | CrmLead_address | | | | emails | CrmEmail[] | | | | telephones | CrmTelephone[] | | | | source | string | | | | status | string | | | | link_urls | CrmLead_link_urls | | | | metadata | CrmMetadata[] | | | | raw | any | | | ### CrmPipeline | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | is_active | boolean | | | | deal_probability | number | | | | display_order | number | | | | stages | CrmStage[] | | | | raw | any | | | ### CrmCompany_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | ### CrmCompany_contact_ids An array of contact IDs associated with this company ### CrmCompany_deal_ids An array of deal IDs associated with this contact ### CrmCompany_domains ### CrmCompany_link_urls Additional URLs associated with the contact e.g., LinkedIn, website, etc ### CrmCompany_tags ### CrmCompany_websites ### CrmContact_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | ### CrmContact_company_ids An array of company IDs associated with this contact ### CrmContact_deal_ids An array of deal IDs associated with this contact ### CrmContact_link_urls Additional URLs associated with the contact e.g., LinkedIn, website, etc ### CrmDeal_company_ids ### CrmDeal_contact_ids ### CrmDeal_tags ### CrmEmail | Field | Type | Required | Description | |---|---|---|---| | email | string | | | | type | string enum: WORK, HOME, OTHER | | | ### CrmEventFormField | Field | Type | Required | Description | |---|---|---|---| | name | string | | | | type | string enum: TEXT, NUMBER, DATE, BOOLEAN, MULTIPLE_CHOICE, FILE, TEXTAREA, SINGLE_SELECT, MULTIPLE_SELECT, EMAIL, PHONE, YES_NO, CURRENCY, URL | | | | options | CrmEventFormOption[] | | | | required | boolean | | | ### CrmEventFormOption | Field | Type | Required | Description | |---|---|---|---| | label | string | | | | value | string | | | ### CrmEvent_call The call object, when type = call | Field | Type | Required | Description | |---|---|---|---| | duration | number | | The event call's duration in minutes | | description | string | | The event call's descriptionnotes | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### CrmEvent_company_ids An array of company IDs associated with this event ### CrmEvent_contact_ids An array of contact IDs associated with this event ### CrmEvent_deal_ids An array of deal IDs associated with this event ### CrmEvent_email The email object, when type = email | Field | Type | Required | Description | |---|---|---|---| | from | string | | The event email's from name & email address (name ) | | to | CrmEvent_email_to | | The event email's "to" name & email (name ) | | cc | CrmEvent_email_cc | | The event email's cc name & email (name ) | | subject | string | | The event email's subject | | body | string | | The event email's body | | attachment_file_ids | CrmEvent_email_attachment_file_ids | | | ### CrmEvent_email_attachment_file_ids ### CrmEvent_email_cc The event email's cc name & email (name ) ### CrmEvent_email_to The event email's "to" name & email (name ) ### CrmEvent_form | Field | Type | Required | Description | |---|---|---|---| | name | string | | | | archived_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | fields | CrmEventFormField[] | | | | redirect_url | string | | | ### CrmEvent_lead_ids ### CrmEvent_marketing_email | Field | Type | Required | Description | |---|---|---|---| | name | string | | | | from | string | | The event email's from name & email address (name ) | | to | CrmEvent_marketing_email_to | | The event email's "to" name & email (name ) | | cc | CrmEvent_marketing_email_cc | | The event email's cc name & email (name ) | | subject | string | | The event email's subject | | body | string | | The event email's body | | attachment_file_ids | CrmEvent_marketing_email_attachment_file_ids | | | ### CrmEvent_marketing_email_attachment_file_ids ### CrmEvent_marketing_email_cc The event email's cc name & email (name ) ### CrmEvent_marketing_email_to The event email's "to" name & email (name ) ### CrmEvent_meeting The meeting object, when type = meeting | Field | Type | Required | Description | |---|---|---|---| | start_at | string (date) | | The event meeting's start datetime (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | The event meeting's end datetime (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | title | string | | The event meeting's topicsubject | | description | string | | The event meeting's agendadescription | ### CrmEvent_note The note object, when type = note | Field | Type | Required | Description | |---|---|---|---| | description | string | | The event note's description | | title | string | | | ### CrmEvent_page_view | Field | Type | Required | Description | |---|---|---|---| | count | number | | | | average | number | | | | url | string | | | ### CrmEvent_task The task object, when type = task | Field | Type | Required | Description | |---|---|---|---| | name | string | | The event task's nametitle/subject | | status | string enum: COMPLETED, NOT_STARTED, WORK_IN_PROGRESS, DEFERRED | | The event task's status | | description | string | | The event task's descriptionnote | | due_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | priority | string enum: HIGH, MEDIUM, LOW | | | ### CrmLead_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | ### CrmLead_link_urls ### CrmMetadata | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | slug | string | | | | value | any | | | | namespace | string | | | | format | string enum: TEXT, NUMBER, DATE, BOOLEAN, FILE, TEXTAREA, SINGLE_SELECT, MULTIPLE_SELECT, MEASUREMENT, PRICE, YES_NO, CURRENCY, URL | | | | extra_data | any | | | ### CrmReference | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | | type | string | | | ### CrmStage | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | active | boolean | | | | deal_probability | number | | | | is_closed | boolean | | | | display_order | number | | | ### CrmTelephone | Field | Type | Required | Description | |---|---|---|---| | telephone | string | Yes | | | type | string enum: WORK, HOME, OTHER, FAX, MOBILE | | | --- # Datastore API – Endpoints & Data Models ## Datastore API data models URL: https://docs.unified.to/datastore/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /datastore/{connection_id}/database | Create a database | | GET | /datastore/{connection_id}/database | List all databases | | GET | /datastore/{connection_id}/database/{id} | Retrieve a database | | PUT | /datastore/{connection_id}/database/{id} | Update a database | | DELETE | /datastore/{connection_id}/database/{id} | Remove a database | | POST | /datastore/{connection_id}/table | Create a table | | GET | /datastore/{connection_id}/table | List all tables | | GET | /datastore/{connection_id}/table/{id} | Retrieve a table | | PUT | /datastore/{connection_id}/table/{id} | Update a table | | DELETE | /datastore/{connection_id}/table/{id} | Remove a table | | POST | /datastore/{connection_id}/record | Create a record | | GET | /datastore/{connection_id}/record | List all records | | GET | /datastore/{connection_id}/record/{id} | Retrieve a record | | PUT | /datastore/{connection_id}/record/{id} | Update a record | | DELETE | /datastore/{connection_id}/record/{id} | Remove a record | | POST | /datastore/{connection_id}/query | Create a query | #### Data Models ### DatastoreDatabase | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | web_url | string | | | | is_active | boolean | | | | password | string | | | | region | string | | | | raw | any | | | ### DatastoreQuery | Field | Type | Required | Description | |---|---|---|---| | table_id | string | | | | database_id | string | | | | query | DatastoreQuery_query | | | | response | DatastoreQuery_response | | | | raw | any | | | ### DatastoreRecord | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | database_id | string | | source Database ID | | table_id | string | | source Database Table ID | | fields | DatastoreRecord_fields | Yes | | | row_number | number | | | | raw | any | | | ### DatastoreTable | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | database_id | string | | source Database ID | | fields | DatastoreField[] | | | | relationships | DatastoreRelationship[] | | | | web_url | string | | | | parent_id | string | | | | raw | any | | | ### DatastoreField | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | Yes | | | type | string enum: TEXT, NUMBER, DATE, BOOLEAN, FILE, TEXTAREA, SINGLE_SELECT, MULTIPLE_SELECT, CURRENCY, URL, EMAIL, PHONE, LINKED_RECORD, RELATION | Yes | | | original_type | string | Yes | | | is_required | boolean | | | | is_indexed | boolean | | | | is_primary_key | boolean | | | | is_unique | boolean | | | | is_nullable | boolean | | | | max_length | number | | | | precision | number | | | | scale | number | | | | default_value | DatastoreField_default_value | | | | description | string | | | | raw | any | | | ### DatastoreFieldValue | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFieldValue_selection | | | | is_null | boolean | | | ### DatastoreFieldValue_selection ### DatastoreField_default_value | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreField_default_value_selection | | | | is_null | boolean | | | ### DatastoreField_default_value_selection ### DatastoreFilter | Field | Type | Required | Description | |---|---|---|---| | field | string | Yes | | | condition | DatastoreFilter_condition | Yes | | ### DatastoreFilter_condition | Field | Type | Required | Description | |---|---|---|---| | eq | DatastoreFilter_condition_eq | | | | neq | DatastoreFilter_condition_neq | | | | gt | DatastoreFilter_condition_gt | | | | gte | DatastoreFilter_condition_gte | | | | lt | DatastoreFilter_condition_lt | | | | lte | DatastoreFilter_condition_lte | | | | like | DatastoreFilter_condition_like | | | | ilike | DatastoreFilter_condition_ilike | | | | in | DatastoreFieldValue[] | | | | contains | DatastoreFilter_condition_contains | | | | not_contains | DatastoreFilter_condition_not_contains | | | | is_null | boolean | | | | is_not_null | boolean | | | ### DatastoreFilter_condition_contains | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFilter_condition_contains_selection | | | | is_null | boolean | | | ### DatastoreFilter_condition_contains_selection ### DatastoreFilter_condition_eq | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFilter_condition_eq_selection | | | | is_null | boolean | | | ### DatastoreFilter_condition_eq_selection ### DatastoreFilter_condition_gt | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFilter_condition_gt_selection | | | | is_null | boolean | | | ### DatastoreFilter_condition_gt_selection ### DatastoreFilter_condition_gte | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFilter_condition_gte_selection | | | | is_null | boolean | | | ### DatastoreFilter_condition_gte_selection ### DatastoreFilter_condition_ilike | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFilter_condition_ilike_selection | | | | is_null | boolean | | | ### DatastoreFilter_condition_ilike_selection ### DatastoreFilter_condition_like | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFilter_condition_like_selection | | | | is_null | boolean | | | ### DatastoreFilter_condition_like_selection ### DatastoreFilter_condition_lt | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFilter_condition_lt_selection | | | | is_null | boolean | | | ### DatastoreFilter_condition_lt_selection ### DatastoreFilter_condition_lte | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFilter_condition_lte_selection | | | | is_null | boolean | | | ### DatastoreFilter_condition_lte_selection ### DatastoreFilter_condition_neq | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFilter_condition_neq_selection | | | | is_null | boolean | | | ### DatastoreFilter_condition_neq_selection ### DatastoreFilter_condition_not_contains | Field | Type | Required | Description | |---|---|---|---| | string | string | | | | number | number | | | | boolean | boolean | | | | date | string | | | | selection | DatastoreFilter_condition_not_contains_selection | | | | is_null | boolean | | | ### DatastoreFilter_condition_not_contains_selection ### DatastoreQuery_query | Field | Type | Required | Description | |---|---|---|---| | sql | string | | Raw SQL – when set, executed directly (table_id optional) | | select | DatastoreQuery_query_select | | | | expand | DatastoreQuery_query_expand | | | | filter | DatastoreQuery_query_filter | | | | sort_fields | DatastoreQuery_query_sort_fields | | | | sort_order | string enum: asc, desc | | | | limit | number | | | | offset | number | | | | aggregate | DatastoreQuery_query_aggregate | | | | is_count_only | boolean | | | ### DatastoreQuery_query_aggregate | Field | Type | Required | Description | |---|---|---|---| | count | boolean | | | | sum | DatastoreQuery_query_aggregate_sum | | | | avg | DatastoreQuery_query_aggregate_avg | | | | min | DatastoreQuery_query_aggregate_min | | | | max | DatastoreQuery_query_aggregate_max | | | | group_by | DatastoreQuery_query_aggregate_group_by | | | ### DatastoreQuery_query_aggregate_avg | Field | Type | Required | Description | |---|---|---|---| | field | string | Yes | | | alias | string | | | ### DatastoreQuery_query_aggregate_group_by ### DatastoreQuery_query_aggregate_max | Field | Type | Required | Description | |---|---|---|---| | field | string | Yes | | | alias | string | | | ### DatastoreQuery_query_aggregate_min | Field | Type | Required | Description | |---|---|---|---| | field | string | Yes | | | alias | string | | | ### DatastoreQuery_query_aggregate_sum | Field | Type | Required | Description | |---|---|---|---| | field | string | Yes | | | alias | string | | | ### DatastoreQuery_query_expand ### DatastoreQuery_query_filter | Field | Type | Required | Description | |---|---|---|---| | type | string enum: FILTER, AND, OR | Yes | | | filters | DatastoreFilter[] | | | | and | DatastoreQuery_query_filter_and | | | | or | DatastoreQuery_query_filter_or | | | ### DatastoreQuery_query_select ### DatastoreQuery_query_sort_fields ### DatastoreQuery_response | Field | Type | Required | Description | |---|---|---|---| | items | DatastoreRecord[] | | | | aggregates | DatastoreQuery_response_aggregates | | | | total_count | number | | | ### DatastoreQuery_response_aggregates | Field | Type | Required | Description | |---|---|---|---| | count | number | | | | sum | DatastoreQuery_response_aggregates_sum | | | | avg | DatastoreQuery_response_aggregates_avg | | | | min | DatastoreQuery_response_aggregates_min | | | | max | DatastoreQuery_response_aggregates_max | | | | groups | DatastoreQuery_response_aggregates_groups | | | ### DatastoreQuery_response_aggregates_avg ### DatastoreQuery_response_aggregates_groups ### DatastoreQuery_response_aggregates_max ### DatastoreQuery_response_aggregates_min ### DatastoreQuery_response_aggregates_sum ### DatastoreRecord_fields ### DatastoreRelationship | Field | Type | Required | Description | |---|---|---|---| | name | string | Yes | | | field | string | Yes | | | table_id | string | Yes | target Database Table ID | | type | string enum: ONE, MANY | Yes | | --- # Enrich API – Endpoints & Data Models ## Enrich API data models URL: https://docs.unified.to/enrich/overview #### Endpoints | Method | Path | Description | |---|---|---| | GET | /enrich/{connection_id}/person | Retrieve enrichment information for a person | | GET | /enrich/{connection_id}/company | Retrieve enrichment information for a company | #### Data Models ### EnrichCompany A company object from an enrichment integration | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this company object | | created_at | string (date) | | The date that this company object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this company object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the company | | description | string | | The description of the company | | domain | string | | The webmail domain of the company | | twitter_handle | string | | The twitter handle of the company | | twitter_url | string | | The twitter URL of the company | | address | EnrichCompany_address | | The address of the company | | telephones | EnrichTelephone[] | | An array of telephones for this company | | linkedin_url | string | | The LinkedIn URL of the company | | crunchbase_url | string | | The Crunchbase URL of the company | | facebook_url | string | | The Facebook URL of the company | | youtube_url | string | | The Youtube URL of the company | | instagram_url | string | | The Crunchbase URL of the company | | yelp_url | string | | The Yelp URL of the company | | logo_url | string | | The URL of the logo of the company | | exchange | string | | The public exchange of the company (eg. NASDAQ) | | stock | string | | The stock ticker of the company (eg. AMZN) | | year_founded | number | | The year that the company was founded | | alexa_rank | number | | The Alexa rank of the company | | naics_code | number | | The NAICS code of the company | | sic_code | number | | The SIC code of the company | | employees | string | | The number of employees at the company | | revenue | string | | The approximate revenue of the company | | industry | string | | The industry of the company | | raw | any | | The raw data returned by the integration for this company | ### EnrichPerson A person object from an enrichment integration | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this person object | | created_at | string (date) | | The date that this person object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this person object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the person | | first_name | string | | | | last_name | string | | | | title | string | | The job title of the person | | birthdate | string | | The birth date of the person | | image_url | string | | The image URL of the person | | bio | string | | The biography description of the person | | company | string | | The companyorganization name of the person | | company_domain | string | | The company's domain | | emails | EnrichEmail[] | | An array of email addresses for this person | | telephones | EnrichTelephone[] | | An array of telephones for this person | | twitter_handle | string | | The twitter handle of the person | | linkedin_url | string | | The LinkedIn URL of the person | | github_url | string | | The GitHub URL of the person | | github_username | string | | The GitHub username of the person | | twitter_url | string | | The twitter URL of the person | | facebook_url | string | | The Facebook URL of the person | | gender | string enum: MALE, FEMALE | | The gender of the person | | address | EnrichPerson_address | | The address of the person | | timezone | string | | The timezone code of the person | | utc_offset | number | | The timezone's hourly offset from UTC of the person | | work_histories | EnrichPersonWorkHistory[] | | | | raw | any | | The raw data returned by the integration for this person | ### EnrichCompany_address The address of the company | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | | ### EnrichEmail | Field | Type | Required | Description | |---|---|---|---| | email | string | Yes | | | type | string enum: WORK, HOME, OTHER | | | | is_verified | boolean | | | ### EnrichPersonWorkHistory | Field | Type | Required | Description | |---|---|---|---| | company_id | string | | (reference to EnrichCompany) | | company_name | string | | | | title | string | Yes | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | location | string | | | | company_domain | string | | | ### EnrichPerson_address The address of the person | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | | ### EnrichTelephone | Field | Type | Required | Description | |---|---|---|---| | telephone | string | Yes | | | type | string enum: WORK, HOME, OTHER, FAX, MOBILE | | | --- # Forms API – Endpoints & Data Models ## Forms API data models URL: https://docs.unified.to/forms/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /forms/{connection_id}/form | Create a form | | GET | /forms/{connection_id}/form | List all forms | | GET | /forms/{connection_id}/form/{id} | Retrieve a form | | PUT | /forms/{connection_id}/form/{id} | Update a form | | DELETE | /forms/{connection_id}/form/{id} | Remove a form | | GET | /forms/{connection_id}/submission/{id} | Retrieve a submission | | GET | /forms/{connection_id}/submission | List all submissions | #### Data Models ### FormsForm | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | fields | FormField[] | | | | is_active | boolean | | | | published_url | string | | | | response_count | number | | | | has_multiple_submissions | boolean | | | | has_progress_bar | boolean | | | | has_shuffle_questions | boolean | | | | confirmation_message | string | | | | confirmation_redirect_url | string | | | | raw | any | | | ### FormsSubmission | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | form_id | string | Yes | | | respondent_email | string | | | | respondent_name | string | | | | answers | FormAnswer[] | Yes | | | raw | any | | | ### FormAnswer | Field | Type | Required | Description | |---|---|---|---| | field_id | string | Yes | | | field_name | string | | | | value | string | | Can be string, number, boolean, array, etc. | | file_ids | FormAnswer_file_ids | | references StorageFile ID | ### FormAnswer_file_ids references StorageFile ID ### FormField | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | Yes | | | description | string | | | | type | string enum: TEXT, TEXTAREA, NUMBER, EMAIL, URL, DATE, TIME, DATETIME, PHONE, BOOLEAN, SINGLE_SELECT, MULTIPLE_SELECT, FILE_UPLOAD, RATING, SCALE, MATRIX, SECTION_HEADER, OTHER | Yes | | | is_required | boolean | | | | is_active | boolean | | | | choices | FormField_choices | | For select/radio/checkbox fields | | default_value | string | | | | min | number | | | | max | number | | | | pattern | string | | | | min_length | number | | | | max_length | number | | | | order | number | | | ### FormField_choices For select/radio/checkbox fields --- # Genai API – Endpoints & Data Models ## Genai API data models URL: https://docs.unified.to/genai/overview #### Endpoints | Method | Path | Description | |---|---|---| | GET | /genai/{connection_id}/model/{id} | Retrieve a model | | GET | /genai/{connection_id}/model | List all models | | POST | /genai/{connection_id}/prompt | Create a prompt | | POST | /genai/{connection_id}/embedding | Create an embedding | #### Data Models ### GenaiEmbedding | Field | Type | Required | Description | |---|---|---|---| | model_id | string | | | | content | GenaiEmbeddingContent[] | | | | enconding_format | string enum: FLOAT, UINT8, INT8, BINARY, UBINARY, BASE64 | | | | type | string | | 'SEARCH_DOC' \| 'SEARCH_QUERY' \| 'CLUSTERING' \| 'CLASSIFICATION'; | | dimension | number | | 256, 512, 1024, and 1536 | | max_tokens | number | | | | embeddings | string | | JSON string based off of the format (read-only) | | tokens_used | number | | (read-only) | | raw | any | | | | id | string | | | ### GenaiModel | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | | description | string | | | | max_tokens | number | | The maximum number of tokens that the model can process in a single response. This limits ensures computational efficiency and resource management. | | web_url | string | | | | has_temperature | boolean | | Controls randomness of responses. A lower temperature leads to more predictable outputs while a higher temperature results in more varies and sometimes more creative outputs. 0-1 | | raw | any | | | ### GenaiPrompt | Field | Type | Required | Description | |---|---|---|---| | model_id | string | | | | messages | GenaiContent[] | | | | temperature | number | | 0-1 | | max_tokens | number | | a float between 0-1 | | responses | GenaiPrompt_responses | | | | tokens_used | number | | | | mcp_url | string | | Supply a remote MCP URL to send to the LLM API for it to call its tools. Note: Some LLM APIs do not yet support remote MCP URLs. | | mcp_deferred_tools | GenaiPrompt_mcp_deferred_tools | | | | mcp_authorization_token | string | | OAuth Bearer token for MCP servers that require authentication. | | raw | any | | | ### GenaiContent | Field | Type | Required | Description | |---|---|---|---| | role | string enum: SYSTEM, USER, ASSISTANT | | | | content | string | Yes | | ### GenaiEmbeddingContent | Field | Type | Required | Description | |---|---|---|---| | text | string | Yes | | ### GenaiPrompt_mcp_deferred_tools ### GenaiPrompt_responses --- # Hris API – Endpoints & Data Models ## Hris API data models URL: https://docs.unified.to/hris/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /hris/{connection_id}/employee | Create an employee | | GET | /hris/{connection_id}/employee | List all employees | | GET | /hris/{connection_id}/employee/{id} | Retrieve an employee | | PUT | /hris/{connection_id}/employee/{id} | Update an employee | | DELETE | /hris/{connection_id}/employee/{id} | Remove an employee | | POST | /hris/{connection_id}/company | Create a company | | GET | /hris/{connection_id}/company | List all companies | | GET | /hris/{connection_id}/company/{id} | Retrieve a company | | PUT | /hris/{connection_id}/company/{id} | Update a company | | DELETE | /hris/{connection_id}/company/{id} | Remove a company | | POST | /hris/{connection_id}/location | Create a location | | GET | /hris/{connection_id}/location | List all locations | | GET | /hris/{connection_id}/location/{id} | Retrieve a location | | PUT | /hris/{connection_id}/location/{id} | Update a location | | DELETE | /hris/{connection_id}/location/{id} | Remove a location | | POST | /hris/{connection_id}/group | Create a group | | GET | /hris/{connection_id}/group | List all groups | | GET | /hris/{connection_id}/group/{id} | Retrieve a group | | PUT | /hris/{connection_id}/group/{id} | Update a group | | DELETE | /hris/{connection_id}/group/{id} | Remove a group | | POST | /hris/{connection_id}/timeoff | Create a timeoff | | GET | /hris/{connection_id}/timeoff | List all timeoffs | | GET | /hris/{connection_id}/timeoff/{id} | Retrieve a timeoff | | PUT | /hris/{connection_id}/timeoff/{id} | Update a timeoff | | DELETE | /hris/{connection_id}/timeoff/{id} | Remove a timeoff | | GET | /hris/{connection_id}/payslip/{id} | Retrieve a payslip | | GET | /hris/{connection_id}/payslip | List all payslips | | POST | /hris/{connection_id}/device | Create a device | | GET | /hris/{connection_id}/device | List all devices | | GET | /hris/{connection_id}/device/{id} | Retrieve a device | | PUT | /hris/{connection_id}/device/{id} | Update a device | | DELETE | /hris/{connection_id}/device/{id} | Remove a device | | POST | /hris/{connection_id}/timeshift | Create a timeshift | | GET | /hris/{connection_id}/timeshift | List all timeshifts | | GET | /hris/{connection_id}/timeshift/{id} | Retrieve a timeshift | | PUT | /hris/{connection_id}/timeshift/{id} | Update a timeshift | | DELETE | /hris/{connection_id}/timeshift/{id} | Remove a timeshift | | POST | /hris/{connection_id}/benefit | Create a benefit | | GET | /hris/{connection_id}/benefit | List all benefits | | GET | /hris/{connection_id}/benefit/{id} | Retrieve a benefit | | PUT | /hris/{connection_id}/benefit/{id} | Update a benefit | | DELETE | /hris/{connection_id}/benefit/{id} | Remove a benefit | | POST | /hris/{connection_id}/deduction | Create a deduction | | GET | /hris/{connection_id}/deduction | List all deductions | | GET | /hris/{connection_id}/deduction/{id} | Retrieve a deduction | | PUT | /hris/{connection_id}/deduction/{id} | Update a deduction | | DELETE | /hris/{connection_id}/deduction/{id} | Remove a deduction | | POST | /hris/{connection_id}/bankaccount | Create a bankaccount | | GET | /hris/{connection_id}/bankaccount | List all bankaccounts | | GET | /hris/{connection_id}/bankaccount/{id} | Retrieve a bankaccount | | PUT | /hris/{connection_id}/bankaccount/{id} | Update a bankaccount | | DELETE | /hris/{connection_id}/bankaccount/{id} | Remove a bankaccount | | GET | /hris/{connection_id}/taxonomy/{id} | Retrieve a taxonomy | | GET | /hris/{connection_id}/taxonomy | List all taxonomies | | POST | /hris/{connection_id}/document | Create a document | | GET | /hris/{connection_id}/document | List all documents | | GET | /hris/{connection_id}/document/{id} | Retrieve a document | | PUT | /hris/{connection_id}/document/{id} | Update a document | | DELETE | /hris/{connection_id}/document/{id} | Remove a document | #### Data Models ### HrisBankaccount | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | user_id | string | | employee ID (required for listcreate) (reference to HrisEmployee) | | company_id | string | | (reference to HrisCompany) | | account_type | string enum: CHECKING, SAVINGS | | | | bank_name | string | | | | routing_number | string | | 9-digit US routing number | | account_number | string | | | | account_number_last4 | string | | when full number not returned | | name | string | | account nickname (e.g. "BoA Checking") | | is_primary | boolean | | primary for direct deposit | | raw | any | | | ### HrisBenefit Company-wide benefit plans available to employees. | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | e.g. "401(k) Plan" | | description | string | | e.g. "Company 401(k) retirement plan" | | company_id | string | | (reference to HrisCompany) | | type | string enum: RETIREMENT, HEALTH, DENTAL, VISION, LIFE, HSA, FSA, SHORT_TERM_DISABILITY, LONG_TERM_DISABILITY, WORKERS_COMP, HOUSING_STIPEND, EMPLOYER_TAX_CONTRIBUTION, GARNISHMENT, LOAN_REPAYMENT, CHARITABLE_CONTRIBUTION, OTHER | | | | tax | string enum: PRE_TAX, POST_TAX, TAXABLE, NON_TAXABLE, TAX | | Tax describes the tax treatment of the benefit | | frequency | string enum: ONE_TIME, DAY, QUARTER, YEAR, HOUR, MONTH, WEEK | | Frequency that costs accrue for this benefit. For insurance/benefits, usually "MONTHLY". For compensation, matches pay cycle (WEEKLY, BIWEEKLY, etc). | | is_active | boolean | | | | employer_contribution_type | string enum: PERCENTAGE, FIXED | | | | employer_contribution_amount | number | | percentage or money | | employer_contribution_max_amount | number | | always money | | coverage_level | string enum: EMPLOYEE_ONLY, EMPLOYEE_SPOUSE, EMPLOYEE_CHILD, EMPLOYEE_CHILDREN, EMPLOYEE_FAMILY, FAMILY, OTHER | | | | currency | string | | | | raw | any | | | ### HrisCompany | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | legal_name | string | | | | address | HrisCompany_address | | | | raw | any | | | ### HrisDeduction Employee-specific deduction/benefit enrolment. | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | user_id | string | | employee ID (reference to HrisEmployee) | | company_id | string | | (reference to HrisCompany) | | benefit_id | string | | | | amount | number | | percentage or absolute amount (employee's portion) | | type | string enum: FIXED, PERCENTAGE | | | | coverage_level | string enum: EMPLOYEE_ONLY, EMPLOYEE_SPOUSE, EMPLOYEE_CHILD, EMPLOYEE_CHILDREN, EMPLOYEE_FAMILY, FAMILY, OTHER | | Level selected by employee (e.g. "FAMILY", "EMPLOYEE_ONLY", or other) | | frequency | string enum: ONE_TIME, DAY, QUARTER, YEAR, HOUR, MONTH, WEEK | | Frequency for this deduction (should always be set, matches IHrisBenefit.frequency) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | is_active | boolean | | | | notes | string | | | | raw | any | | | ### HrisDevice | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | asset_tag | string | | | | version | string | | device version | | manufacturer | string | | device manufacturer | | model | string | | device model | | os | string | | operating system name | | os_version | string | | | | user_ids | HrisDevice_user_ids | | users who have this device (reference to HrisEmployee) | | admin_user_ids | HrisDevice_admin_user_ids | | (reference to HrisEmployee) | | location_id | string | | pointer to HR Location object (reference to HrisLocation) | | has_antivirus | boolean | | | | has_password_manager | boolean | | | | has_firewall | boolean | | | | has_hd_encrypted | boolean | | | | has_screenlock | boolean | | | | is_missing | boolean | | | | raw | any | | | ### HrisDocument | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | document_url | string | | download link, expires after 1 hour | | document_data | string | | base64 encoded file contents for create/update | | filename | string | | | | type | string enum: CONTRACT, OFFER_LETTER, POLICY, TAX, ID, VISA, PAYSLIP, BENEFITS, CERTIFICATION, PERFORMANCE_REVIEW, ONBOARDING, TERMINATION, MEDICAL, OTHER | | unified, normalized document type (OTHER when unmappable) | | user_id | string | | pointer to the HR Employee object that owns this document (reference to HrisEmployee) | | company_id | string | | (reference to HrisCompany) | | raw | any | | | ### HrisEmployee | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | first_name | string | | | | last_name | string | | | | emails | HrisEmail[] | | | | title | string | | | | manager_id | string | | | | employment_status | string enum: ACTIVE, INACTIVE | | | | gender | string enum: MALE, FEMALE, INTERSEX, TRANS, NON_BINARY | | | | telephones | HrisTelephone[] | | | | date_of_birth | string (date) | | YYYYMM/DD | | employee_number | string | | the company's ID for this employee | | hired_at | string (date) | | The employee's start date (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | terminated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | termination_reason | string | | | | marital_status | string enum: MARRIED, SINGLE | | | | employment_type | string enum: FULL_TIME, PART_TIME, CONTRACTOR, INTERN, CONSULTANT, VOLUNTEER, CASUAL, SEASONAL, FREELANCE, OTHER | | | | address | HrisEmployee_address | | | | language_locale | string | | | | currency | string | | | | timezone | string | | | | image_url | string | | This link expires after 1 hour. | | company_id | string | | (reference to HrisCompany) | | pronouns | string | | | | employee_roles | HrisEmployee_employee_roles | | | | compensation | HrisCompensation[] | | | | salutation | string | | Mr. Mrs. Ms. | | bio | string | | | | ssn_sin | string | | | | groups | HrisGroup[] | | Which groups/teams/units that this employee/user belongs to. May not have all of the Group fields present, but should have id, name, or email. | | locations | HrisLocation[] | | | | metadata | HrisMetadata[] | | | | storage_quota_allocated | number | | | | storage_quota_used | number | | | | storage_quota_available | number | | | | relationships | HrisEmployeerelationship[] | | the employee's personal relationships (eg. emergency contacts, spouse, dependants, ...) | | has_mfa | boolean | | does the user/employee have multi-factor authentication enabled | | timeoff_days_total | number | | Total time off allowance for the current leave period, in days (when reported by the provider) | | timeoff_days_used | number | | Time off days used or booked in the current leave period (when reported by the provider) | | raw | any | | | ### HrisGroup | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | parent_id | string | | | | type | string enum: TEAM, GROUP, DEPARTMENT, DIVISION, BUSINESS_UNIT, BRANCH, SUB_DEPARTMENT | | | | user_ids | HrisGroup_user_ids | | (reference to HrisEmployee) | | manager_ids | HrisGroup_manager_ids | | | | is_active | boolean | | | | company_id | string | | (reference to HrisCompany) | | raw | any | | | ### HrisLocation | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | address | HrisLocation_address | | | | parent_id | string | | | | external_identifier | string | | | | telephones | HrisTelephone[] | | | | timezone | string | | | | currency | string | | | | language_locale | string | | | | is_active | boolean | | | | is_hq | boolean | | if this location is the headquarters of the company | | company_id | string | | (reference to HrisCompany) | | raw | any | | | ### HrisPayslip | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | user_id | string | | the employee (reference to HrisEmployee) | | company_id | string | | (reference to HrisCompany) | | payment_reference | string | | | | payment_type | string enum: DIRECT, CHEQUE, CASH | | | | paid_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | currency | string | | | | gross_amount | number | | | | net_amount | number | | | | details | HrisPayslipDetail[] | | | | deduction | HrisPayslip_deduction | | The ID (and optionally name) of the employee deduction (if this detail represents a deduction) | | raw | any | | | ### HrisTaxonomy | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | type | string enum: SKILL, KNOWLEDGE, COMPETENCE, ABILITY, CERTIFICATION, ROLE | | the kind of taxonomy item: skill, knowledge, competence, ability, certification or role/occupation | | name | string | | the preferred label of the skill/role | | description | string | | | | domain | string | | 1st-level grouping (broadest) | | category | string | | 2nd-level grouping | | subcategory | string | | 3rd-level grouping | | parent_id | string | | id of the parent taxonomy item, for providers exposing arbitrary-depth trees | | alternative_names | HrisTaxonomy_alternative_names | | synonyms / alternative labels | | url | string | | external reference URL for this item | | role_ids | HrisTaxonomy_role_ids | | ids of hris_taxonomy items of type ROLE that this skill relates to | | is_active | boolean | | | | raw | any | | | ### HrisTimeoff | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | user_id | string | Yes | employee ID (reference to HrisEmployee) | | company_id | string | | (reference to HrisCompany) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | is_paid | boolean | | | | status | string enum: APPROVED, PENDING, DENIED, CANCELLED | | | | approver_user_id | string | | (reference to HrisEmployee) | | approved_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | comments | string | | | | reason | string | | | | type | string enum: VACATION, SICK, HOLIDAY, BEREAVEMENT, PARENTAL, UNPAID, IN_LIEU, OTHER | | | | duration | number | | | | duration_type | string enum: HOUR, DAY | | | | raw | any | | | ### HrisTimeshift | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | employee_user_id | string | Yes | (reference to HrisEmployee) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | location_id | string | | (reference to HrisLocation) | | company_id | string | | (reference to HrisCompany) | | group_id | string | | department, team, etc. (reference to HrisGroup) | | compensation | HrisCompensation[] | | | | approver_user_id | string | | (reference to HrisEmployee) | | approved_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | hours | number | | | | is_approved | boolean | | | | raw | any | | | ### HrisCompany_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | ### HrisCompensation | Field | Type | Required | Description | |---|---|---|---| | type | string enum: SALARY, BONUS, STOCK_OPTIONS, EQUITY, OTHER | | | | amount | number | | | | currency | string | | | | frequency | string enum: ONE_TIME, DAY, QUARTER, YEAR, HOUR, MONTH, WEEK | | | | group_id | string | | (reference to HrisGroup) | | notes | string | | | ### HrisDevice_admin_user_ids ### HrisDevice_user_ids users who have this device ### HrisEmail | Field | Type | Required | Description | |---|---|---|---| | email | string | Yes | | | type | string enum: WORK, HOME, OTHER | | | ### HrisEmployee_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | ### HrisEmployee_employee_roles ### HrisEmployeerelationship | Field | Type | Required | Description | |---|---|---|---| | name | string | | | | first_name | string | | | | last_name | string | | | | emails | HrisEmail[] | | | | telephones | HrisTelephone[] | | | | address | HrisEmployeerelationship_address | | | | type | string enum: EMERGENCY, SPOUSE, CHILD, PARENT, SIBLING, FRIEND, OTHER | | | | is_dependent | boolean | | | | is_beneficiary | boolean | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### HrisEmployeerelationship_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | ### HrisGroup_manager_ids ### HrisGroup_user_ids ### HrisLocation_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | ### HrisMetadata | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | value | any | | | | namespace | string | | | | format | string enum: TEXT, NUMBER, DATE, BOOLEAN, FILE, TEXTAREA, SINGLE_SELECT, MULTIPLE_SELECT, MEASUREMENT, PRICE, YES_NO, CURRENCY, URL | | | | extra_data | any | | | | slug | string | | | ### HrisPayslipDetail | Field | Type | Required | Description | |---|---|---|---| | type | string enum: EARNING_SALARY, EARNING_OVERTIME, EARNING_TIP, EARNING_BONUS, EARNING_COMMISSION, EARNING_ADJUSTMENT, EARNING, PRETAX_DEDUCTION, PRETAX_DEDUCTION_HEALTH_INSURANCE, PRETAX_DEDUCTION_RETIREMENT, PRETAX_DEDUCTION_HRA, TAX_FEDERAL, TAX_REGION, TAX_LOCAL, POSTTAX_BENEFIT, POSTTAX_GARNISHMENT, REIMBURSEMENT | | | | name | string | | | | description | string | | | | amount | number | Yes | | | employee_amount | number | | | | company_amount | number | | | ### HrisPayslip_deduction The ID (and optionally name) of the employee deduction (if this detail represents a deduction) | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | user_id | string | | employee ID (reference to HrisEmployee) | | company_id | string | | (reference to HrisCompany) | | benefit_id | string | | | | amount | number | | percentage or absolute amount (employee's portion) | | type | string enum: FIXED, PERCENTAGE | | | | coverage_level | string enum: EMPLOYEE_ONLY, EMPLOYEE_SPOUSE, EMPLOYEE_CHILD, EMPLOYEE_CHILDREN, EMPLOYEE_FAMILY, FAMILY, OTHER | | Level selected by employee (e.g. "FAMILY", "EMPLOYEE_ONLY", or other) | | frequency | string enum: ONE_TIME, DAY, QUARTER, YEAR, HOUR, MONTH, WEEK | | Frequency for this deduction (should always be set, matches IHrisBenefit.frequency) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | is_active | boolean | | | | notes | string | | | | raw | any | | | ### HrisTaxonomy_alternative_names synonyms / alternative labels ### HrisTaxonomy_role_ids ids of hris_taxonomy items of type ROLE that this skill relates to ### HrisTelephone | Field | Type | Required | Description | |---|---|---|---| | telephone | string | Yes | | | type | string enum: WORK, HOME, OTHER, FAX, MOBILE | | | --- # Kms API – Endpoints & Data Models ## Kms API data models URL: https://docs.unified.to/kms/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /kms/{connection_id}/space | Create a space | | GET | /kms/{connection_id}/space | List all spaces | | GET | /kms/{connection_id}/space/{id} | Retrieve a space | | PUT | /kms/{connection_id}/space/{id} | Update a space | | DELETE | /kms/{connection_id}/space/{id} | Remove a space | | POST | /kms/{connection_id}/page | Create a page | | GET | /kms/{connection_id}/page | List all pages | | GET | /kms/{connection_id}/page/{id} | Retrieve a page | | PUT | /kms/{connection_id}/page/{id} | Update a page | | DELETE | /kms/{connection_id}/page/{id} | Remove a page | | POST | /kms/{connection_id}/comment | Create a comment | | GET | /kms/{connection_id}/comment | List all comments | | GET | /kms/{connection_id}/comment/{id} | Retrieve a comment | | PUT | /kms/{connection_id}/comment/{id} | Update a comment | | DELETE | /kms/{connection_id}/comment/{id} | Remove a comment | #### Data Models ### KmsComment | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | type | string enum: PAGE_INLINE, PAGE | | | | content_type | string enum: HTML, MARKDOWN, TEXT, OTHER | | | | content | string | | | | user_id | string | | (reference to HrisEmployee) | | page_id | string | | (reference to KmsPage) | | parent_id | string | | | | raw | any | | | ### KmsPage | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | title | string | | | | type | string enum: HTML, MARKDOWN, TEXT, OTHER | Yes | | | space_id | string | | (reference to KmsSpace) | | parent_id | string | | | | is_active | boolean | | | | user_id | string | | (reference to HrisEmployee) | | download_url | string | | | | metadata | KmsPageMetadata[] | | | | has_children | boolean | | | | web_url | string | | | | raw | any | | | ### KmsSpace | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | parent_id | string | | | | is_active | boolean | | | | user_id | string | | (reference to HrisEmployee) | | parent_page_id | string | | (reference to KmsPage) | | raw | any | | | ### KmsPageMetadata | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | value | any | | | | namespace | string | | | | format | string enum: TEXT, NUMBER, DATE, BOOLEAN, FILE, TEXTAREA, SINGLE_SELECT, MULTIPLE_SELECT, MEASUREMENT, PRICE, YES_NO, CURRENCY, URL, PERCENT, EMAIL, PHONE, REFERENCE, TIME | | | | extra_data | any | | | | slug | string | | | --- # Lms API – Endpoints & Data Models ## Lms API data models URL: https://docs.unified.to/lms/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /lms/{connection_id}/course | Create a course | | GET | /lms/{connection_id}/course | List all courses | | GET | /lms/{connection_id}/course/{id} | Retrieve a course | | PUT | /lms/{connection_id}/course/{id} | Update a course | | DELETE | /lms/{connection_id}/course/{id} | Remove a course | | POST | /lms/{connection_id}/class | Create a class | | GET | /lms/{connection_id}/class | List all classes | | GET | /lms/{connection_id}/class/{id} | Retrieve a class | | PUT | /lms/{connection_id}/class/{id} | Update a class | | DELETE | /lms/{connection_id}/class/{id} | Remove a class | | POST | /lms/{connection_id}/student | Create a student | | GET | /lms/{connection_id}/student | List all students | | GET | /lms/{connection_id}/student/{id} | Retrieve a student | | PUT | /lms/{connection_id}/student/{id} | Update a student | | DELETE | /lms/{connection_id}/student/{id} | Remove a student | | POST | /lms/{connection_id}/instructor | Create an instructor | | GET | /lms/{connection_id}/instructor | List all instructors | | GET | /lms/{connection_id}/instructor/{id} | Retrieve an instructor | | PUT | /lms/{connection_id}/instructor/{id} | Update an instructor | | DELETE | /lms/{connection_id}/instructor/{id} | Remove an instructor | | POST | /lms/{connection_id}/content | Create a content | | GET | /lms/{connection_id}/content | List all contents | | GET | /lms/{connection_id}/content/{id} | Retrieve a content | | PUT | /lms/{connection_id}/content/{id} | Update a content | | DELETE | /lms/{connection_id}/content/{id} | Remove a content | | POST | /lms/{connection_id}/collection | Create a collection | | GET | /lms/{connection_id}/collection | List all collections | | GET | /lms/{connection_id}/collection/{id} | Retrieve a collection | | PUT | /lms/{connection_id}/collection/{id} | Update a collection | | DELETE | /lms/{connection_id}/collection/{id} | Remove a collection | | POST | /lms/{connection_id}/activity | Create an activity | | GET | /lms/{connection_id}/activity | List all activities | | GET | /lms/{connection_id}/activity/{id} | Retrieve an activity | | PUT | /lms/{connection_id}/activity/{id} | Update an activity | | DELETE | /lms/{connection_id}/activity/{id} | Remove an activity | #### Data Models ### LmsActivity | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | content_id | string | | | | course_id | string | | For providers that track at course level (e.g., Learnupon enrollments) (reference to Course) | | student_id | string | | (reference to LmsStudent) | | duration_minutes | number | | | | assigned_grade | string | | | | is_completed | boolean | | | | progress_percentage | number | | | | started_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | completed_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | raw | any | | | ### LmsClass | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | media | LmsMedia[] | | | | course_id | string | | (reference to Course) | | instructor_ids | LmsClass_instructor_ids | | (reference to LmsInstructor) | | student_ids | LmsClass_student_ids | | (reference to LmsStudent) | | languages | LmsClass_languages | | | | raw | any | | | ### LmsCollection | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | media | LmsMedia[] | | | | is_active | boolean | | | | parent_id | string | | | | raw | any | | | ### LmsContent | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | external_reference | string | | | | course_ids | LmsContent_course_ids | | | | name | string | | | | description | string | | | | languages | LmsContent_languages | | | | media | LmsMedia[] | | | | is_active | boolean | | | | duration_minutes | number | | | | categories | LmsContent_categories | | | | subjects | LmsSubject[] | | Topic taxonomy as {name, rank} pairs carrying the full ancestor chain (rank = depth, 0 = top level) | | skills | LmsContent_skills | | | | tags | LmsContent_tags | | | | difficulty | string | | Difficulty or level of the content (e.g. Beginner, Intermediate, Advanced) | | sort_order | number | | | | provider_name | string | | | | short_description | string | | | | published_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | localizations | LmsContentLocalization[] | | | | instructor_ids | LmsContent_instructor_ids | | (reference to LmsInstructor) | | collection_ids | LmsContent_collection_ids | | (reference to CommerceCollection) | | raw | any | | | ### LmsCourse | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | is_private | boolean | | | | is_active | boolean | | | | price_amount | number | | How much the course costs | | languages | LmsCourse_languages | | | | categories | LmsCourse_categories | | | | currency | string | | The currency code (ISO) of the course price | | media | LmsMedia[] | | | | instructor_ids | LmsCourse_instructor_ids | | @deprecated; use instructors (reference to LmsInstructor) | | instructors | LmsInstructor[] | | | | student_ids | LmsCourse_student_ids | | @deprecated; use students (reference to LmsStudent) | | students | LmsStudent[] | | | | content_ids | LmsCourse_content_ids | | | | skills | LmsCourse_skills | | | | duration_minutes | number | | | | provider_name | string | | | | raw | any | | | | published_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | time_estimate_minutes | number | | | ### LmsInstructor | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | first_name | string | | | | last_name | string | | | | emails | LmsEmail[] | | | | title | string | | | | telephones | LmsTelephone[] | | | | image_url | string | | | | raw | any | | | ### LmsStudent | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | first_name | string | | | | last_name | string | | | | emails | LmsEmail[] | | | | telephones | LmsTelephone[] | | | | address | LmsStudent_address | | | | image_url | string | | | | raw | any | | | ### LmsClass_instructor_ids ### LmsClass_languages ### LmsClass_student_ids ### LmsContentLocalization | Field | Type | Required | Description | |---|---|---|---| | name | string | | | | description | string | | | | language | string | | | ### LmsContent_categories ### LmsContent_collection_ids ### LmsContent_course_ids ### LmsContent_instructor_ids ### LmsContent_languages ### LmsContent_skills ### LmsContent_tags ### LmsCourse_categories ### LmsCourse_content_ids ### LmsCourse_instructor_ids @deprecated; use instructors ### LmsCourse_languages ### LmsCourse_skills ### LmsCourse_student_ids @deprecated; use students ### LmsEmail ### LmsMedia | Field | Type | Required | Description | |---|---|---|---| | url | string | | | | name | string | | | | description | string | | | | type | string enum: IMAGE, HEADSHOT, VIDEO, WEB, DOCUMENT, TEXT, HTML, OTHER | | | | thumbnail_url | string | | | | content | string | | Inline text/HTML/markdown content (use when url is empty or for embedded content) | | languages | LmsMedia_languages | | ISO 2-digit language codes | ### LmsMedia_languages ISO 2-digit language codes ### LmsStudent_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | ### LmsSubject | Field | Type | Required | Description | |---|---|---|---| | name | string | | | | rank | number | | Depth of this subject in the provider's topic hierarchy (0 = top level) | ### LmsTelephone | Field | Type | Required | Description | |---|---|---|---| | telephone | string | Yes | | | type | string enum: WORK, HOME, OTHER, FAX, MOBILE | | | --- # Martech API – Endpoints & Data Models ## Martech API data models URL: https://docs.unified.to/martech/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /martech/{connection_id}/list | Create a list | | GET | /martech/{connection_id}/list | List all lists | | GET | /martech/{connection_id}/list/{id} | Retrieve a list | | PUT | /martech/{connection_id}/list/{id} | Update a list | | DELETE | /martech/{connection_id}/list/{id} | Remove a list | | POST | /martech/{connection_id}/member | Create a member | | GET | /martech/{connection_id}/member | List all members | | GET | /martech/{connection_id}/member/{id} | Retrieve a member | | PUT | /martech/{connection_id}/member/{id} | Update a member | | DELETE | /martech/{connection_id}/member/{id} | Remove a member | | POST | /martech/{connection_id}/campaign | Create a campaign | | GET | /martech/{connection_id}/campaign | List all campaigns | | GET | /martech/{connection_id}/campaign/{id} | Retrieve a campaign | | PUT | /martech/{connection_id}/campaign/{id} | Update a campaign | | DELETE | /martech/{connection_id}/campaign/{id} | Remove a campaign | | GET | /martech/{connection_id}/report | List all reports | #### Data Models ### MarketingCampaign | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for the campaign | | created_at | string (date) | | When the campaign was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | When the campaign was last updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | Campaign nametitle | | type | string | | Campaign type (e.g., regular, automation, ab_test) | | status | string enum: DRAFT, SCHEDULED, SENDING, SENT, CANCELLED, PAUSED, ARCHIVED, OTHER | | Campaign status | | list_ids | MarketingCampaign_list_ids | | Associated listaudience IDs | | subject_line | string | | Email subject line | | preview_text | string | | Email preview text | | from_name | string | | Sender name | | from_email | string | | Sender email address | | reply_to_email | string | | Reply-to email address | | send_at | string (date) | | Scheduled send time (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | raw | any | | Raw data from integration | ### MarketingList Mailing List | Field | Type | Required | Description | |---|---|---|---| | id | string | | Identifier for this list | | created_at | string (date) | | Date that this list was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The list's name | | description | string | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | user_id | string | | The user who created this list (reference to HrisEmployee) | | is_active | boolean | | | | address | MarketingList_address | | | | subject | string | | | | sender_name | string | | | | sender_email | string | | | | language | string | | | | sender_company | string | | | | sender_phone | string | | | | raw | any | | The raw data returned by the integration for this list | ### MarketingMember A member represents a person | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this member object | | created_at | string (date) | | The date that this member object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this member object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the member | | first_name | string | | | | last_name | string | | | | company | string | | The name of the company/organization the member belongs to | | emails | MarketingEmail[] | | An array of email addresses for this member | | list_ids | MarketingMember_list_ids | | An array of list IDs associated with this member | | tags | MarketingMember_tags | | An array of tags associated with this member | | status | string enum: SUBSCRIBED, UNSUBSCRIBED, CLEANED, PENDING, TRANSACTIONAL | | | | raw | any | | The raw data returned by the integration for this member | ### MarketingCampaign_list_ids Associated listaudience IDs ### MarketingEmail | Field | Type | Required | Description | |---|---|---|---| | email | string | Yes | | | type | string enum: WORK, HOME, OTHER | | | ### MarketingList_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | | ### MarketingMember_list_ids An array of list IDs associated with this member ### MarketingMember_tags An array of tags associated with this member --- # Messaging API – Endpoints & Data Models ## Messaging API data models URL: https://docs.unified.to/messaging/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /messaging/{connection_id}/channel | Create a channel | | GET | /messaging/{connection_id}/channel | List all channels | | GET | /messaging/{connection_id}/channel/{id} | Retrieve a channel | | PUT | /messaging/{connection_id}/channel/{id} | Update a channel | | DELETE | /messaging/{connection_id}/channel/{id} | Remove a channel | | POST | /messaging/{connection_id}/message | Create a message | | GET | /messaging/{connection_id}/message | List all messages | | GET | /messaging/{connection_id}/message/{id} | Retrieve a message | | PUT | /messaging/{connection_id}/message/{id} | Update a message | | DELETE | /messaging/{connection_id}/message/{id} | Remove a message | | PUT | /messaging/{connection_id}/event/{id} | Update an event | #### Data Models ### MessagingChannel | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | parent_id | string | | | | has_subchannels | boolean | | | | members | MessagingMember[] | | | | is_active | boolean | | | | is_private | boolean | | | | web_url | string | | | | raw | any | | | ### MessagingEvent | Field | Type | Required | Description | |---|---|---|---| | id | string | | not used | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | type | string enum: MESSAGE_RECEIVED, REACTION_ADDED, REACTION_REMOVED, BUTTON_CLICK, APP_MENTION, CHANNEL_JOINED, CHANNEL_LEFT, CHANNEL_CREATED, CHANNEL_DELETED, CHANNEL_RENAMED, USER_CREATED, USER_DELETED, USER_UPDATED | | | | channel | MessagingEvent_channel | | | | message | MessagingEvent_message | | | | button | MessagingEvent_button | | | | user | MessagingEvent_user | | | | raw | any | | | | is_replacing_original | boolean | | | ### MessagingMessage | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | channel_id | string | | @deprecated (reference to MessagingChannel) | | channel_ids | MessagingMessage_channel_ids | | @deprecated; use channels instead (reference to MessagingChannel) | | channels | MessagingReference[] | | Represents the names of all channels to which the message is sent. Identifies the channels where the message is posted. | | parent_id | string | | Represents the ID of the immediate predecessor message in the thread. Identifies a specific message to which the current message directly replies. | | root_message_id | string | | @deprecated | | message_thread_identifier | string | | the opaque identifier for the first message in a thread | | author_member | MessagingMessage_author_member | | for email systems, this field represents the From value | | destination_members | MessagingMember[] | | for email systems, this field represents the To value | | hidden_members | MessagingMember[] | | for email systems, this field represents the BCC value | | mentioned_members | MessagingMember[] | | for email systems, this field represents the CC value | | reactions | MessagingReaction[] | | | | subject | string | | | | message | string | | | | message_html | string | | | | message_markdown | string | | | | attachments | MessagingAttachment[] | | | | web_url | string | | | | reference | string | | eg. RFC822 MessageID | | has_children | boolean | | | | is_unread | boolean | | | | buttons | MessagingButton[] | | | | raw | any | | | ### MessagingAttachment | Field | Type | Required | Description | |---|---|---|---| | filename | string | | | | size | number | | | | content_identifier | string | | the inline attachment's content_id | | content_type | string | | | | download_url | string | | | | message_id | string | | | ### MessagingButton | Field | Type | Required | Description | |---|---|---|---| | id | string | Yes | | | text | string | | | | icon | string | | | ### MessagingEvent_button | Field | Type | Required | Description | |---|---|---|---| | id | string | Yes | | | text | string | | | | icon | string | | | ### MessagingEvent_channel | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | parent_id | string | | | | has_subchannels | boolean | | | | members | MessagingMember[] | | | | is_active | boolean | | | | is_private | boolean | | | | web_url | string | | | | raw | any | | | ### MessagingEvent_message | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | channel_id | string | | @deprecated (reference to MessagingChannel) | | channel_ids | MessagingEvent_message_channel_ids | | @deprecated; use channels instead (reference to MessagingChannel) | | channels | MessagingReference[] | | Represents the names of all channels to which the message is sent. Identifies the channels where the message is posted. | | parent_id | string | | Represents the ID of the immediate predecessor message in the thread. Identifies a specific message to which the current message directly replies. | | root_message_id | string | | @deprecated | | message_thread_identifier | string | | the opaque identifier for the first message in a thread | | author_member | MessagingEvent_message_author_member | | for email systems, this field represents the From value | | destination_members | MessagingMember[] | | for email systems, this field represents the To value | | hidden_members | MessagingMember[] | | for email systems, this field represents the BCC value | | mentioned_members | MessagingMember[] | | for email systems, this field represents the CC value | | reactions | MessagingReaction[] | | | | subject | string | | | | message | string | | | | message_html | string | | | | message_markdown | string | | | | attachments | MessagingAttachment[] | | | | web_url | string | | | | reference | string | | eg. RFC822 MessageID | | has_children | boolean | | | | is_unread | boolean | | | | buttons | MessagingButton[] | | | | raw | any | | | ### MessagingEvent_message_author_member for email systems, this field represents the From value | Field | Type | Required | Description | |---|---|---|---| | user_id | string | | (reference to HrisEmployee) | | email | string | | | | name | string | | | | image_url | string | | | ### MessagingEvent_message_channel_ids @deprecated; use channels instead ### MessagingEvent_user | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | ### MessagingMember | Field | Type | Required | Description | |---|---|---|---| | user_id | string | | (reference to HrisEmployee) | | email | string | | | | name | string | | | | image_url | string | | | ### MessagingMessage_author_member for email systems, this field represents the From value | Field | Type | Required | Description | |---|---|---|---| | user_id | string | | (reference to HrisEmployee) | | email | string | | | | name | string | | | | image_url | string | | | ### MessagingMessage_channel_ids @deprecated; use channels instead ### MessagingReaction | Field | Type | Required | Description | |---|---|---|---| | reaction | string | Yes | | | member | MessagingReaction_member | Yes | | ### MessagingReaction_member | Field | Type | Required | Description | |---|---|---|---| | user_id | string | | (reference to HrisEmployee) | | email | string | | | | name | string | | | | image_url | string | | | ### MessagingReference | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | | --- # Metadata API – Endpoints & Data Models ## Metadata API data models URL: https://docs.unified.to/metadata/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /metadata/{connection_id}/metadata | Create a metadata | | GET | /metadata/{connection_id}/metadata | List all metadatas | | GET | /metadata/{connection_id}/metadata/{id} | Retrieve a metadata | | PUT | /metadata/{connection_id}/metadata/{id} | Update a metadata | | DELETE | /metadata/{connection_id}/metadata/{id} | Remove a metadata | #### Data Models ### MetadataMetadata | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | Yes | | | slug | string | | | | format | string enum: TEXT, NUMBER, DATE, BOOLEAN, FILE, TEXTAREA, SINGLE_SELECT, MULTIPLE_SELECT, MEASUREMENT, PRICE, YES_NO, CURRENCY, URL | | | | original_format | string | | | | options | MetadataMetadata_options | | | | object_type | string | Yes | This is the unified object type that this metadata is associated with. eg. crm_contact or coomerce_item | | objects | MetadataMetadata_objects | | | | is_required | boolean | | | | raw | any | | | ### MetadataMetadata_objects ### MetadataMetadata_options --- # Passthrough API – Endpoints & Data Models ## Passthrough API data models URL: https://docs.unified.to/passthrough/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /passthrough/{connection_id}/{path} | Passthrough POST | | PUT | /passthrough/{connection_id}/{path} | Passthrough PUT | | GET | /passthrough/{connection_id}/{path} | Passthrough GET | | DELETE | /passthrough/{connection_id}/{path} | Passthrough DELETE | #### Data Models ### Payload integration-specific payload --- # Payment API – Endpoints & Data Models ## Payment API data models URL: https://docs.unified.to/payment/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /payment/{connection_id}/payment | Create a payment | | GET | /payment/{connection_id}/payment | List all payments | | GET | /payment/{connection_id}/payment/{id} | Retrieve a payment | | PUT | /payment/{connection_id}/payment/{id} | Update a payment | | DELETE | /payment/{connection_id}/payment/{id} | Remove a payment | | POST | /payment/{connection_id}/link | Create a link | | GET | /payment/{connection_id}/link | List all links | | GET | /payment/{connection_id}/link/{id} | Retrieve a link | | PUT | /payment/{connection_id}/link/{id} | Update a link | | DELETE | /payment/{connection_id}/link/{id} | Remove a link | | GET | /payment/{connection_id}/refund/{id} | Retrieve a refund | | GET | /payment/{connection_id}/refund | List all refunds | | GET | /payment/{connection_id}/payout/{id} | Retrieve a payout | | GET | /payment/{connection_id}/payout | List all payouts | | POST | /payment/{connection_id}/subscription | Create a subscription | | GET | /payment/{connection_id}/subscription | List all subscriptions | | GET | /payment/{connection_id}/subscription/{id} | Retrieve a subscription | | PUT | /payment/{connection_id}/subscription/{id} | Update a subscription | | DELETE | /payment/{connection_id}/subscription/{id} | Remove a subscription | #### Data Models ### PaymentLink | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | is_active | boolean | | | | lineitems | PaymentLineitem[] | | | | currency | string | | | | amount | number | | | | payment_id | string | | (reference to PaymentPayment) | | contact_id | string | | ID of the AccountingContact (reference to AccountingContact) | | url | string | | the payment link | | description | string | | | | is_chargeable_now | boolean | | | | success_url | string | | | | raw | any | | | ### PaymentPayment | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_amount | number | | | | contact_id | string | | (reference to AccountingContact) | | payment_method | string | | | | type | string enum: INVOICE, BILL | | | | currency | string | | | | notes | string | | | | invoice_id | string | | (reference to AccountingInvoice) | | bill_id | string | | references AccountingBill | | link_id | string | | | | account_id | string | | (reference to AccountingAccount) | | reference | string | | | | organization_id | string | | reference to an AccountingOrganization | | raw | any | | | | allocations | PaymentAllocation[] | | What this payment was applied to (invoices, bills, credit memos, etc.). Replaces separate invoice/bill payment endpoints. | ### PaymentPayout | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_amount | number | | | | currency | string | | | | notes | string | | | | status | string enum: SUCCEEDED, PENDING, FAILED, CANCELED | | | | raw | any | | | ### PaymentRefund | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_amount | number | | | | payment_id | string | | (reference to PaymentPayment) | | currency | string | | | | notes | string | | | | status | string enum: SUCCEEDED, PENDING, FAILED, CANCELED | | | | reference | string | | | | raw | any | | | ### PaymentSubscription | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | description | string | | | | contact_id | string | | a reference to an Accounting Contact (reference to AccountingContact) | | invoice_id | string | | a reference to an Accounting Invoice (reference to AccountingInvoice) | | current_period_end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | current_period_start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | canceled_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | currency | string | | | | total_amount | number | | | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | status | string enum: ACTIVE, INACTIVE, CANCELED, PAUSED | | | | month | number | | the month on which to charge a customer (1-12). Only applies when the interval_unit is YEAR. | | interval_unit | string enum: YEAR, MONTH, WEEK, DAY | | | | day_of_month | number | | The day of the month to charge customers on. 1-28 or -1 to indicate the last day of the month. Only applies when the interval_unit is MONTH. | | day_of_week | number | | The day of the week to charge customers on. 1-7 or -1 to indicate the last day of the week. Only applies when the interval_unit is WEEK. | | interval | number | | The number of intervals between charges within the interval_unit. Defaults to 1. | | lineitems | PaymentLineitem[] | | | | raw | any | | | ### PaymentAllocation | Field | Type | Required | Description | |---|---|---|---| | object_type | string enum: INVOICE, BILL, CREDITMEMO, VENDORCREDIT, SALESORDER, PURCHASEORDER | | The type of object this payment was applied to | | object_id | string | | The id of the object this payment was applied to | | amount | number | | The amount of the payment applied to this object | | currency | string | | ISO 4217 currency code of the applied amount | | exchange_rate | number | | The exchange rate at the time the payment was applied, for multi-currency | | allocated_at | string (date) | | Date and time when the payment was applied, in ISO 8601 format and UTC (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | ### PaymentLineitem | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | refunded_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | total_amount | number | | | | refund_amount | number | | new field | | discount_amount | number | | | | tax_amount | number | | | | item_id | string | | (reference to CommerceItem) | | unit_amount | number | | | | unit_quantity | number | | | | item_sku | string | | | | item_name | string | | | | item_description | string | | | | notes | string | | | | taxrate_id | string | | | | account_id | string | | (reference to AccountingAccount) | --- # Repo API – Endpoints & Data Models ## Repo API data models URL: https://docs.unified.to/repo/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /repo/{connection_id}/organization | Create an organization | | GET | /repo/{connection_id}/organization | List all organizations | | GET | /repo/{connection_id}/organization/{id} | Retrieve an organization | | PUT | /repo/{connection_id}/organization/{id} | Update an organization | | DELETE | /repo/{connection_id}/organization/{id} | Remove an organization | | POST | /repo/{connection_id}/repository | Create a repository | | GET | /repo/{connection_id}/repository | List all repositories | | GET | /repo/{connection_id}/repository/{id} | Retrieve a repository | | PUT | /repo/{connection_id}/repository/{id} | Update a repository | | DELETE | /repo/{connection_id}/repository/{id} | Remove a repository | | POST | /repo/{connection_id}/branch | Create a branch | | GET | /repo/{connection_id}/branch | List all branches | | GET | /repo/{connection_id}/branch/{id} | Retrieve a branch | | PUT | /repo/{connection_id}/branch/{id} | Update a branch | | DELETE | /repo/{connection_id}/branch/{id} | Remove a branch | | POST | /repo/{connection_id}/commit | Create a commit | | GET | /repo/{connection_id}/commit | List all commits | | GET | /repo/{connection_id}/commit/{id} | Retrieve a commit | | PUT | /repo/{connection_id}/commit/{id} | Update a commit | | DELETE | /repo/{connection_id}/commit/{id} | Remove a commit | | POST | /repo/{connection_id}/pullrequest | Create a pullrequest | | GET | /repo/{connection_id}/pullrequest | List all pullrequests | | GET | /repo/{connection_id}/pullrequest/{id} | Retrieve a pullrequest | | PUT | /repo/{connection_id}/pullrequest/{id} | Update a pullrequest | | DELETE | /repo/{connection_id}/pullrequest/{id} | Remove a pullrequest | #### Data Models ### RepoBranch | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | Yes | | | repo_id | string | Yes | (reference to RepoRepository) | | raw | any | | | ### RepoCommit | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | user_id | string | | (reference to HrisEmployee) | | repo_id | string | Yes | (reference to RepoRepository) | | message | string | | | | branch_id | string | | (reference to RepoBranch) | | pullrequest_ids | RepoCommit_pullrequest_ids | | | | lines_added | number | | | | lines_deleted | number | | | | lines_changed | number | | | | raw | any | | | ### RepoOrganization | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | avatar_url | string | | | | web_url | string | | | | user_ids | RepoOrganization_user_ids | | id values of the users/employees associated with this organization (reference to HrisEmployee) | | raw | any | | | ### RepoPullrequest | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | user_ids | RepoPullrequest_user_ids | | (reference to HrisEmployee) | | repo_id | string | | (reference to RepoRepository) | | title | string | | | | notes | string | | | | target_branch_id | string | | | | source_branch_id | string | | | | status | string enum: PENDING, APPROVED, REJECTED | | | | labels | RepoPullrequest_labels | | | | closed_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | commit_ids | RepoPullrequest_commit_ids | | (reference to RepoCommit) | | raw | any | | | ### RepoRepository | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | Yes | | | description | string | | | | owner | string | | | | is_private | boolean | | | | web_url | string | | | | org_id | string | | (reference to RepoOrganization) | | raw | any | | | ### RepoCommit_pullrequest_ids ### RepoOrganization_user_ids id values of the users/employees associated with this organization ### RepoPullrequest_commit_ids ### RepoPullrequest_labels ### RepoPullrequest_user_ids --- # Scim API – Endpoints & Data Models ## Scim API data models URL: https://docs.unified.to/scim/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /scim/{connection_id}/users | Create user | | GET | /scim/{connection_id}/users | List users | | GET | /scim/{connection_id}/users/{id} | Get user | | PUT | /scim/{connection_id}/users/{id} | Update user | | DELETE | /scim/{connection_id}/users/{id} | Delete user | | POST | /scim/{connection_id}/groups | Create group | | GET | /scim/{connection_id}/groups | List groups | | GET | /scim/{connection_id}/groups/{id} | Get group | | PUT | /scim/{connection_id}/groups/{id} | Update group | | DELETE | /scim/{connection_id}/groups/{id} | Delete group | #### Data Models ### ScimGroup | Field | Type | Required | Description | |---|---|---|---| | id | string | | The group's unique id | | externalId | string | | A groups id in an external system | | displayName | string | Yes | The group's display name | | groupType | string | | Organization [NOT STANDARD] | | members | ScimGroupMember[] | | An array of members | | meta | ScimGroup_meta | | | | schemas | ScimGroup_schemas | | Array of schema URIs | ### ScimUser | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | schemas | ScimUser_schemas | | | | externalId | string | | | | userType | string | | 'Employee','Super Admin', 'General Admin', 'Account Manager', 'User Manager', 'Workflow Manager', 'Experience Manager', 'Author','Reporter','Contractor,'Intern','Temp','External','Unknown'] | | userName | string | | | | meta | ScimUser_meta | | | | name | ScimUser_name | | | | displayName | string | | | | nickName | string | | | | profileUrl | string | | | | title | string | | | | preferredLanguage | string | | | | locale | string | | | | timezone | string | | | | active | boolean | | | | password | string | | | | phoneNumbers | ScimPhoneNumber[] | | | | emails | ScimEmail[] | | | | ims | ScimIms[] | | | | photos | ScimPhoto[] | | | | addresses | ScimAddress[] | | | | groups | ScimUserGroups[] | | | | entitlements | ScimEntitlement[] | | | | roles | ScimRole[] | | Student, Faculty, ... | | x509Certificates | ScimRole[] | | | | urn_ietf_params_scim_schemas_extension_enterprise_2_0_User | ScimUser_urn_ietf_params_scim_schemas_extension_enterprise_2_0_User | | an organization. | | urn_ietf_params_scim_schemas_extension_lattice_attributes_1_0_User | ScimUser_urn_ietf_params_scim_schemas_extension_lattice_attributes_1_0_User | | | | urn_ietf_params_scim_schemas_extension_peakon_2_0_User | ScimUser_urn_ietf_params_scim_schemas_extension_peakon_2_0_User | | | ### ScimAddress | Field | Type | Required | Description | |---|---|---|---| | formatted | string | | | | streetAddress | string | | could be multi-line | | locality | string | | city | | region | string | | stateprovince | | postalCode | string | | | | country | string | | | | type | string enum: work, home, other | | | ### ScimEmail | Field | Type | Required | Description | |---|---|---|---| | type | string enum: work, home, other | Yes | | | value | string | | | | primary | boolean | | | | display | string | | | ### ScimEntitlement | Field | Type | Required | Description | |---|---|---|---| | value | string | Yes | | | display | string | | | | type | string | | | | primary | boolean | | | ### ScimGroupMember | Field | Type | Required | Description | |---|---|---|---| | value | string | Yes | ID of user | | $ref | string | | url to user | | display | string | | user name | | type | string enum: User, Group | | NOT STANDARD | | operation | string enum: add, delete | | ONLY USED ON UPDATES | ### ScimGroup_meta | Field | Type | Required | Description | |---|---|---|---| | resourceType | string enum: User, Group | | | | created | string | | | | lastModified | string | | | | location | string | | | | version | string | | | ### ScimGroup_schemas Array of schema URIs ### ScimIms | Field | Type | Required | Description | |---|---|---|---| | type | string enum: aim, qtalk, icq, xmpp, msn, skype, qq, yahoo | | | | value | string | | | | display | string | | | | primary | boolean | | | ### ScimManager "id" attribute of another User. | Field | Type | Required | Description | |---|---|---|---| | value | string | | The "id" of the SCIM resource representing the user's manager. RECOMMENDED. | | managerId | string | | alias for value? | | $ref | string | | The URI of the SCIM resource representing the User's manager. RECOMMENDED. | | displayName | string | | The displayName of the user's manager. This attribute is OPTIONAL, and mutability is "readOnly". | | type | string enum: direct, indirect | | | ### ScimPhoneNumber | Field | Type | Required | Description | |---|---|---|---| | type | string enum: work, home, other, mobile, fax, pager | | | | value | string | | | | display | string | | | | primary | boolean | | | ### ScimPhoto | Field | Type | Required | Description | |---|---|---|---| | value | string | | URL | | display | string | | | | type | string enum: photo, thumbnail | | | | primary | boolean | | | ### ScimRole | Field | Type | Required | Description | |---|---|---|---| | value | string | Yes | value of the role | | display | string | | | | type | string | | | | primary | boolean | | | ### ScimUserGroups | Field | Type | Required | Description | |---|---|---|---| | value | string | Yes | ID | | $ref | string | | | | display | string | | | | type | string enum: direct, indirect | | | ### ScimUser_meta | Field | Type | Required | Description | |---|---|---|---| | resourceType | string enum: User, Group | | | | created | string | | | | lastModified | string | | | | location | string | | | | version | string | | | ### ScimUser_name | Field | Type | Required | Description | |---|---|---|---| | formatted | string | | | | familyName | string | | | | givenName | string | | | | middleName | string | | | | honorificPrefix | string | | | | honorificSuffix | string | | | ### ScimUser_schemas ### ScimUser_urn_ietf_params_scim_schemas_extension_enterprise_2_0_User an organization. | Field | Type | Required | Description | |---|---|---|---| | employeeNumber | string | | | | costCenter | string | | Identifies the name of a cost center. | | organization | string | | Identifies the name of an organization. | | division | string | | Identifies the name of a division. | | department | string | | Identifies the name of a department. | | manager | ScimUser_urn_ietf_params_scim_schemas_extension_enterprise_2_0_User_manager | | "id" attribute of another User. | | additionalManagers | ScimManager[] | | | | level | string | | | | startDate | string (date) | | 2011-03-25 | | endDate | string (date) | | 2022-02-25 | | birthday | string (date) | | 1985-07-20 | | gender | string enum: male, female | | | | location | string | | Berlin | | currency | string | | User currency | ### ScimUser_urn_ietf_params_scim_schemas_extension_enterprise_2_0_User_manager "id" attribute of another User. | Field | Type | Required | Description | |---|---|---|---| | value | string | | The "id" of the SCIM resource representing the user's manager. RECOMMENDED. | | managerId | string | | alias for value? | | $ref | string | | The URI of the SCIM resource representing the User's manager. RECOMMENDED. | | displayName | string | | The displayName of the user's manager. This attribute is OPTIONAL, and mutability is "readOnly". | | type | string enum: direct, indirect | | | ### ScimUser_urn_ietf_params_scim_schemas_extension_lattice_attributes_1_0_User | Field | Type | Required | Description | |---|---|---|---| | startDate | string (date) | | "2022-03-08", | | birthDate | string (date) | | "1987-10-06", | | gender | string enum: male, female | | | | sexualOrientation | string enum: Queer, Heterosexual, Straight | | | | ethnicity | string enum: Caucasian, East Asian, Middle Eastern, Black, Biracial (South Asian & Caucasian), Filipino, South Asian, Indian, White, Asian | | | | People_Manager__Reviews_ | string | | 'People Manager', | | Salary_Information | string | | '130000', | | Remote_Work___Location | string | | 'Ontario', | | Job_Level | string | | 'M3', | | Sub_Departments | string | | | ### ScimUser_urn_ietf_params_scim_schemas_extension_peakon_2_0_User | Field | Type | Required | Description | |---|---|---|---| | Gender | string enum: Female, Male | | | | Team | string | | | | Manager | string | | | | Date_of_Birth | string (date) | | | --- # Shipping API – Endpoints & Data Models ## Shipping API data models URL: https://docs.unified.to/shipping/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /shipping/{connection_id}/shipment | Create a shipment | | GET | /shipping/{connection_id}/shipment | List all shipments | | GET | /shipping/{connection_id}/shipment/{id} | Retrieve a shipment | | PUT | /shipping/{connection_id}/shipment/{id} | Update a shipment | | DELETE | /shipping/{connection_id}/shipment/{id} | Remove a shipment | | POST | /shipping/{connection_id}/label | Create a label | | GET | /shipping/{connection_id}/label | List all labels | | GET | /shipping/{connection_id}/label/{id} | Retrieve a label | | PUT | /shipping/{connection_id}/label/{id} | Update a label | | DELETE | /shipping/{connection_id}/label/{id} | Remove a label | | GET | /shipping/{connection_id}/tracking/{id} | Retrieve a tracking | | GET | /shipping/{connection_id}/tracking | List all trackings | | POST | /shipping/{connection_id}/rate | Create a rate | | GET | /shipping/{connection_id}/carrier/{id} | Retrieve a carrier | | GET | /shipping/{connection_id}/carrier | List all carriers | #### Data Models ### ShippingCarrier | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this carrier object | | created_at | string (date) | | The date that this carrier object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this carrier object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the carrier | | code | string | | Carrier code (e.g., "usps", "fedex", "ups") | | is_active | boolean | | Whether the carrier is currently active | | logo_url | string | | Carrier logo URL | | website_url | string | | Carrier website | | raw | any | | The raw data returned by the integration for this carrier | ### ShippingLabel | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this label object | | created_at | string (date) | | The date that this label object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this label object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | shipment_id | string | | Reference to the shipment | | tracking_number | string | | Tracking number for the shipment | | label_url | string | | URL to download the label | | label_format | string enum: PDF, PNG, ZPL, EPL2, PDF_4X6, PDF_4X8, PNG_4X6, PNG_4X8 | | Format of the label (PDF, PNG, ZPL, EPL2) | | status | string enum: PENDING, PROCESSING, IN_TRANSIT, DELIVERED, EXCEPTION, CANCELLED, LABEL_CREATED, PICKED_UP, OUT_FOR_DELIVERY, DELIVERY_ATTEMPTED, RETURNED_TO_SENDER, HELD_AT_LOCATION, CUSTOMS_CLEARANCE, EXCEPTION_RESOLVED | | Status of the label | | is_voided | boolean | | Whether label has been voided | | label_cost | number | | Cost to purchase label | | label_cost_currency | string | | | | rate_id | string | | Rate used for this label; points to ShippingRate | | service_code | string | | Service code used | | raw | any | | The raw data returned by the integration for this label | ### ShippingRate | Field | Type | Required | Description | |---|---|---|---| | shipment_id | string | | Reference to the shipping object (if rate is for existing shipment) | | currency | string | | | | from_address | ShippingRate_from_address | | Origin address | | to_address | ShippingRate_to_address | | Destination address | | packages | ShippingPackage[] | | Multiple packages (alternative to package) | | ship_by_at | string (date) | | Latest date to ship (from order) (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | carrier_id | string | | Reference to the carrier | | id | string | | Unique identifier for this rate object | | rates | ShippingRateRate[] | | | | raw | any | | | ### ShippingShipment | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this shipment object | | created_at | string (date) | | The date that this shipment object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this shipment object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | order_id | string | | Reference to commerce_order or accounting_order | | from_address | ShippingShipment_from_address | | Origin address | | to_address | ShippingShipment_to_address | | Destination address | | packages | ShippingPackage[] | | Array of packages in this shipment | | carrier_id | string | | Reference to the carrier | | service_code | string | | Code for the shipping service used | | status | string enum: PENDING, PROCESSING, IN_TRANSIT, DELIVERED, EXCEPTION, CANCELLED, LABEL_CREATED, PICKED_UP, OUT_FOR_DELIVERY, DELIVERY_ATTEMPTED, RETURNED_TO_SENDER, HELD_AT_LOCATION, CUSTOMS_CLEARANCE, EXCEPTION_RESOLVED | | Current status of the shipment | | rate_id | string | | Optional reference to the selected rate (for traceability) | | label_id | string | | Optional reference to the shipping label | | tracking_id | string | | Optional reference to the tracking information | | shipped_at | string (date) | | When shipment was createddispatched (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | rate_amount | number | | The rate amount used (may differ from shipping_cost due to adjustments) | | rate_currency | string | | Currency for rate_amount | | rate_service_name | string | | Service name from the rate (e.g., "Priority Mail") | | rate_estimated_days | number | | Estimated delivery days from the rate | | rate_estimated_delivery_at | string (date) | | Estimated delivery date from the rate (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | is_rate_guaranteed | boolean | | Whether delivery is guaranteed (from rate) | | return_address | ShippingShipment_return_address | | Return address (may differ from from_address) | | return_authorization_number | string | | RMA number if return shipment | | warehouse_location_id | string | | Origin warehouselocation ID; points to CommerceLocation | | warehouse_location_name | string | | Origin warehouse location name | | customs | ShippingShipment_customs | | Customs information | | is_international | boolean | | Whether shipment is international | | insurance | ShippingShipment_insurance | | Insurance details | | special_instructions | ShippingShipment_special_instructions | | Array of special instructions | | is_signature_required | boolean | | Signature required on delivery | | is_adult_signature_required | boolean | | Adult signature required | | reference_number | string | | Customer reference number | | is_return | boolean | | Whether this is a return shipment | | original_shipment_id | string | | Reference to original shipment if return; points to ShippingShipment | | return_reason | string | | Reason for return | | return_type | string enum: CUSTOMER, VENDOR, WARRANTY, DEFECTIVE, OTHER | | Type of return | | raw | any | | The raw data returned by the integration for this shipment | ### ShippingTracking | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this tracking object | | created_at | string (date) | | The date that this tracking object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this tracking object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | shipment_id | string | | Reference to the shipment | | tracking_number | string | | Tracking number for the shipment | | status | string enum: PENDING, PROCESSING, IN_TRANSIT, DELIVERED, EXCEPTION, CANCELLED, LABEL_CREATED, PICKED_UP, OUT_FOR_DELIVERY, DELIVERY_ATTEMPTED, RETURNED_TO_SENDER, HELD_AT_LOCATION, CUSTOMS_CLEARANCE, EXCEPTION_RESOLVED | | Current status of the shipment | | events | ShippingTrackingEvent[] | | Array of tracking events | | estimated_delivery | string (date) | | Estimated delivery date | | actual_delivery_at | string (date) | | Actual delivery timestamp (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | status_description | string | | Human-readable status | | carrier_id | string | | Reference to the carrier | | carrier_status_code | string | | Carrier's status code | | carrier_status_description | string | | Carrier's status description | | raw | any | | The raw data returned by the integration for this tracking | ### ShippingCustomsItem | Field | Type | Required | Description | |---|---|---|---| | description | string | | Item description | | quantity | number | | Quantity | | amount | number | | Value per unit | | currency | string | | | | weight | number | | Weight per unit | | weight_unit | string enum: g, kg, oz, lb | | | | harmonized_tariff_code | string | | HS code | | country_of_origin | string | | Country of origin (ISO code) | | sku | string | | SKU | ### ShippingPackage | Field | Type | Required | Description | |---|---|---|---| | weight | number | | Weight of the package | | weight_unit | string enum: g, kg, oz, lb | | Unit for weight (g, kg, oz, lb) | | length | number | | Length of the package | | width | number | | Width of the package | | height | number | | Height of the package | | size_unit | string enum: cm, inch | | Unit for dimensions (cm, inch) | | description | string | | Description of the package contents | | value | number | | Declared value of the package | | currency | string | | ISO 4217 currency code | | tracking_number | string | | Package-level tracking (for multi-package shipments) | | insured_amount | number | | Insured value for this package | ### ShippingRateRate | Field | Type | Required | Description | |---|---|---|---| | title | string | Yes | | | description | string | | | | amount | number | | | | currency | string | | | | base_amount | number | | Base shipping rate | | tax_amount | number | | | | discount_amount | number | | Applied discount | | surcharges | ShippingRateSurcharge[] | | Additional charges | | rate_source | string | | Source of rate (e.g., "carrier", "negotiated", "account") | | is_negotiated_rate | boolean | | Whether this is a negotiated rate | | estimated_days | number | | Estimated delivery time in days | | delivery_days | number | | Delivery days (integer) | | estimated_delivery_end_at | string (date) | | Specific delivery date (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | estimated_delivery_start_at | string (date) | | Time window (e.g., "10:00 AM - 2:00 PM") (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | delivery_terms | string | | Delivery duration terms (e.g., "3-5 business days") | | transit_hours | number | | Transit time in hours | | is_guaranteed | boolean | | Whether delivery is guaranteed | | package_type | string | | Package type (e.g., "package", "envelope", "flat") | | is_trackable | boolean | | Whether shipment is trackable | | is_active | boolean | | Whether rate is currently available | ### ShippingRateSurcharge | Field | Type | Required | Description | |---|---|---|---| | code | string | | Surcharge type (e.g., "RESIDENTIAL", "FUEL", "SIGNATURE") | | name | string | | Human-readable name | | amount | number | | Surcharge amount | | description | string | | Description of surcharge | ### ShippingRate_from_address Origin address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | | name | string | | Recipientsender name | | company_name | string | | Company name (for commercial addresses) | | telephone | string | | Phone number (required by FedEx, UPS, DHL) | | email | string | | Email address (for delivery notifications) | | is_residential | boolean | | Alias for address_type === 'RESIDENTIAL' | | is_validated | boolean | | Whether address has been validated | | delivery_instructions | string | | Special delivery notes | ### ShippingRate_to_address Destination address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | | name | string | | Recipientsender name | | company_name | string | | Company name (for commercial addresses) | | telephone | string | | Phone number (required by FedEx, UPS, DHL) | | email | string | | Email address (for delivery notifications) | | is_residential | boolean | | Alias for address_type === 'RESIDENTIAL' | | is_validated | boolean | | Whether address has been validated | | delivery_instructions | string | | Special delivery notes | ### ShippingShipment_customs Customs information | Field | Type | Required | Description | |---|---|---|---| | contents_type | string enum: MERCHANDISE, DOCUMENTS, GIFT, RETURNED_GOODS, SAMPLE, OTHER | | | | description | string | | Explanation of contents | | amount | number | | Customs declared value | | currency | string | | | | duties_paid_by | string enum: SENDER, RECIPIENT, THIRD_PARTY | | | | taxes_paid_by | string enum: SENDER, RECIPIENT, THIRD_PARTY | | | | shipper_eori | string | | Exporter EORI number | | recipient_eori | string | | Importer EORI number | | shipper_tax_number | string | | Exporter tax ID | | recipient_tax_number | string | | Importer tax ID | | items | ShippingCustomsItem[] | | Customs items | | restrictions | ShippingShipment_customs_restrictions | | Any restrictions | | non_delivery_option | string enum: RETURN, ABANDON | | | ### ShippingShipment_customs_restrictions Any restrictions ### ShippingShipment_from_address Origin address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | | name | string | | Recipientsender name | | company_name | string | | Company name (for commercial addresses) | | telephone | string | | Phone number (required by FedEx, UPS, DHL) | | email | string | | Email address (for delivery notifications) | | is_residential | boolean | | Alias for address_type === 'RESIDENTIAL' | | is_validated | boolean | | Whether address has been validated | | delivery_instructions | string | | Special delivery notes | ### ShippingShipment_insurance Insurance details | Field | Type | Required | Description | |---|---|---|---| | insured_value | number | | Insured value | | currency | string | | | | insurance_provider | string | | Insurance provider name | | insurance_provider_code | string | | Provider code | | insurance_cost | number | | Cost of insurance | | insurance_cost_currency | string | | | | coverage_type | string enum: STANDARD, PREMIUM, CUSTOM | | | | coverage_amount | number | | Coverage amount (may differ from insured_value) | ### ShippingShipment_return_address Return address (may differ from from_address) | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | | name | string | | Recipientsender name | | company_name | string | | Company name (for commercial addresses) | | telephone | string | | Phone number (required by FedEx, UPS, DHL) | | email | string | | Email address (for delivery notifications) | | is_residential | boolean | | Alias for address_type === 'RESIDENTIAL' | | is_validated | boolean | | Whether address has been validated | | delivery_instructions | string | | Special delivery notes | ### ShippingShipment_special_instructions Array of special instructions ### ShippingShipment_to_address Destination address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | | name | string | | Recipientsender name | | company_name | string | | Company name (for commercial addresses) | | telephone | string | | Phone number (required by FedEx, UPS, DHL) | | email | string | | Email address (for delivery notifications) | | is_residential | boolean | | Alias for address_type === 'RESIDENTIAL' | | is_validated | boolean | | Whether address has been validated | | delivery_instructions | string | | Special delivery notes | ### ShippingTrackingEvent | Field | Type | Required | Description | |---|---|---|---| | created_at | string (date) | | The date that this tracking event object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | description | string | | Description of the tracking event | | status | string enum: PENDING, PROCESSING, IN_TRANSIT, DELIVERED, EXCEPTION, CANCELLED, LABEL_CREATED, PICKED_UP, OUT_FOR_DELIVERY, DELIVERY_ATTEMPTED, RETURNED_TO_SENDER, HELD_AT_LOCATION, CUSTOMS_CLEARANCE, EXCEPTION_RESOLVED | | Status at the time of this event | | event_code | string | | Carrier-specific event code | | carrier_status_code | string | | Carrier's status code at this event | | location_address | ShippingTrackingEvent_location_address | | | | location_id | string | | Location ID; points to CommerceLocation (reference to ShippingLocation) | | location_name | string | | Location name | | notes | string | | Additional notes | | signed_by | string | | Who signed for delivery | ### ShippingTrackingEvent_location_address | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | | | region_code | string | | | | postal_code | string | | | | country | string | | | | country_code | string | | ISO 2-digit country code | | name | string | | Recipientsender name | | company_name | string | | Company name (for commercial addresses) | | telephone | string | | Phone number (required by FedEx, UPS, DHL) | | email | string | | Email address (for delivery notifications) | | is_residential | boolean | | Alias for address_type === 'RESIDENTIAL' | | is_validated | boolean | | Whether address has been validated | | delivery_instructions | string | | Special delivery notes | --- # Signing API – Endpoints & Data Models ## Signing API data models URL: https://docs.unified.to/signing/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /signing/{connection_id}/document | Create a document | | GET | /signing/{connection_id}/document | List all documents | | GET | /signing/{connection_id}/document/{id} | Retrieve a document | | PUT | /signing/{connection_id}/document/{id} | Update a document | | DELETE | /signing/{connection_id}/document/{id} | Remove a document | | POST | /signing/{connection_id}/signatory | Create a signatory | | GET | /signing/{connection_id}/signatory | List all signatories | | GET | /signing/{connection_id}/signatory/{id} | Retrieve a signatory | | PUT | /signing/{connection_id}/signatory/{id} | Update a signatory | | DELETE | /signing/{connection_id}/signatory/{id} | Remove a signatory | | GET | /signing/{connection_id}/template/{id} | Retrieve a template | | GET | /signing/{connection_id}/template | List all templates | #### Data Models ### SigningDocument | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this signing document | | created_at | string (date) | | The date that this signing document was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this signing document was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | Document/envelope title | | description | string | | Message to signers | | status | string enum: DRAFT, SENT, DELIVERED, IN_PROGRESS, COMPLETED, DECLINED, VOIDED, EXPIRED | | Current status of the signing document | | sent_at | string (date) | | When sent for signature (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | completed_at | string (date) | | When all signatures were collected (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | expires_at | string (date) | | Signing deadline (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | template_id | string | | If created from a template; points to SigningTemplate | | download_url | string | | URL to download the signed document | | creator_id | string | | User who created the signing document | | raw | any | | The raw data returned by the integration | ### SigningSignatory | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this signatory | | created_at | string (date) | | The date that this signatory was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this signatory was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | document_id | string | | Parent signing document; points to SigningDocument | | name | string | | Full name of the signatory | | email | string | | Email address of the signatory | | role | string enum: SIGNER, CC, APPROVER, IN_PERSON_SIGNER, VIEWER | | Role of the signatory | | status | string enum: PENDING, SENT, DELIVERED, SIGNED, DECLINED, ERROR | | Current status of this signatory | | order | number | | Signing order (1, 2, 3...) | | signed_at | string (date) | | When this signatory signed (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | decline_reason | string | | Reason for declining (if status is DECLINED) | | raw | any | | The raw data returned by the integration | ### SigningTemplate | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this template | | created_at | string (date) | | The date that this template was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this template was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | Template name | | description | string | | Template description | | is_active | boolean | | Whether the template is active/available | | creator_id | string | | User who created the template | | raw | any | | The raw data returned by the integration | --- # Storage API – Endpoints & Data Models ## Storage API data models URL: https://docs.unified.to/storage/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /storage/{connection_id}/file | Create a file | | GET | /storage/{connection_id}/file | List all files | | GET | /storage/{connection_id}/file/{id} | Retrieve a file | | PUT | /storage/{connection_id}/file/{id} | Update a file | | DELETE | /storage/{connection_id}/file/{id} | Remove a file | #### Data Models ### StorageFile | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | parent_id | string | | | | user_id | string | | (reference to HrisEmployee) | | size | number | | | | type | string enum: FILE, FOLDER | | | | mime_type | string | | | | permissions | StoragePermission[] | | | | download_url | string | | This link expires after 1 hour. When you need to retrieve the contents of a file, call the "Retrieve a file" endpoint again to get a new download_url. | | hash | string | | | | data | string | | base64 encoded file contents used to create/update only | | version | string | | | | web_url | string | | | | references | StorageReference[] | | | | raw | any | | | ### StoragePermission | Field | Type | Required | Description | |---|---|---|---| | user_id | string | | (reference to HrisEmployee) | | group_id | string | | (reference to HrisGroup) | | roles | StoragePermission_roles | Yes | | | is_hidden | boolean | | | | is_public | boolean | | | ### StoragePermission_roles ### StorageReference | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | type | string | | | | name | string | | | --- # Task API – Endpoints & Data Models ## Task API data models URL: https://docs.unified.to/task/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /task/{connection_id}/project | Create a project | | GET | /task/{connection_id}/project | List all projects | | GET | /task/{connection_id}/project/{id} | Retrieve a project | | PUT | /task/{connection_id}/project/{id} | Update a project | | DELETE | /task/{connection_id}/project/{id} | Remove a project | | POST | /task/{connection_id}/task | Create a task | | GET | /task/{connection_id}/task | List all tasks | | GET | /task/{connection_id}/task/{id} | Retrieve a task | | PUT | /task/{connection_id}/task/{id} | Update a task | | DELETE | /task/{connection_id}/task/{id} | Remove a task | | POST | /task/{connection_id}/comment | Create a comment | | GET | /task/{connection_id}/comment | List all comments | | GET | /task/{connection_id}/comment/{id} | Retrieve a comment | | PUT | /task/{connection_id}/comment/{id} | Update a comment | | DELETE | /task/{connection_id}/comment/{id} | Remove a comment | | GET | /task/{connection_id}/change/{id} | Retrieve a change | | GET | /task/{connection_id}/change | List all changes | #### Data Models ### TaskChange | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | task_id | string | | (reference to TaskTask) | | user_id | string | | (reference to HrisEmployee) | | items | TaskChangeItem[] | | | | raw | any | | | ### TaskComment | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | text | string | | | | user_id | string | | (reference to HrisEmployee) | | user_name | string | | | | task_id | string | | (reference to TaskTask) | | parent_id | string | | | | has_children | boolean | | | | raw | any | | | ### TaskProject | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | parent_id | string | | | | user_ids | TaskProject_user_ids | | (reference to HrisEmployee) | | group_ids | TaskProject_group_ids | | (reference to HrisGroup) | | description | string | | | | has_tasks | boolean | | If you can use this project ID to list tasks. Default is true. | | has_children | boolean | | If this project has children folders / lists | | metadata | TaskMetadata[] | | | | raw | any | | | ### TaskTask | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | project_id | string | | (reference to TaskProject) | | parent_id | string | | | | completed_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | status | string enum: OPENED, IN_PROGRESS, COMPLETED | | | | notes | string | | | | due_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | start_at | string (date) | | Scheduled/planned start of the task (distinct from created_at) (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | Scheduled/planned end of the task (distinct from due_at and completed_at) (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | priority | string | | | | assigned_user_ids | TaskTask_assigned_user_ids | | (reference to HrisEmployee) | | creator_user_id | string | | The user who created this task (reference to HrisEmployee) | | follower_user_ids | TaskTask_follower_user_ids | | (reference to HrisEmployee) | | group_ids | TaskTask_group_ids | | (reference to HrisGroup) | | tags | TaskTask_tags | | | | url | string | | | | attachment_ids | TaskTask_attachment_ids | | Array of attachment IDs retrieved from StorageFile.Get endpoint (reference to StorageFile) | | metadata | TaskMetadata[] | | | | has_children | boolean | | | | type | string | | The task/issue type name (e.g. Jira issue type). | | time_spent | number | | | | time_spent_unit | string | | | | progress | number | | 0-100 | | story_points | number | | | | raw | any | | | ### TaskChangeItem | Field | Type | Required | Description | |---|---|---|---| | field | string | Yes | unified field in ITaskTask | | from | string | | | | to | string | | | ### TaskMetadata | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | key | string | | @deprecated; use slug | | value | any | | | | namespace | string | | | | type | string | | @deprecated; use format instead | | format | string enum: TEXT, NUMBER, DATE, BOOLEAN, FILE, TEXTAREA, SINGLE_SELECT, MULTIPLE_SELECT, MEASUREMENT, PRICE, YES_NO, CURRENCY, URL | | | | extra_data | any | | | | slug | string | | | ### TaskProject_group_ids ### TaskProject_user_ids ### TaskTask_assigned_user_ids ### TaskTask_attachment_ids Array of attachment IDs retrieved from StorageFile.Get endpoint ### TaskTask_follower_user_ids ### TaskTask_group_ids ### TaskTask_tags --- # Ticketing API – Endpoints & Data Models ## Ticketing API data models URL: https://docs.unified.to/ticketing/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /ticketing/{connection_id}/ticket | Create a ticket | | GET | /ticketing/{connection_id}/ticket | List all tickets | | GET | /ticketing/{connection_id}/ticket/{id} | Retrieve a ticket | | PUT | /ticketing/{connection_id}/ticket/{id} | Update a ticket | | DELETE | /ticketing/{connection_id}/ticket/{id} | Remove a ticket | | POST | /ticketing/{connection_id}/customer | Create a customer | | GET | /ticketing/{connection_id}/customer | List all customers | | GET | /ticketing/{connection_id}/customer/{id} | Retrieve a customer | | PUT | /ticketing/{connection_id}/customer/{id} | Update a customer | | DELETE | /ticketing/{connection_id}/customer/{id} | Remove a customer | | POST | /ticketing/{connection_id}/note | Create a note | | GET | /ticketing/{connection_id}/note | List all notes | | GET | /ticketing/{connection_id}/note/{id} | Retrieve a note | | PUT | /ticketing/{connection_id}/note/{id} | Update a note | | DELETE | /ticketing/{connection_id}/note/{id} | Remove a note | | POST | /ticketing/{connection_id}/category | Create a category | | GET | /ticketing/{connection_id}/category | List all categories | | GET | /ticketing/{connection_id}/category/{id} | Retrieve a category | | PUT | /ticketing/{connection_id}/category/{id} | Update a category | | DELETE | /ticketing/{connection_id}/category/{id} | Remove a category | #### Data Models ### TicketingCategory | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | description | string | | | | is_active | boolean | | | | parent_id | string | | | | raw | any | | | ### TicketingCustomer | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | | | emails | TicketingEmail[] | | | | telephones | TicketingTelephone[] | | | | tags | TicketingCustomer_tags | | | | raw | any | | | ### TicketingNote | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | customer_id | string | | | | description | string | | | | ticket_id | string | | | | user_id | string | | (reference to HrisEmployee) | | raw | any | | | ### TicketingTicket | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | customer_id | string | | | | subject | string | | short description of the ticket | | description | string | | Full description of ticket issue | | status | string enum: ACTIVE, CLOSED | | | | closed_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | priority | string | | | | category_id | string | | (reference to AccountingCategory) | | source | string | | Channel where ticket was originally submitted | | source_ref | string | | Reference to source-specific object | | tags | TicketingTicket_tags | | | | user_id | string | | (reference to HrisEmployee) | | url | string | | | | attachment_ids | TicketingTicket_attachment_ids | | Array of attachment IDs retrieved from StorageFile.Get endpoint (reference to StorageFile) | | due_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | raw | any | | | ### TicketingCustomer_tags ### TicketingEmail | Field | Type | Required | Description | |---|---|---|---| | email | string | Yes | | | type | string enum: WORK, HOME, OTHER | | | ### TicketingTelephone | Field | Type | Required | Description | |---|---|---|---| | telephone | string | Yes | | | type | string enum: WORK, HOME, OTHER, FAX, MOBILE | | | ### TicketingTicket_attachment_ids Array of attachment IDs retrieved from StorageFile.Get endpoint ### TicketingTicket_tags --- # Uc API – Endpoints & Data Models ## Uc API data models URL: https://docs.unified.to/uc/overview #### Endpoints | Method | Path | Description | |---|---|---| | GET | /uc/{connection_id}/call/{id} | Retrieve a call | | GET | /uc/{connection_id}/call | List all calls | | POST | /uc/{connection_id}/contact | Create a contact | | GET | /uc/{connection_id}/contact | List all contacts | | GET | /uc/{connection_id}/contact/{id} | Retrieve a contact | | PUT | /uc/{connection_id}/contact/{id} | Update a contact | | DELETE | /uc/{connection_id}/contact/{id} | Remove a contact | | POST | /uc/{connection_id}/comment | Create a comment | | GET | /uc/{connection_id}/comment | List all comments | | GET | /uc/{connection_id}/comment/{id} | Retrieve a comment | | PUT | /uc/{connection_id}/comment/{id} | Update a comment | | DELETE | /uc/{connection_id}/comment/{id} | Remove a comment | | POST | /uc/{connection_id}/recording | Create a recording | | GET | /uc/{connection_id}/recording | List all recordings | | GET | /uc/{connection_id}/recording/{id} | Retrieve a recording | | PUT | /uc/{connection_id}/recording/{id} | Update a recording | | DELETE | /uc/{connection_id}/recording/{id} | Remove a recording | #### Data Models ### UcCall | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this call object | | created_at | string (date) | | The date that this call object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this call object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | contact_id | string | | The contact ID for call (reference to UcContact) | | telephone | UcCall_telephone | | The telephone number called | | start_at | string (date) | | The start time of the call (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | The end time of the call (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | user_id | string | | (reference to HrisEmployee) | | contacts | UcContact[] | | | | is_private | boolean | | | | user_name | string | | | | user_phone | string | | | | type | string enum: INBOUND, OUTBOUND | | | | raw | any | | The raw data returned by the integration for this call | ### UcComment | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | content | string | | | | user_id | string | | reference to HrisEmployee (reference to HrisEmployee) | | call_id | string | | | | raw | any | | | ### UcContact A contact represents a person that optionally is associated with a call | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this contact object | | created_at | string (date) | | The date that this contact object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | The last date that this contact object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | | The name of the contact | | first_name | string | | | | last_name | string | | | | title | string | | The job title of the contact | | company | string | | The company/organization name of the contact | | emails | UcEmail[] | | An array of email addresses for this contact | | telephones | UcTelephone[] | | An array of telephones for this contact | | raw | any | | The raw data returned by the integration for this contact | ### UcRecording | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | expires_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | call_id | string | | | | web_url | string | | | | contact_id | string | | @deprecated; use contacts (reference to UcContact) | | contact_name | string | | @deprecated; use contacts | | contact_phone | string | | @deprecated; use contacts | | contacts | UcContact[] | | | | user_id | string | | reference to HrisEmployee (reference to HrisEmployee) | | media | UcRecordingMedia[] | | | | user_name | string | | | | user_phone | string | | | | type | string enum: INBOUND, OUTBOUND | | | | raw | any | | | ### UcCall_telephone The telephone number called | Field | Type | Required | Description | |---|---|---|---| | telephone | string | Yes | | | type | string enum: WORK, HOME, OTHER, FAX, MOBILE | | | ### UcEmail | Field | Type | Required | Description | |---|---|---|---| | email | string | Yes | | | type | string enum: WORK, HOME, OTHER | | | ### UcRecordingMedia | Field | Type | Required | Description | |---|---|---|---| | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | language | string | | two digit ISO code | | transcripts | UcRecordingTranscript[] | | | | transcript_download_url | string | | | | recording_download_url | string | | | ### UcRecordingTranscript | Field | Type | Required | Description | |---|---|---|---| | start_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | end_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | text | string | Yes | | | contact_id | string | | (reference to UcContact) | | user_id | string | | reference to HrisEmployee (reference to HrisEmployee) | ### UcTelephone | Field | Type | Required | Description | |---|---|---|---| | telephone | string | Yes | | | type | string enum: WORK, HOME, OTHER, FAX, MOBILE | | | --- # Unified API – Endpoints & Data Models ## Unified API data models URL: https://docs.unified.to/unified/overview #### Endpoints | Method | Path | Description | |---|---|---| | POST | /unified/connection | Create connection | | GET | /unified/connection | List all connections | | GET | /unified/connection/{id} | Retrieve connection | | PUT | /unified/connection/{id} | Update connection | | DELETE | /unified/connection/{id} | Remove connection | | POST | /unified/workspace/secretsmanager | Create secrets manager | | GET | /unified/workspace/secretsmanager | List secrets managers | | GET | /unified/workspace/secretsmanager/{id} | Retrieve secrets manager | | DELETE | /unified/workspace/secretsmanager/{id} | Remove secrets manager | | GET | /unified/issue/{id} | Retrieve support issue | | GET | /unified/issue | List support issues | | GET | /unified/integration | Returns all integrations | | GET | /unified/integration/workspace/{workspace_id} | Returns all activated integrations in a workspace | | GET | /unified/webhook/{id} | Retrieve webhook by its ID | | PUT | /unified/webhook/{id} | Update webhook subscription | | DELETE | /unified/webhook/{id} | Remove webhook subscription | | GET | /unified/webhook | Returns all registered webhooks | | POST | /unified/webhook | Create webhook subscription | | PUT | /unified/webhook/{id}/trigger | Trigger webhook | | GET | /unified/apicall/{id} | Retrieve specific API Call by its ID | | GET | /unified/apicall | Returns API Calls | | GET | /unified/environment | Returns all environments | | POST | /unified/environment | Create new environments | | DELETE | /unified/environment/{env} | Remove an environment | | GET | /unified/integration/auth/{workspace_id}/{integration_type} | Authorize new connection | #### Data Models ### ApiCall | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this API call (read-only) | | created_at | string (date) | | The date that this object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) (read-only) | | connection_id | string | | | | workspace_id | string | | (reference to KmsSpace) (read-only) | | integration_type | string | Yes | The integration type | | external_xref | string | | your customer's user ID | | name | string | Yes | The called name of the API method | | path | string | Yes | The called API method's HTTP verb and route path (PUT /crm/{integration}/deak/{id}) | | size | number | | The size of the response | | status | string | Yes | The resulting HTTP status code (200) | | error | string | | The error description (if status code is >= 400) | | ip_address | string | | | | type | string enum: login, webhook, inbound, mcp | Yes | The type of API Call being logged | | method | string | Yes | | | environment | string | | | | webhook_id | string | | | | is_billable | boolean | | | | user_agent | string | | | | unified_response_time | number | | | | endapi_response_time | number | | | ### Connection A connection represents a specific authentication of an integration. | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for this integration object (read-only) | | created_at | string (date) | | The date that this integration object was created (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) (read-only) | | updated_at | string (date) | | The last date that this integration object was updated (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) (read-only) | | workspace_id | string | | (reference to KmsSpace) (read-only) | | integration_type | string | Yes | The integration type | | integration_name | string | | | | external_xref | string | | customer's user ID | | permissions | Connection_permissions | Yes | | | categories | Connection_categories | Yes | The Integration categories that this connection supports | | auth | Connection_auth | | An authentication object that represents a specific authorized user's connection to an integration. | | is_paused | boolean | | Whether this integration has exceed the monthly limit of the plan (read-only) | | environment | string | | | | last_healthy_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) (read-only) | | last_unhealthy_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) (read-only) | | last_unhealthy_code | string | | | | secretsmanager_id | string | | the ID of the SecretsManager object | | secretsmanager_key | string | | the key/path/name of the secret within the vault | ### Environments_List A list of environments in the workspace. ### Issue | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | title | string | Yes | | | status | string enum: COMPLETED, NEW, ROADMAP, IN_PROGRESS, ON_HOLD, VALIDATING, REJECTED | Yes | | | url | string | | | | workspace_id | string | Yes | (reference to KmsSpace) | | type | Issue_type | | | | resolution_time | number | | | | ticket_ref | string | Yes | | | size | number | | 1-5, 1 is lowest | | importance | number | | 1-5, 1 is lowest | | customer_note | string | | | ### SecretsManager | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | type | string enum: aws, azure, gcp, hashicorp, composio | Yes | | | name | string | Yes | | | workspace_id | string | | (reference to KmsSpace) | | auth | SecretsManager_auth | Yes | secrets-manager specific authentication values | | dcs | SecretsManager_dcs | | data-regions | ### Webhook A webhook is used to POST new/updated information to your server. | Field | Type | Required | Description | |---|---|---|---| | id | string | | (read-only) | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) (read-only) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) (read-only) | | workspace_id | string | | (reference to KmsSpace) (read-only) | | connection_id | string | Yes | | | hook_url | string | | The URL of the webhook | | object_type | string enum: accounting_account, accounting_transaction, accounting_journal, accounting_contact, accounting_invoice, accounting_bill, accounting_vendorcredit, accounting_creditmemo, accounting_taxrate, accounting_organization, accounting_order, accounting_salesorder, accounting_purchaseorder, accounting_report, accounting_balancesheet, accounting_profitloss, accounting_trialbalance, accounting_category, accounting_expense, accounting_cashflow, payment_payment, payment_link, payment_payout, payment_refund, payment_subscription, commerce_item, commerce_collection, commerce_inventory, commerce_location, commerce_review, commerce_saleschannel, commerce_itemvariant, commerce_reservation, commerce_availability, verification_package, verification_request, assessment_package, assessment_order, ats_activity, ats_application, ats_applicationstatus, ats_candidate, ats_document, ats_interview, ats_job, ats_scorecard, ats_company, crm_company, crm_contact, crm_deal, crm_event, crm_lead, crm_pipeline, crm_picklist, hris_employee, hris_group, hris_payslip, hris_timeoff, hris_company, hris_location, hris_device, hris_timeshift, hris_deduction, hris_benefit, hris_bankaccount, hris_document, hris_taxonomy, martech_list, martech_member, martech_campaign, martech_report, passthrough, ticketing_note, ticketing_ticket, ticketing_customer, ticketing_category, uc_contact, uc_call, uc_comment, uc_recording, enrich_person, enrich_company, storage_file, genai_model, genai_prompt, genai_embedding, messaging_message, messaging_channel, messaging_event, kms_space, kms_page, kms_comment, task_project, task_task, task_comment, task_change, scim_users, scim_groups, lms_course, lms_class, lms_student, lms_instructor, lms_content, lms_collection, lms_activity, repo_organization, repo_repository, repo_branch, repo_commit, repo_pullrequest, metadata_metadata, calendar_calendar, calendar_event, calendar_busy, calendar_link, calendar_recording, calendar_webinar, ads_organization, ads_ad, ads_campaign, ads_report, ads_group, ads_creative, ads_asset, ads_insertionorder, ads_target, ads_promoted, analytics_property, analytics_event, analytics_session, analytics_visitor, analytics_report, forms_form, forms_submission, shipping_carrier, shipping_rate, shipping_shipment, shipping_label, shipping_tracking, signing_document, signing_signatory, signing_template, clubs_group, clubs_member, clubs_activity, clubs_location, clubs_event, datastore_database, datastore_table, datastore_record, datastore_query, cdp_profile, cdp_segment, cdp_event, cdp_source, cdp_destination, cdp_activation | Yes | The object to return (eg. CRM "contact") | | interval | number | | The interval (in minutes) to check for updated/new objets. | | checked_at | string (date) | | The last date/time that a check was done on this object (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) (read-only) | | integration_type | string | | (read-only) | | environment | string | | (read-only) | | event | string enum: updated, created, deleted | Yes | | | runs | Webhook_runs | | An array of the most revent virtual webhook runs (read-only) | | fields | string | | | | webhook_type | string enum: virtual, native | | | | meta | any | | (read-only) | | is_healthy | boolean | | (read-only) | | page_max_limit | number | | | | filters | Webhook_filters | | | | db_type | string enum: mongodb, mysql, postgres, mssql, mariadb, supabase, snowflake | | | | db_url | string | | | | db_schema | string | | | | db_name_prefix | string | | | | is_paused | boolean | | | | is_beta | boolean | | | ### Connection_auth An authentication object that represents a specific authorized user's connection to an integration. | Field | Type | Required | Description | |---|---|---|---| | token | string | | | | access_token | string | | | | refresh_token | string | | | | expiry_date | string (date) | | | | expires_in | number | | | | emails | Connection_auth_emails | | | | name | string | | | | user_id | string | | (reference to HrisEmployee) | | app_id | string | | | | client_id | string | | | | client_secret | string | | | | consumer_key | string | | | | consumer_secret | string | | | | meta | any | | (read-only) | | state | string | | | | other_auth_info | Connection_auth_other_auth_info | | When integration.auth_type = "other", this field contains the authentication credentials in the same order as token_names | | api_url | string | | | | authorize_url | string | | | | token_url | string | | | | refresh_url | string | | | | pem | string | | the PEM X.509 certificate in Base64 ASCII format | | key | string | | the private KEY X.509 certificate in Base64 ASCII format | | refresh_token_expires_in | number | | | | refresh_token_expires_date | string (date) | | | | dev_api_key | string | | | | audience | string | | | ### Connection_auth_emails ### Connection_auth_other_auth_info When integration.auth_type = "other", this field contains the authentication credentials in the same order as token_names ### Connection_categories The Integration categories that this connection supports ### Connection_permissions ### Issue_type ### SecretsManager_auth secrets-manager specific authentication values ### SecretsManager_dcs data-regions ### Webhook_filters ### Webhook_runs An array of the most revent virtual webhook runs --- # Verification API – Endpoints & Data Models ## Verification API data models URL: https://docs.unified.to/verification/overview #### Endpoints | Method | Path | Description | |---|---|---| | GET | /verification/{connection_id}/package/{id} | Retrieve a package | | GET | /verification/{connection_id}/package | List all packages | | POST | /verification/{connection_id}/request | Create a request | | GET | /verification/{connection_id}/request | List all requests | | GET | /verification/{connection_id}/request/{id} | Retrieve a request | | PUT | /verification/{connection_id}/request/{id} | Update a request | | DELETE | /verification/{connection_id}/request/{id} | Remove a request | #### Data Models ### VerificationPackage | Field | Type | Required | Description | |---|---|---|---| | id | string | | Unique identifier for the verification type | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | name | string | Yes | Name of the verification type | | type | string enum: IDENTITY_VERIFICATION, SCREENING, BACKGROUND_CHECK, EMPLOYMENT_VERIFICATION, EDUCATION_VERIFICATION, CREDIT_CHECK, FRAUD_PREVENTION, OTHER | Yes | | | aliases | VerificationPackage_aliases | | | | tags | VerificationPackage_tags | | Category (Verification, Validation, Background Check) | | description | string | | Detailed description | | parameters | VerificationParameter[] | | Questions that need to be answered for this verification | | average_processing_times | VerificationTime[] | | average processing time in minutes | | has_redirect_url | boolean | | | | has_target_url | boolean | | where the provider will redirect the user to after the verification | | needs_ip_address | boolean | | | | cost_amount | number | | Cost-related information | | currency | string | | | | max_score | number | | if this verification returns a score, what is the maximum? | | info_url | string | | where to find additional information | | valid_regions | VerificationPackage_valid_regions | | {country}-{state/province/territory} or just {country} 2-digit ISO codes | | raw | any | | | ### VerificationRequest | Field | Type | Required | Description | |---|---|---|---| | id | string | | (read-only) | | created_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) (read-only) | | updated_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) (read-only) | | package_id | string | | | | parameters | VerificationParameterInput[] | | | | target_url | string | | | | candidate_id | string | | points to ATS Candidate | | profile_ip_address | string | | XXX.XXX.XXX.XXX | | profile_name | string | | | | profile_date_of_birth | string | | YYYY-MM-DD | | profile_addresses | VerificationAddress[] | | | | profile_gender | string enum: MALE, FEMALE, INTERSEX, TRANS, NON_BINARY | | | | profile_emails | VerificationRequest_profile_emails | | | | profile_telephones | VerificationRequest_profile_telephones | | | | profile_national_identifier | string | | | | response_completed_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | response_expires_at | string (date) | | (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | response_issued_at | string (date) | | datetime that identify was issued (ISO-8601 / YYYY-MM-DDTHH:MM:SSZ format) | | response_status | string enum: COMPLETED, FAILED, PASSED, PENDING | | Background checks and compliance checks return CLEARFLAGGED or PASS/FAIL or YES/NO | | response_score | number | | Identity verification services provide confidence scores (e.g., 0-100), Credit checks return specific numeric scores (e.g., FICO score 300-850), Fraud prevention tools use risk scores (e.g., 1-999) | | response_redirect_url | string | | Most modern IDV providers (like Onfido, Veriff, Jumio, ID.me). Background Check Providers, such as Checkr, GoodHire, Sterling, and HireRight. Credit bureaus and services often require direct user consent and information. This allows them to properly handle disclosures required by regulations like FCRA. Employment and Education Verification need users to confirm previous employers and schools. | | response_download_urls | VerificationRequest_response_download_urls | | report download | | response_details | VerificationResponseDetail[] | | | | response_source | string | | | | raw | any | | | ### VerificationAddress | Field | Type | Required | Description | |---|---|---|---| | address1 | string | | | | address2 | string | | | | city | string | | | | region | string | | Regional area of the employee's address. For example, in the U.S., the region is the employee's state; in Canada, the region is the employee’s province. | | region_code | string | | Short form for the regional area of the employee's address. For example, in the U.S., the region code is the two-letter abbreviation for the employee’s state; in Canada, the region is the two-letter abbreviation for the employee's province. | | postal_code | string | | | | country | string | | | | country_code | string | | Country code for the country, in ISO 3166 A-2 format | ### VerificationPackage_aliases ### VerificationPackage_tags Category (Verification, Validation, Background Check) ### VerificationPackage_valid_regions {country}-{state/province/territory} or just {country} 2-digit ISO codes ### VerificationParameter | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | Yes | | | public_question | string | | question to ask the human | | type | string enum: TEXT, NUMBER, MULTIPLE_CHOICE, MULTIPLE_SELECT, DATE, FILE | | | | options | VerificationParameter_options | | options for MULTIPLE_CHOICE and MULTIPLE_SELECT | | file_types | VerificationParameter_file_types | | valid file mime types | | valid_regions | VerificationParameter_valid_regions | | {country}-{stateprovince/territory} or just {country} 2-digit ISO codes | | is_required | boolean | | | ### VerificationParameterInput | Field | Type | Required | Description | |---|---|---|---| | id | string | | | | name | string | | name of parameter | | inputs | VerificationParameterInput_inputs | | | ### VerificationParameterInput_inputs ### VerificationParameter_file_types valid file mime types ### VerificationParameter_options options for MULTIPLE_CHOICE and MULTIPLE_SELECT ### VerificationParameter_valid_regions {country}-{stateprovince/territory} or just {country} 2-digit ISO codes ### VerificationRequest_profile_emails ### VerificationRequest_profile_telephones ### VerificationRequest_response_download_urls report download ### VerificationResponseDetail | Field | Type | Required | Description | |---|---|---|---| | title | string | | | | text | string | | | | is_private | boolean | | | | is_failed_reason | boolean | | | | parameter_id | string | | in reference to the parameter input | | download_url | string | | | ### VerificationTime | Field | Type | Required | Description | |---|---|---|---| | milliseconds | number | | | | valid_regions | VerificationTime_valid_regions | | {country}-{stateprovince/territory} or just {country} 2-digit ISO codes | ### VerificationTime_valid_regions {country}-{stateprovince/territory} or just {country} 2-digit ISO codes