Skip to main content
Getting Started

Cookbook: Onboarding a New Customer

Step-by-step walkthrough for onboarding a new customer via the BlueRockTEL API: create a catalog family, add an item, register the customer, and build their billing file with licences, one-time charges, and recurring subscriptions.

This cookbook walks through the complete onboarding sequence for a new customer using the BlueRockTEL API. You will create all required objects in the correct dependency order, from catalog setup through to a fully configured billing dossier.

Prerequisites: You must have a valid API bearer token. See Authentication for details.

export BASE_URL="https://your-instance.bluerocktel.net/api"
export TOKEN="your-bearer-token"

Step 1: Create a Product Family

Product families are top-level catalog categories. If a suitable family already exists, skip this step and use its id in Step 2.

Endpoint: POST /v1/families

Required fields:

Field Type Description
name string Family name
curl -s -X POST "$BASE_URL/v1/families" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"name": "VoIP Services"}'
import requests

BASE_URL = "https://your-instance.bluerocktel.net/api"
TOKEN = "your-bearer-token"
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Accept": "application/json"}

response = requests.post(
    f"{BASE_URL}/v1/families",
    json={"name": "VoIP Services"},
    headers=HEADERS,
)
family = response.json()
family_id = family["id"]
print(f"Family created: id={family_id}")
use Illuminate\Support\Facades\Http;

$response = Http::withToken($token)
    ->post("{$baseUrl}/v1/families", [
        'name' => 'VoIP Services',
    ]);

$family = $response->json();
$familyId = $family['id'];
const response = await fetch(`${BASE_URL}/v1/families`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    Accept: "application/json",
  },
  body: JSON.stringify({ name: "VoIP Services" }),
});
const family = await response.json();
const familyId = family.id;

Response 201:

{
  "id": 7,
  "name": "VoIP Services",
  "created_at": "2026-03-24T10:00:00Z",
  "updated_at": "2026-03-24T10:00:00Z"
}

Step 2: Create a Catalog Item

Items are the individual products and services sold to customers. Link the item to the family created in Step 1.

Endpoint: POST /v1/items

Required fields:

Field Type Description
description string Item name shown on invoices

Commonly used optional fields:

Field Type Description
family_id integer FK to families (from Step 1)
selling_price_without_tax decimal Selling price, excl. VAT
billing_frequency integer Default billing frequency in months
active_for_recurring_billing boolean Available as a subscription line
active_for_initial_billing boolean Available as a one-time charge
curl -s -X POST "$BASE_URL/v1/items" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "description": "SIP Trunk — 2 channels",
    "family_id": 7,
    "selling_price_without_tax": 19.90,
    "billing_frequency": 1,
    "active_for_recurring_billing": true
  }'
response = requests.post(
    f"{BASE_URL}/v1/items",
    json={
        "description": "SIP Trunk — 2 channels",
        "family_id": family_id,
        "selling_price_without_tax": 19.90,
        "billing_frequency": 1,
        "active_for_recurring_billing": True,
    },
    headers=HEADERS,
)
item = response.json()
item_id = item["id"]
print(f"Item created: id={item_id}")
$response = Http::withToken($token)
    ->post("{$baseUrl}/v1/items", [
        'description'                  => 'SIP Trunk — 2 channels',
        'family_id'                    => $familyId,
        'selling_price_without_tax'    => 19.90,
        'billing_frequency'            => 1,
        'active_for_recurring_billing' => true,
    ]);

$item = $response->json();
$itemId = $item['id'];
const itemResponse = await fetch(`${BASE_URL}/v1/items`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    Accept: "application/json",
  },
  body: JSON.stringify({
    description: "SIP Trunk — 2 channels",
    family_id: familyId,
    selling_price_without_tax: 19.90,
    billing_frequency: 1,
    active_for_recurring_billing: true,
  }),
});
const item = await itemResponse.json();
const itemId = item.id;

Response 201:

