Save the CRM integration

Save the rooftop's chosen partner for CRM, vehicle history or service scheduler, and register the partner link where the entity needs it.

POSThttps://api.spyne.ai/console/v1/product-onboarding/integration
AuthBearer token (console user)Rate limitNot documentedProcessingSynchronous (up to 30 s). Partner onboarding failures are ignored.
📌

Sales agents need CRM, inventory and vehicle history. On this page send entity: CRM. For vehicle history see Save the vehicle history integration.

The console forwards the body to the central configuration service. That service validates entityconfig for the entity, runs the entity's downstream steps, then saves the record for enterpriseId + teamId + domain + entity. A save replaces the whole stored entityconfig; it does not merge. The userId in the body is ignored; the user id from the token is used.

What runs before the save

EntityWhat runs
CRM, listed partner (api.partnerId and api.dealerId set)Registers or updates the rooftop's CRM link (registryId = partnerId, dealer id sent as the external id, sync every 60 minutes). Picking a different CRM partner creates a second link; the earlier one is not removed.
CRM, partner not in the list (no api.partnerId, api.partnerName set)Opens an onboarding request for the partner team with the partner name and contact details. If that request fails, the save still succeeds.
CAR_HISTORYNothing runs. Only the record is saved.
SERVICE_SCHEDULER, when api is presentThe three steps below, in order.
  1. Scheduler link. Registers, updates or switches the rooftop's scheduler link. The dealer identifiers sent depend on the exact lowercase partnerName: xtime, tekion, cdk, pbs, updatepromise use externalId (or dealerId); dealerfx needs externalId or dealerId, and oemName; mykaarma needs externalId or dealerId, and departmentUUID; evenflow needs serviceCenterId, username and password. If the name is not one of these, the required values are missing, or there is no partnerId, this step is skipped without an error.
  2. Service agent settings. Writes price display, transfer rules, upsell rules and, per partner, service rules, supported makes, available services and transportation options. For pbs and cdk it also writes slot settings (slot length, default 30; bookings per slot, default 1). Supported operations are reset to an empty list on every save.
  3. Partner not in the list (no partnerId, partnerName set and not spyne): opens an onboarding request. If that request fails, the save still succeeds.

If step 1 or 2 fails, or the CRM link call fails, the request returns 500 and the record is not saved. Steps that already ran are not undone.

The same endpoint also accepts entity: "DMS". That flow is not covered here.

Before you call it

  • You need the bearer token of a signed-in console user. The console checks it, then the configuration service checks the same token again. The route does not check that teamId belongs to the caller.
  • enterpriseId and teamId come from the onboarding session.
  • For CRM and service scheduler: partnerId, partnerName and logo from List CRM and scheduler partners, and the rooftop's dealer id at that partner.
  • For vehicle history: partnerId, partnerName and logo from List inventory and history partners. logo must not be empty.
  • Use exact entity casing: CRM, CAR_HISTORY, SERVICE_SCHEDULER. Validation accepts any casing, but other casings fail with 500 after the downstream steps have run.
  • The partner not in the list option is shown only inside the onboarding wizard, not on the Settings integration pages.
  • Synchronous. The console waits up to 30 seconds for the configuration service. Partner onboarding requests are awaited, but their failures are ignored. Rate limit: not documented.

Request body

