DealHub CRM Import API

Start, track, inspect and retry asynchronous imports of buyer accounts and contacts from a tenant's connected CRM into DealHub, with per-id lookup and aggregate success/failure counts.

Operations 5

POST /api/v1/accounts/crm-import Start CRM Import #
GET /api/v1/accounts/crm-import/status Get CRM Import Status #
GET /api/v1/accounts/crm-import/detail Get CRM Import Detail #
GET /api/v1/accounts/crm-import/lookup Lookup CRM Import Id #
POST /api/v1/accounts/crm-import/retry Retry CRM Import #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/dealhub-crm-import-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no email required.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

dealhub-crm-import-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: CRM Import API
  description: 'Public, token-authenticated services that bulk-migrate a tenant''s buyer accounts and their contacts out of the tenant''s connected CRM (Salesforce, HubSpot, or Microsoft Dynamics) and into DealHub.


    The caller provides a list of buyer-account CRM ids; for each id the system fetches the matching account and all of its contacts from the CRM and persists them. The import is asynchronous — the start call returns a `request_id` immediately and the work runs in the background. Progress and per-record outcomes are tracked through the status / detail / lookup services, and a partially failed import can be re-run with the retry service.


    Accounts that already exist in DealHub (matched by CRM id) and ids not found in the CRM are skipped, so the import is safe to run again.'
  version: 1.0.0
servers:
- url: https://api.dealhub.io
  description: The base URL for your DealHub instance.
  variables:
    your-dealhub-instance:
      default: app
      description: Your specific DealHub instance name (e.g., 'app', 'service-eu1').
security:
- bearerToken: []
tags:
- name: CRM Import
  description: Bulk migration of buyer accounts and contacts from the tenant's connected CRM into DealHub.