{
  "id": 42,
  "description": "SIP Trunk — 2 channels",
  "family_id": 7,
  "selling_price_without_tax": "19.90",
  "billing_frequency": 1,
  "active_for_recurring_billing": true,
  "active": true,
  "created_at": "2026-03-24T10:01:00Z",
  "updated_at": "2026-03-24T10:01:00Z"
}

Step 3: Create a Customer

The customer is the top-level billing entity.

Endpoint: POST /v1/customers

Required fields:

Field Type Description
name string Company or individual name

Commonly used optional fields:

Field Type Description
email_address string Main contact email
phone string Main phone number
main_address_line1 string Address line 1
main_address_postal_code string Postal code
main_address_city string City
main_address_country string Country (default: France)
charge_vat boolean Apply VAT to invoices
curl -s -X POST "$BASE_URL/v1/customers" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "name": "Acme Telecom SAS",
    "email_address": "contact@acme-telecom.fr",
    "phone": "+33123456789",
    "main_address_line1": "12 rue de la Paix",
    "main_address_postal_code": "75001",
    "main_address_city": "Paris",
    "main_address_country": "France",
    "charge_vat": true
  }'
response = requests.post(
    f"{BASE_URL}/v1/customers",
    json={
        "name": "Acme Telecom SAS",
        "email_address": "contact@acme-telecom.fr",
        "phone": "+33123456789",
        "main_address_line1": "12 rue de la Paix",
        "main_address_postal_code": "75001",
        "main_address_city": "Paris",
        "main_address_country": "France",
        "charge_vat": True,
    },
    headers=HEADERS,
)
customer = response.json()
customer_id = customer["id"]
print(f"Customer created: id={customer_id}, account={customer['customer_account']}")
$response = Http::withToken($token)
    ->post("{$baseUrl}/v1/customers", [
        'name'                      => 'Acme Telecom SAS',
        'email_address'             => 'contact@acme-telecom.fr',
        'phone'                     => '+33123456789',
        'main_address_line1'        => '12 rue de la Paix',
        'main_address_postal_code'  => '75001',
        'main_address_city'         => 'Paris',
        'main_address_country'      => 'France',
        'charge_vat'                => true,
    ]);

$customer = $response->json();
$customerId = $customer['id'];
const customerResponse = await fetch(`${BASE_URL}/v1/customers`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    Accept: "application/json",
  },
  body: JSON.stringify({
    name: "Acme Telecom SAS",
    email_address: "contact@acme-telecom.fr",
    phone: "+33123456789",
    main_address_line1: "12 rue de la Paix",
    main_address_postal_code: "75001",
    main_address_city: "Paris",
    main_address_country: "France",
    charge_vat: true,
  }),
});
const customer = await customerResponse.json();
const customerId = customer.id;

Response 201:

{
  "id": 101,
  "name": "Acme Telecom SAS",
  "customer_account": "CL000101",
  "email_address": "contact@acme-telecom.fr",
  "charge_vat": true,
  "active": true,
  "created_at": "2026-03-24T10:02:00Z",
  "updated_at": "2026-03-24T10:02:00Z"
}

Step 4: Create a Customer File

A customer file is the billing dossier attached to the customer. Pass the customer ID as both the query parameter and body field.

Endpoint: POST /v1/customer-files?fileable_type=customer&fileable_id={customer_id}

Required query parameters:

Parameter Type Description
fileable_type string customer (or prospect)
fileable_id integer Customer ID from Step 3

Commonly used body fields:

Field Type Description
name string Label for this file (e.g. project or site name)
billing_frequency integer Billing cycle in months (1, 3, 6, 12, ...)
services_start date Date when services begin (YYYY-MM-DD)
first_invoice date Date of the first invoice (YYYY-MM-DD)
curl -s -X POST "$BASE_URL/v1/customer-files?fileable_type=customer&fileable_id=101" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "name": "Main Contract 2026",
    "billing_frequency": 1,
    "services_start": "2026-04-01",
    "first_invoice": "2026-04-01"
  }'
