Submit or save the brand registration
Saves the rooftop's brand registration as a draft, or submits it to the phone carrier.
Reads the rooftop's stored address and website from the rooftop record. Then it overwrites three fields in businessProfile:
address, taken from the rooftop record, with the country converted to a 2-letter ISO codewebsite, cleaned up to a plainhttps://URLindustry, alwaysAUTOMOTIVE
Any provider field in the request is dropped. The request is then forwarded to the registration service, which picks the rooftop's carrier and acts on the current state:
| Current state | draft: true | draft: false |
|---|---|---|
| No registration | Saves a draft, without full validation | Submits a new registration |
| Draft | Overwrites the draft | Submits the draft for the first time |
| Failed | 409 | Retries with the new data |
| Anything else (pending, active, awaiting prerequisite, suspended) | 409 | 409 |
On a real submission for one carrier, the registration service immediately asks the checklist to mark "Brand Registration" done. For the other carrier, it does this when the business profile is approved. See the Rooftop Setup introduction.
This endpoint does not update the checklist itself.
Before you call it
-
Run Save rooftop details first. The rooftop must have an address with a country that maps to an ISO code, and a website. Otherwise this call returns 400.
-
Unknown fields are rejected (400). Send only the fields listed below.
-
For
draft: false, everybusinessProfilefield below is required, including oneauthorizedRepresentative. -
Authentication: Bearer token of a signed-in console user, checked here. The forwarded call to the registration service carries no token.
-
Rate limit: Not documented
-
Processing: Synchronous. Waits for the registration service (30 second timeout). A submission's carrier review continues after the response. Use "Get the brand (CNAM) registration profile" to see the outcome.
Request
- Read the current state with Get the brand registration.
- Use
draft: trueto save partial data, anddraft: falseto submit. - EIN goes in
registrationId. The UI checks the format##-#######. - Pick the representative from the employee directory. Set
jobPositionCategoryto one of the allowed values. - On the rooftop's first save only, the UI calls Mark a checklist task done with
{teamId, taskName: "CNAM Registration", isCompleted: false}(in progress).
| Parameter | Type | Required | Description |
|---|---|---|---|
| Headers | — | — | Sent as HTTP headers. |
Authorization | string | Required | Bearer YOUR_TOKEN |
Content-Type | string | Required | application/json |
| Body | — | — | Sent as a JSON body. |
enterpriseId | string | Required | Dealer group ID. |
teamId | string | Required | Rooftop ID. |
draft | boolean | Optional | true = save as draft and skip businessProfile validation. false or omitted = submit. |
businessProfile | object | Required if | Required when submitting. Business details. |
businessProfile.legalBusinessName | string | Required | Legal business name. |
businessProfile.displayName | string | Required | Caller ID display name. |
businessProfile.entityType | string | Required | private_profit, public_profit, non_profit, government or sole_proprietor. |
businessProfile.registrationId | string | Required | EIN. |
businessProfile.email | string | Required | Business email. |
businessProfile.isdCode | string | Required | e.g. +1. |
businessProfile.phoneNumber | string | Required | National number, without the country code. |
businessProfile.privacyPolicyUrl | string | Required | Privacy policy URL. |
businessProfile.termsAndConditionsUrl | string | Required | Terms URL. |
businessProfile.authorizedRepresentative | object | Required | firstName, lastName, email, isdCode, phoneNumber, jobTitle, jobPositionCategory (Director, GM, VP, CEO, CFO, General Counsel, Other), and an optional employeeId (string). |
businessProfile.address | object | Optional | Always replaced with the rooftop address on file. |
businessProfile.website | string | Optional | Always replaced with the rooftop website on file. |
businessProfile.industry | string | Optional | Always replaced with AUTOMOTIVE. |
Sample request
curl -X POST "https://api.spyne.ai/console/v1/product-onboarding/vini/business-registration" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enterpriseId": "<enterpriseId>",
"teamId": "<teamId>",
"draft": false,
"businessProfile": {
"legalBusinessName": "Example Motors LLC",
"displayName": "Example Motors",
"entityType": "private_profit",
"registrationId": "12-3456789",
"email": "[email protected]",
"isdCode": "+1",
"phoneNumber": "5550100000",
"privacyPolicyUrl": "https://www.example.com/privacy",
"termsAndConditionsUrl": "https://www.example.com/terms",
"authorizedRepresentative": {
"firstName": "Jane", "lastName": "Doe", "email": "[email protected]",
"isdCode": "+1", "phoneNumber": "5550100000", "jobTitle": "General Manager",
"jobPositionCategory": "GM", "employeeId": "101"
}
}
}'import requests
payload = {
"enterpriseId": "<enterpriseId>",
"teamId": "<teamId>",
"draft": False,
"businessProfile": {
"legalBusinessName": "Example Motors LLC",
"displayName": "Example Motors",
"entityType": "private_profit",
"registrationId": "12-3456789",
"email": "[email protected]",
"isdCode": "+1",
"phoneNumber": "5550100000",
"privacyPolicyUrl": "https://www.example.com/privacy",
"termsAndConditionsUrl": "https://www.example.com/terms",
"authorizedRepresentative": {
"firstName": "Jane", "lastName": "Doe", "email": "[email protected]",
"isdCode": "+1", "phoneNumber": "5550100000", "jobTitle": "General Manager",
"jobPositionCategory": "GM", "employeeId": "101",
},
},
}
res = requests.post(
"https://api.spyne.ai/console/v1/product-onboarding/vini/business-registration",
json=payload,
headers={"Authorization": "Bearer YOUR_TOKEN"},
)
print(res.status_code, res.json())const res = await fetch("https://api.spyne.ai/console/v1/product-onboarding/vini/business-registration", {
method: "POST",
headers: { Authorization: "Bearer YOUR_TOKEN", "Content-Type": "application/json" },
body: JSON.stringify({
enterpriseId: "<enterpriseId>",
teamId: "<teamId>",
draft: false,
businessProfile: {
legalBusinessName: "Example Motors LLC",
displayName: "Example Motors",
entityType: "private_profit",
registrationId: "12-3456789",
email: "[email protected]",
isdCode: "+1",
phoneNumber: "5550100000",
privacyPolicyUrl: "https://www.example.com/privacy",
termsAndConditionsUrl: "https://www.example.com/terms",
authorizedRepresentative: {
firstName: "Jane", lastName: "Doe", email: "[email protected]",
isdCode: "+1", phoneNumber: "5550100000", jobTitle: "General Manager",
jobPositionCategory: "GM", employeeId: "101",
},
},
}),
});
console.log(res.status, await res.json());Sample response
{
"id": "<registrationId>",
"enterpriseId": "<enterpriseId>",
"teamId": "<teamId>",
"provider": "<provider>",
"stageKind": "business_profile",
"parentRegistrationId": null,
"providerResourceId": "<providerResourceId>",
"status": "pending",
"failureReason": null,
"stageMetadataJson": {},
"submittedPayloadJson": { "legalBusinessName": "Example Motors LLC", "displayName": "Example Motors" },
"rawJson": null,
"previousAttemptId": null,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}{ "message": "Team rooftop address is missing for teamId: <teamId>. Add it in the rooftop profile before submitting business registration.", "error": true }{
"statusCode": 400,
"message": ["businessProfile.authorizedRepresentative.jobPositionCategory must be one of the following values: Director, GM, VP, CEO, CFO, General Counsel, Other"],
"error": "Bad Request"
}{ "message": "Invalid Token", "error": true, "code": "ERROR_AUTH_401", "details": "", "data": null }{ "message": "...", "error": true, "code": "ERROR_REQ_TIMEOUT_408", "details": "", "data": null }{ "statusCode": 409, "message": "Business registration already in progress for this team", "error": "Conflict" }{ "message": "No response received from external API", "error": "timeout of 30000ms exceeded" }201 Created (draft saved or updated, draft promoted, or failed attempt retried: one registration record). A first-time submission (no earlier record) returns an array of these records, one per registration stage created.
400 Bad Request (rooftop data missing). Other messages with the same shape:
Could not resolve an ISO country code for country: <country>Team website is missing for teamId: <teamId>. Add it in the rooftop profile before submitting business registration.
Response fields
| Parameter | Type | Description |
|---|---|---|
id | string | Registration record ID. |
enterpriseId, teamId | string | Dealer group and rooftop. |
provider | string | Carrier picked for the rooftop. |
stageKind | string | Registration stage, e.g. business_profile, brand, cnam. |
status | string | draft, awaiting_prerequisite, pending, active, failed or suspended. |
providerResourceId | string or null | Carrier-side ID. |
failureReason | string or null | Why a stage failed. |
submittedPayloadJson | object | The business profile as saved. |
previousAttemptId | string or null | Earlier attempt, on retry or draft promotion. |
createdAt, updatedAt | string | Timestamps. |
Errors
| Status | Reason | When it happens |
|---|---|---|
400 | Team rooftop address is missing / Could not resolve an ISO country code / Team website is missing | The rooftop record has no address, an unmapped country, or no website. Fix with Save rooftop details. |
400 | Field validation (passed through) | A required field is missing or invalid on submit, a value is not in an allowed list, or an unknown field was sent. message is a list. |
401 | ERROR_AUTH_401 | Missing or invalid token. |
408 | ERROR_REQ_TIMEOUT_408 | Token check could not reach the auth service. |
409 | Business registration already in progress for this team | A registration is pending, active, awaiting a prerequisite or suspended, or draft: true was sent for a failed registration. |
500 | No response received from external API / Error setting up request | The registration service or the rooftop record read did not answer, or the request could not be sent. A rooftop record read that answers with an error returns that status. |
Next
Re-read with Get the brand registration to see state and status. On the rooftop's first save, the UI calls Mark a checklist task done with {teamId: "<teamId>", taskName: "CNAM Registration", isCompleted: false}. That sets the task to in progress. Completion is expected to come from the server after approval (see the Rooftop Setup introduction).
- Called before: Save rooftop details, Get the brand registration, List the employee directory.
- Called after: Mark a checklist task done, Get the brand registration.
Updated about 1 hour ago