Requiredalways sendOptionalcan be left outRequired ifrequired only in the case described
ParameterTypeRequiredDescription
Headers———
AuthorizationstringRequiredBearer YOUR_TOKEN
Content-TypestringRequiredapplication/json
All entities———
enterpriseIdstringRequired<enterpriseId> of the dealer group.
teamIdstringRequired<teamId> of the rooftop.
domainstringRequiredintegration.
entitystringRequiredCRM, CAR_HISTORY or SERVICE_SCHEDULER.
entityconfigobjectRequiredShape per entity, below. Unknown keys are dropped.
enterpriseNamestringOptionalDealer group name, stored with the record.
teamNamestringOptionalRooftop name, stored with the record.
userIdstringOptionalIgnored. The token's user is used.
entityconfig for CRM———
partnerProviderTypesstring[]RequiredAt least one value. The UI sends ["CRM"].
apiobjectOptionalPartner details. Without api, only the record is saved.
api.partnerIdstringOptionalRegistry id from the CRM partner list. When set, api.dealerId is required.
api.dealerIdstringRequired ifThe rooftop's id at the CRM. Required when partnerId is set.
api.partnerNamestringRequired ifPartner name. Required when partnerId is not set.
api.logostring or nullOptionalLogo URL.
api.approvedbooleanRequired ifRequired when partnerId is not set. The UI sends false.
api.poc_namestringRequired ifRequired when partnerId is not set. Contact at the CRM.
api.poc_emailstringRequired ifRequired when partnerId is not set.
api.poc_contactstringRequired ifRequired when partnerId is not set. Phone number.
api.configStrategy, api.isActive, api.workflowId, api.emailvariousOptionalAccepted and stored. Not sent by the UI.
app, consolebooleanOptionalDefault false.
entityconfig for CAR_HISTORY———
partnerIdstringRequiredPartner id from the partner list with category=CarHistory.
partnerNamestringRequiredPartner name.
logostringRequiredPartner logo URL. Must not be empty.
entityconfig for SERVICE_SCHEDULER———
apiobjectOptionalPartner details. Without api, no downstream step runs and only the record is saved.
api.partnerIdstringOptionalRegistry id from the scheduler partner list. Without it, the scheduler link step is skipped.
api.partnerNamestringOptionalPartner name. Matched in lowercase against xtime, tekion, cdk, pbs, updatepromise, dealerfx, mykaarma, evenflow. For a partner not in the list, send only partnerName and approved.
api.dealerIdstringOptionalThe rooftop's id at the scheduler. Used when externalId is not set.
api.externalIdstringOptionalOverrides dealerId as the external id.
api.oemNamestringRequired ifNeeded for dealerfx.
api.departmentUUIDstringRequired ifNeeded for mykaarma. Not collected by the UI.
api.serviceCenterId, api.username, api.passwordstringRequired ifNeeded for evenflow. Not collected by the UI. Stored as sent.
api.logostring or nullOptionalLogo URL.
api.approved, api.isActivebooleanOptionalThe UI sends approved: false.
serviceSchedulerConfigobjectRequired ifService agent settings. Optional in the schema, but send it whenever api is present: the server reads highlightPrices, transferScenarios and upselling.isOpted, so a request with api and no serviceSchedulerConfig.upselling fails with 500 after the link step. The UI always sends it.
serviceSchedulerConfig.highlightPricesbooleanOptionalWhether the agent quotes prices.
serviceSchedulerConfig.transferScenariosstring[]OptionalExtra callback and transfer rules.
serviceSchedulerConfig.upsellingobjectOptionalisOpted (boolean, default false) and optional data (string). data is used only when isOpted is true.
serviceSchedulerConfig.serviceRulesstring[]OptionalUsed for xtime, tekion, dealerfx.
serviceSchedulerConfig.supportedMakesstring[]OptionalUsed for pbs and for partners outside the named list.
serviceSchedulerConfig.availableServicesstring[]OptionalUsed for partners outside the named list.
serviceSchedulerConfig.serviceFacilitiesobjectOptionalpickAndDropoff, loanerBookings, shuttle, each boolean, default false. Becomes transportation options (waiter is always included). Used for xtime and partners outside the named list.
serviceSchedulerConfig.slotDurationnumberOptionalSlot length in minutes for pbs and cdk. Default 30. Must be a number; null fails validation.
serviceSchedulerConfig.capacityPerSlotnumberOptionalBookings per slot for pbs and cdk. Default 1. Must be a number; null fails validation.

For CAR_HISTORY, other keys are dropped (the UI also sends approved: false). There is no partner not in the list path for vehicle history.

Request checklist
  1. Headers: set Authorization: Bearer YOUR_TOKEN and Content-Type: application/json.
  2. Identify the record: send enterpriseId, teamId, domain: "integration" and entity.
  3. Build the config: send entityconfig in the shape for the entity.
  4. Check the result: treat any response without success: true as a failure.
  5. Finish: for CRM, set the lead sync rules next. Then mark the checklist task done with the task name for the entity.

Sample request

curl -X POST "https://api.spyne.ai/console/v1/product-onboarding/integration" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "enterpriseId": "<enterpriseId>",
    "enterpriseName": "Example Motors Group",
    "teamId": "<teamId>",
    "teamName": "Example Motors",
    "domain": "integration",
    "entity": "CRM",
    "entityconfig": {
      "api": {
        "partnerId": "<partnerId>",
        "partnerName": "examplecrm",
        "dealerId": "<dealerId>",
        "logo": "https://example.com/logo.png",
        "approved": false
      },
      "partnerProviderTypes": ["CRM"]
    }
  }'
curl -X POST "https://api.spyne.ai/console/v1/product-onboarding/integration" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "enterpriseId": "<enterpriseId>",
    "teamId": "<teamId>",
    "domain": "integration",
    "entity": "CRM",
    "entityconfig": {
      "api": {
        "partnerName": "Example CRM Co",
        "approved": false,
        "poc_name": "<contact name>",
        "poc_email": "<contact email>",
        "poc_contact": "<contact phone>"
      },
      "partnerProviderTypes": ["CRM"]
    }
  }'