response = requests.post(
    f"{BASE_URL}/v1/customer-files",
    params={"fileable_type": "customer", "fileable_id": customer_id},
    json={
        "name": "Main Contract 2026",
        "billing_frequency": 1,
        "services_start": "2026-04-01",
        "first_invoice": "2026-04-01",
    },
    headers=HEADERS,
)
customer_file = response.json()
customer_file_id = customer_file["id"]
print(f"Customer file created: id={customer_file_id}")
$response = Http::withToken($token)
    ->post("{$baseUrl}/v1/customer-files", [
        'fileable_type'     => 'customer',
        'fileable_id'       => $customerId,
        'name'              => 'Main Contract 2026',
        'billing_frequency' => 1,
        'services_start'    => '2026-04-01',
        'first_invoice'     => '2026-04-01',
    ]);

$customerFile = $response->json();
$customerFileId = $customerFile['id'];
const fileResponse = await fetch(
  `${BASE_URL}/v1/customer-files?fileable_type=customer&fileable_id=${customerId}`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
      Accept: "application/json",
    },
    body: JSON.stringify({
      name: "Main Contract 2026",
      billing_frequency: 1,
      services_start: "2026-04-01",
      first_invoice: "2026-04-01",
    }),
  }
);
const customerFile = await fileResponse.json();
const customerFileId = customerFile.id;

Response 201:

{
  "id": 500,
  "fileable_type": "customer",
  "fileable_id": 101,
  "name": "Main Contract 2026",
  "billing_frequency": 1,
  "services_start": "2026-04-01",
  "first_invoice": "2026-04-01",
  "created_at": "2026-03-24T10:03:00Z",
  "updated_at": "2026-03-24T10:03:00Z"
}

Step 5: Add a Licence Line Item

Licence line items represent licence-based billing (e.g. software seats or access licences). They are billed at a configurable renewal frequency.

Endpoint: POST /v1/customer-files/licences

Required fields:

Field Type Description
customer_file_id integer Customer file ID from Step 4
item_id integer Catalog item ID from Step 2
quantity numeric Number of licences (must be > 0)

Commonly used optional fields:

Field Type Description
activation_date date Activation date (YYYY-MM-DD)
renewal_date date First renewal date (YYYY-MM-DD)
selling_price_without_tax decimal Override the catalog selling price
frequency integer Renewal frequency in months
active boolean Whether this licence is active (default: true)
curl -s -X POST "$BASE_URL/v1/customer-files/licences" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "customer_file_id": 500,
    "item_id": 42,
    "quantity": 5,
    "activation_date": "2026-04-01",
    "renewal_date": "2027-04-01",
    "frequency": 12
  }'
response = requests.post(
    f"{BASE_URL}/v1/customer-files/licences",
    json={
        "customer_file_id": customer_file_id,
        "item_id": item_id,
        "quantity": 5,
        "activation_date": "2026-04-01",
        "renewal_date": "2027-04-01",
        "frequency": 12,
    },
    headers=HEADERS,
)
licence = response.json()
print(f"Licence created: id={licence['id']}")
$response = Http::withToken($token)
    ->post("{$baseUrl}/v1/customer-files/licences", [
        'customer_file_id' => $customerFileId,
        'item_id'          => $itemId,
        'quantity'         => 5,
        'activation_date'  => '2026-04-01',
        'renewal_date'     => '2027-04-01',
        'frequency'        => 12,
    ]);

$licence = $response->json();
const licenceResponse = await fetch(`${BASE_URL}/v1/customer-files/licences`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    Accept: "application/json",
  },
  body: JSON.stringify({
    customer_file_id: customerFileId,
    item_id: itemId,
    quantity: 5,
    activation_date: "2026-04-01",
    renewal_date: "2027-04-01",
    frequency: 12,
  }),
});
const licence = await licenceResponse.json();

Step 6: Add a One-Time Initial Charge

Initial line items represent one-time charges, such as installation fees or hardware purchases.

