Submit or save the brand registration

Saves the rooftop's brand registration as a draft, or submits it to the phone carrier.

POSThttps://api.spyne.ai/console/v1/product-onboarding/vini/business-registration
AuthBearer token (console user)Rate limitNot documentedProcessingSynchronous (30 s timeout). Carrier review continues after the response.

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 code
  • website, cleaned up to a plain https:// URL
  • industry, always AUTOMOTIVE

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 statedraft: truedraft: false
No registrationSaves a draft, without full validationSubmits a new registration
DraftOverwrites the draftSubmits the draft for the first time
Failed409Retries with the new data
Anything else (pending, active, awaiting prerequisite, suspended)409409

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, every businessProfile field below is required, including one authorizedRepresentative.

  • 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

Requiredalways sendOptionalcan be left outRequired ifrequired only in the case described
Request checklist
  1. Read the current state with Get the brand registration.
  2. Use draft: true to save partial data, and draft: false to submit.
  3. EIN goes in registrationId. The UI checks the format ##-#######.
  4. Pick the representative from the employee directory. Set jobPositionCategory to one of the allowed values.
  5. On the rooftop's first save only, the UI calls Mark a checklist task done with {teamId, taskName: "CNAM Registration", isCompleted: false} (in progress).
ParameterTypeRequiredDescription
Headers——Sent as HTTP headers.
AuthorizationstringRequiredBearer YOUR_TOKEN
Content-TypestringRequiredapplication/json
Body——Sent as a JSON body.
enterpriseIdstringRequiredDealer group ID.
teamIdstringRequiredRooftop ID.
draftbooleanOptionaltrue = save as draft and skip businessProfile validation. false or omitted = submit.
businessProfileobjectRequired ifRequired when submitting. Business details.
businessProfile.legalBusinessNamestringRequiredLegal business name.
businessProfile.displayNamestringRequiredCaller ID display name.
businessProfile.entityTypestringRequiredprivate_profit, public_profit, non_profit, government or sole_proprietor.
businessProfile.registrationIdstringRequiredEIN.
businessProfile.emailstringRequiredBusiness email.
businessProfile.isdCodestringRequirede.g. +1.
businessProfile.phoneNumberstringRequiredNational number, without the country code.
businessProfile.privacyPolicyUrlstringRequiredPrivacy policy URL.
businessProfile.termsAndConditionsUrlstringRequiredTerms URL.
businessProfile.authorizedRepresentativeobjectRequiredfirstName, lastName, email, isdCode, phoneNumber, jobTitle, jobPositionCategory (Director, GM, VP, CEO, CFO, General Counsel, Other), and an optional employeeId (string).
businessProfile.addressobjectOptionalAlways replaced with the rooftop address on file.
businessProfile.websitestringOptionalAlways replaced with the rooftop website on file.
businessProfile.industrystringOptionalAlways 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

ParameterTypeDescription
idstringRegistration record ID.
enterpriseId, teamIdstringDealer group and rooftop.
providerstringCarrier picked for the rooftop.
stageKindstringRegistration stage, e.g. business_profile, brand, cnam.
statusstringdraft, awaiting_prerequisite, pending, active, failed or suspended.
providerResourceIdstring or nullCarrier-side ID.
failureReasonstring or nullWhy a stage failed.
submittedPayloadJsonobjectThe business profile as saved.
previousAttemptIdstring or nullEarlier attempt, on retry or draft promotion.
createdAt, updatedAtstringTimestamps.

Errors

StatusReasonWhen it happens
400Team rooftop address is missing / Could not resolve an ISO country code / Team website is missingThe rooftop record has no address, an unmapped country, or no website. Fix with Save rooftop details.
400Field 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.
401ERROR_AUTH_401Missing or invalid token.
408ERROR_REQ_TIMEOUT_408Token check could not reach the auth service.
409Business registration already in progress for this teamA registration is pending, active, awaiting a prerequisite or suspended, or draft: true was sent for a failed registration.
500No response received from external API / Error setting up requestThe 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).


Did this page help you?