paths:
  /api/v1/accounts/crm-import:
    post:
      tags:
      - CRM Import
      summary: Start CRM Import
      description: Starts (or joins) a CRM import for the authenticated tenant. Accepts the list of buyer-account CRM ids to migrate; duplicate ids are ignored. If an import is already running for the tenant, its `request_id` is returned instead of starting a new one.
      operationId: startCrmImport
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StartCrmImportRequest'
            examples:
              startCrmImportExample:
                summary: Start an import for two buyer accounts
                value:
                  buyer_account_crm_ids:
                  - 0011t00000ABCDE
                  - 0011t00000FGHIJ
      responses:
        '200':
          description: Import started (or joined). The work runs in the background.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CrmImportRequestResponse'
              examples:
                startedExample:
                  summary: Import started
                  value:
                    request_id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
        '400':
          description: Invalid payload, missing ids, or the tenant's CRM integration is not supported.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalidFormat:
                  summary: Invalid payload format
                  value:
                    message: 'Invalid payload format. Supported format: JSON'
                missingIds:
                  summary: No ids provided
                  value:
                    message: No buyer_account_crm_ids provided.
                unsupportedIntegration:
                  summary: Unsupported CRM integration
                  value:
                    message: 'Unsupported integration: <TYPE>'
        '403':
          description: Unauthenticated. The bearer token is missing or invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                unauthenticated:
                  value:
                    message: Unauthenticated
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                serverError:
                  value:
                    message: Internal server error
  /api/v1/accounts/crm-import/status:
    get:
      tags:
      - CRM Import
      summary: Get CRM Import Status
      description: 'Retrieves the summary of an import: aggregate counts plus a flat list of per-id failures with reasons. This is the service to poll. When `request_id` is omitted, the tenant''s most recent import is used.'
      operationId: getCrmImportStatus
      parameters:
      - name: request_id
        in: query
        required: false
        description: ID of the import request. If omitted, the tenant's most-recent import is returned.
        schema:
          type: string
          maxLength: 64
      responses:
        '200':
          description: Import status summary.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CrmImportStatusResponse'
              examples:
                runningExample:
                  summary: Import in progress with some failures
                  value:
                    request_id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
                    status: RUNNING
                    total: 1200
                    processed: 850
                    succeeded: 800
                    failed: 12
                    skipped: 38
                    failures:
                    - crm_id: 0011t00000XYZAB
                      reason: HubSpot rate limit (429)
        '403':
          description: Unauthenticated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: No import found for the tenant (or the given request_id).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notFound:
                  value:
                    message: No CRM import job found.
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/accounts/crm-import/detail:
    get:
      tags:
      - CRM Import
      summary: Get CRM Import Detail
      description: 'Retrieves the full picture of an import: the same summary as the status service plus every buyer account and each of its contacts, each with its own state and (if failed) reason. Use this to see exactly which contacts within an account did or did not migrate. When `request_id` is omitted, the tenant''s most recent import is used.'
      operationId: getCrmImportDetail
      parameters:
      - name: request_id
        in: query
        required: false
        description: ID of the import request. If omitted, the tenant's most-recent import is returned.
        schema:
          type: string
          maxLength: 64
      responses:
        '200':
          description: Full per-account, per-contact import detail.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CrmImportDetailResponse'
              examples:
                detailExample:
                  summary: One succeeded account with a failed contact, one skipped account
                  value:
                    request_id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
                    status: COMPLETED
                    total: 2
                    processed: 2
                    succeeded: 1
                    failed: 0
                    skipped: 1
                    accounts:
                    - crm_id: 0011t00000ABCDE
                      state: SUCCEEDED
                      reason: null
                      contacts:
                      - crm_id: 0031t00000AAA11
                        email: a@example.com
                        state: SUCCEEDED
                        reason: null
                      - crm_id: null
                        email: b@example.com
                        state: FAILED
                        reason: duplicate email
                    - crm_id: 0011t00000FGHIJ
                      state: SKIPPED
                      reason: null
                      contacts: []
        '403':
          description: Unauthenticated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: No import found for the tenant (or the given request_id).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notFound:
                  value:
                    message: No CRM import job found.
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/accounts/crm-import/lookup:
    get:
      tags:
      - CRM Import
      summary: Lookup CRM Import Id
      description: 'Resolves a single CRM id within an import: whether it is a buyer account or a contact (or not part of the import), its state, and the failure reason if any. For a contact match, the buyer-account CRM id it belongs to is also returned.'
      operationId: lookupCrmImportId
      parameters:
      - name: crm_id
        in: query
        required: true
        description: The CRM id to resolve (account or contact).
        schema:
          type: string
      - name: request_id
        in: query
        required: false
        description: ID of the import request. If omitted, the tenant's most-recent import is used.
        schema:
          type: string
          maxLength: 64
      responses:
        '200':
          description: Lookup result for the given CRM id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CrmImportLookupResponse'
              examples:
                contactMatch:
                  summary: Id resolves to a contact
                  value:
                    request_id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
                    crm_id: 0031t00000AAA11
                    type: CONTACT
                    account_crm_id: 0011t00000ABCDE
                    state: SUCCEEDED
                    reason: null
        '400':
          description: Missing required crm_id query parameter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingCrmId:
                  value:
                    message: Query parameter 'crm_id' is required.
        '403':
          description: Unauthenticated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: No import found for the tenant (or the given request_id).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notFound:
                  value:
                    message: No CRM import job found.
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/accounts/crm-import/retry:
    post:
      tags:
      - CRM Import
      summary: Retry CRM Import
      description: 'Re-runs the failed ids of an import (plus any unfinished ids from an interruption) under the same `request_id`. Already-imported accounts are skipped, so retrying is safe. No-op when there is nothing to reprocess. Like Start CRM Import, retry is asynchronous: it re-enqueues the failed ids and returns the `request_id` immediately — poll Get CRM Import Status (same `request_id`) for the outcome as the re-run progresses.'
      operationId: retryCrmImport
      parameters:
      - name: request_id
        in: query
        required: false
        description: ID of the import request to retry. If omitted, the tenant's most-recent import is retried.
        schema:
          type: string
          maxLength: 64
      responses:
        '200':
          description: Retry started (or nothing to reprocess). Same response shape as Start CRM Import.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CrmImportRequestResponse'
              examples:
                retryExample:
                  value:
                    request_id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
        '400':
          description: The tenant's CRM integration is not supported for import.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                unsupportedIntegration:
                  value:
                    message: 'Unsupported integration: <TYPE>'
        '403':
          description: Unauthenticated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: No import found for the tenant (or the given request_id).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notFound:
                  value:
                    message: No CRM import job found.
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    ErrorResponse:
      type: object
      description: A generic error response. The specific message will vary based on the context.
      properties:
        message:
          type: string
          example: Unauthenticated
    CrmImportRequestResponse:
      type: object
      properties:
        request_id:
          type: string
          description: ID of the asynchronous import request. Use it for the status / detail / lookup / retry services, or wait for the `crmImport` completion webhook (same request_id).
    CrmImportAccountResult:
      type: object
      properties:
        crm_id:
          type: string
          description: Buyer-account CRM id.
        state:
          type: string
          enum:
          - SUCCEEDED
          - FAILED
          - SKIPPED
          - PENDING
        reason:
          type:
          - string
          - 'null'
          description: Failure reason (account level). Present only when state = FAILED.
        contacts:
          type: array
          description: Per-contact results for the account.
          items:
            $ref: '#/components/schemas/CrmImportContactResult'
    CrmImportStatusResponse:
      type: object
      properties:
        request_id:
          type: string
          description: ID of the import request.
        status:
          type: string
          enum:
          - RUNNING
          - COMPLETED
          - INTERRUPTED
          description: COMPLETED does not imply zero failures — check the failed count.
        total:
          type: integer
          description: Number of (de-duplicated) ids in the import.
        processed:
          type: integer
          description: Number of ids that reached a terminal state (succeeded + failed + skipped).
        succeeded:
          type: integer
          description: Number of accounts imported.
        failed:
          type: integer
          description: Number of ids that errored. See failures for reasons.
        skipped:
          type: integer
          description: Number of ids skipped — already in DealHub, or not found in the CRM.
        failures:
          type: array
          description: Per-id failures. Empty when nothing failed.
          items:
            $ref: '#/components/schemas/CrmImportFailure'
    CrmImportLookupResponse:
      type: object
      properties:
        request_id:
          type: string
          description: ID of the import request.
        crm_id:
          type: string
          description: The looked-up CRM id.
        type:
          type: string
          enum:
          - ACCOUNT
          - CONTACT
          - NOT_FOUND
          description: What the id resolved to.
        account_crm_id:
          type:
          - string
          - 'null'
          description: For a contact match, the buyer-account CRM id it belongs to. Present only when type = CONTACT.
        state:
          type:
          - string
          - 'null'
          enum:
          - SUCCEEDED
          - FAILED
          - SKIPPED
          - PENDING
          - null
          description: Empty when type = NOT_FOUND.
        reason:
          type:
          - string
          - 'null'
          description: Failure reason, if any.
    CrmImportContactResult:
      type: object
      properties:
        crm_id:
          type:
          - string
          - 'null'
          description: Contact CRM id. Null when the contact has no CRM id (identified by email).
        email:
          type: string
          format: email
          description: Contact email.
        state:
          type: string
          enum:
          - SUCCEEDED
          - FAILED
        reason:
          type:
          - string
          - 'null'
          description: Failure reason (contact level). Present only when state = FAILED.
    StartCrmImportRequest:
      type: object
      properties:
        buyer_account_crm_ids:
          type: array
          description: List of buyer-account CRM ids to import.
          items:
            type: string
          minItems: 1
      required:
      - buyer_account_crm_ids
    CrmImportDetailResponse:
      allOf:
      - $ref: '#/components/schemas/CrmImportStatusResponse'
      - type: object
        properties:
          accounts:
            type: array
            description: Per buyer-account results.
            items:
              $ref: '#/components/schemas/CrmImportAccountResult'
    CrmImportFailure:
      type: object
      properties:
        crm_id:
          type: string
          description: The buyer-account CRM id that failed.
        reason:
          type: string
          description: Human-readable failure reason.
  securitySchemes:
    bearerToken:
      type: http
      scheme: bearer
      description: DealHub authentication token generated by the CPQ administrator and shared with the consuming application in advance. The tenant is resolved from the token — it is never passed in the request.
x-readme:
  explorer-enabled: true
  proxy-enabled: true