Endpoint: POST /v1/customer-files/initials

Required fields:

Field Type Description
customer_file_id integer Customer file ID from Step 4
item_id integer Catalog item ID from Step 2
quantity numeric Quantity (must be > 0)

Commonly used optional fields:

Field Type Description
label string Custom label. Defaults to the item description
selling_price_without_tax decimal Override the catalog selling price
discount_rate decimal Discount rate (e.g. 0.1 = 10%)
charge_after_date date Do not invoice before this date (YYYY-MM-DD)
curl -s -X POST "$BASE_URL/v1/customer-files/initials" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "customer_file_id": 500,
    "item_id": 42,
    "quantity": 1,
    "label": "Installation fee — SIP Trunk setup",
    "selling_price_without_tax": 150.00
  }'
response = requests.post(
    f"{BASE_URL}/v1/customer-files/initials",
    json={
        "customer_file_id": customer_file_id,
        "item_id": item_id,
        "quantity": 1,
        "label": "Installation fee — SIP Trunk setup",
        "selling_price_without_tax": 150.00,
    },
    headers=HEADERS,
)
initial = response.json()
print(f"Initial charge created: id={initial['id']}")
$response = Http::withToken($token)
    ->post("{$baseUrl}/v1/customer-files/initials", [
        'customer_file_id'         => $customerFileId,
        'item_id'                  => $itemId,
        'quantity'                 => 1,
        'label'                    => 'Installation fee — SIP Trunk setup',
        'selling_price_without_tax' => 150.00,
    ]);

$initial = $response->json();
const initialResponse = await fetch(`${BASE_URL}/v1/customer-files/initials`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    Accept: "application/json",
  },
  body: JSON.stringify({
    customer_file_id: customerFileId,
    item_id: itemId,
    quantity: 1,
    label: "Installation fee — SIP Trunk setup",
    selling_price_without_tax: 150.00,
  }),
});
const initial = await initialResponse.json();

Step 7: Add a Recurring Subscription

Recurring line items represent periodic subscriptions billed at each billing cycle.

Endpoint: POST /v1/customer-files/recurrings

Required fields:

Field Type Description
customer_file_id integer Customer file ID from Step 4
item_id integer Catalog item ID from Step 2
quantity numeric Quantity (must be > 0)

Commonly used optional fields:

Field Type Description
label string Custom label. Defaults to the item description
selling_price_without_tax decimal Override the catalog selling price
service_start date Planned service start date (YYYY-MM-DD)
first_invoice date Date of the first invoice for this item (YYYY-MM-DD)
billing_frequency integer Override the file-level billing frequency
commitment integer Commitment duration in months
active boolean Whether this subscription is active
curl -s -X POST "$BASE_URL/v1/customer-files/recurrings" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "customer_file_id": 500,
    "item_id": 42,
    "quantity": 2,
    "service_start": "2026-04-01",
    "first_invoice": "2026-04-01",
    "billing_frequency": 1,
    "commitment": 24,
    "active": true
  }'
response = requests.post(
    f"{BASE_URL}/v1/customer-files/recurrings",
    json={
        "customer_file_id": customer_file_id,
        "item_id": item_id,
        "quantity": 2,
        "service_start": "2026-04-01",
        "first_invoice": "2026-04-01",
        "billing_frequency": 1,
        "commitment": 24,
        "active": True,
    },
    headers=HEADERS,
)
recurring = response.json()
print(f"Recurring subscription created: id={recurring['id']}")
$response = Http::withToken($token)
    ->post("{$baseUrl}/v1/customer-files/recurrings", [
        'customer_file_id' => $customerFileId,
        'item_id'          => $itemId,
        'quantity'         => 2,
        'service_start'    => '2026-04-01',
        'first_invoice'    => '2026-04-01',
        'billing_frequency'=> 1,
        'commitment'       => 24,
        'active'           => true,
    ]);