import requests

res = requests.post(
    "https://api.spyne.ai/console/v1/product-onboarding/integration",
    headers={"Authorization": "Bearer YOUR_TOKEN"},
    json={
        "enterpriseId": "<enterpriseId>",
        "teamId": "<teamId>",
        "domain": "integration",
        "entity": "CAR_HISTORY",
        "entityconfig": {
            "partnerId": "<partnerId>",
            "partnerName": "Example History Reports",
            "logo": "https://example.com/logo.png",
        },
    },
)
body = res.json()
assert body.get("success") is True
const res = await fetch(
  "https://api.spyne.ai/console/v1/product-onboarding/integration",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer YOUR_TOKEN",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      enterpriseId: "<enterpriseId>",
      teamId: "<teamId>",
      domain: "integration",
      entity: "SERVICE_SCHEDULER",
      entityconfig: {
        api: {
          partnerId: "<partnerId>",
          partnerName: "xtime",
          dealerId: "<dealerId>",
          logo: "https://example.com/logo.png",
          approved: false,
        },
        serviceSchedulerConfig: {
          serviceFacilities: { pickAndDropoff: true, loanerBookings: false, shuttle: true },
          serviceRules: ["Example rule"],
          highlightPrices: true,
          transferScenarios: ["Example transfer rule"],
          upselling: { isOpted: false },
        },
      },
    }),
  }
);
const body = await res.json();

Sample response

{
  "success": true,
  "message": "Integration configuration created successfully",
  "data": {
    "_id": "<configId>",
    "enterpriseId": "<enterpriseId>",
    "teamId": "<teamId>",
    "enterpriseName": "Example Motors Group",
    "teamName": "Example Motors",
    "userId": "<userId>",
    "domain": "integration",
    "type": "single",
    "entity": "CAR_HISTORY",
    "entityconfig": {
      "partnerId": "<partnerId>",
      "partnerName": "Example History Reports",
      "logo": "https://example.com/logo.png"
    },
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  }
}
{
  "success": false,
  "message": "Validation error",
  "error": "Invalid request data",
  "errors": [
    { "code": "custom", "message": "When partnerId is provided, dealerId is required", "path": ["entityconfig", "api", "dealerId"] }
  ]
}
{ "message": "Invalid Token", "error": true, "code": "ERROR_AUTH_401", "details": "<details>", "data": null }
{ "status": "401", "message": "Invalid Auth key" }
{ "message": "<error message>", "error": true, "code": "ERROR_REQ_TIMEOUT_408", "details": "<details>", "data": null }
{ "success": false, "message": "Failed to create integration configuration", "error": "<downstream error message>" }
{ "message": "No response received from integration API", "error": "<error message>" }

Response parameters

ParameterTypeDescription
successbooleantrue when saved.
messagestringIntegration configuration created successfully on every successful save, including updates.
dataobjectThe saved record. Same fields as Read a saved integration.
data.entityconfigobjectWhat was stored, after unknown keys were dropped and defaults applied.
data.entityconfig.api.username, data.entityconfig.api.passwordstringSERVICE_SCHEDULER only, present when sent. Credential field returned by the API. Never log or display it.

Errors

StatusCodeWhen it happens
400Validation errorMissing enterpriseId, teamId, domain or entity; entity not allowed for the domain; or entityconfig breaks the entity rules (for example CRM partnerId without dealerId, CRM without partnerProviderTypes, vehicle history without logo, scheduler slotDuration: null).
401Invalid Token or token errorThe console token check failed.
401Invalid Auth keyThe configuration service token check failed.
401User ID not found in tokenThe token has no user id.
408TimeoutThe console could not reach user management.
500Failed to create integration configurationA downstream step failed (CRM link, scheduler link, service agent settings, slot settings) or the save failed. error carries the downstream message. Earlier downstream steps are not undone.
500No response received from integration APIThe configuration service did not answer within 30 seconds.

A failed partner onboarding request (partner not in the list) is not reported; the save returns 201.

What happens next

  • CRM: set the CRM lead sync rules. The UI also saves activity sync settings at the same time when the user turns them on. Only when both succeed does the UI mark CRM Integration done.
  • Vehicle history: mark Vehicle History Integration done. Nothing else is registered for vehicle history.
  • Service scheduler: mark Service Scheduler done.

Mark the task with Mark a checklist task done in the Checklist section. The UI sends no segmentName, so the task is completed in every segment the rooftop has it.


Did this page help you?