$recurring = $response->json();
const recurringResponse = await fetch(`${BASE_URL}/v1/customer-files/recurrings`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    Accept: "application/json",
  },
  body: JSON.stringify({
    customer_file_id: customerFileId,
    item_id: itemId,
    quantity: 2,
    service_start: "2026-04-01",
    first_invoice: "2026-04-01",
    billing_frequency: 1,
    commitment: 24,
    active: true,
  }),
});
const recurring = await recurringResponse.json();

Complete Workflow (Python)

The following script combines all seven steps into a single, runnable onboarding script.

import requests

BASE_URL = "https://your-instance.bluerocktel.net/api"
TOKEN = "your-bearer-token"
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Accept": "application/json"}


def post(path, payload):
    r = requests.post(f"{BASE_URL}{path}", json=payload, headers=HEADERS)
    r.raise_for_status()
    return r.json()


# Step 1 — Create a product family
family = post("/v1/families", {"name": "VoIP Services"})
family_id = family["id"]

# Step 2 — Create a catalog item
item = post("/v1/items", {
    "description": "SIP Trunk — 2 channels",
    "family_id": family_id,
    "selling_price_without_tax": 19.90,
    "billing_frequency": 1,
    "active_for_recurring_billing": True,
})
item_id = item["id"]

# Step 3 — Create a customer
customer = post("/v1/customers", {
    "name": "Acme Telecom SAS",
    "email_address": "contact@acme-telecom.fr",
    "phone": "+33123456789",
    "main_address_line1": "12 rue de la Paix",
    "main_address_postal_code": "75001",
    "main_address_city": "Paris",
    "main_address_country": "France",
    "charge_vat": True,
})
customer_id = customer["id"]

# Step 4 — Create a customer file
r = requests.post(
    f"{BASE_URL}/v1/customer-files",
    params={"fileable_type": "customer", "fileable_id": customer_id},
    json={
        "name": "Main Contract 2026",
        "billing_frequency": 1,
        "services_start": "2026-04-01",
        "first_invoice": "2026-04-01",
    },
    headers=HEADERS,
)
r.raise_for_status()
customer_file = r.json()
customer_file_id = customer_file["id"]

# Step 5 — Add a licence line item
licence = post("/v1/customer-files/licences", {
    "customer_file_id": customer_file_id,
    "item_id": item_id,
    "quantity": 5,
    "activation_date": "2026-04-01",
    "renewal_date": "2027-04-01",
    "frequency": 12,
})

# Step 6 — Add a one-time initial charge
initial = post("/v1/customer-files/initials", {
    "customer_file_id": customer_file_id,
    "item_id": item_id,
    "quantity": 1,
    "label": "Installation fee — SIP Trunk setup",
    "selling_price_without_tax": 150.00,
})

# Step 7 — Add a recurring subscription
recurring = post("/v1/customer-files/recurrings", {
    "customer_file_id": customer_file_id,
    "item_id": item_id,
    "quantity": 2,
    "service_start": "2026-04-01",
    "first_invoice": "2026-04-01",
    "billing_frequency": 1,
    "commitment": 24,
    "active": True,
})

print("Onboarding complete:")
print(f"  Family          : id={family_id}")
print(f"  Item            : id={item_id}")
print(f"  Customer        : id={customer_id}, account={customer['customer_account']}")
print(f"  Customer file   : id={customer_file_id}")
print(f"  Licence         : id={licence['id']}")
print(f"  Initial charge  : id={initial['id']}")
print(f"  Recurring       : id={recurring['id']}")

What's Next?

After completing these steps you have a fully configured billing dossier. Common next actions:

  • Retrieve the file: GET /v1/customer-files/{id} returns the complete file with all line items and relations.
  • Update billing dates: PUT /v1/customer-files/{id} to adjust billing_next or services_stop.
  • Add contacts: PUT /v1/customer-files/{id} with a contact_ids array to link contacts to the file.
  • List invoices: GET /v1/invoices filtered by customer_id to see generated invoices.