curl --request POST \
--url https://app.saasybill.com/api/v1/customers \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"code": "ACME-001",
"name": "Acme Pty Ltd",
"email": "accounts@acme.example"
}
'import requests
url = "https://app.saasybill.com/api/v1/customers"
payload = {
"code": "ACME-001",
"name": "Acme Pty Ltd",
"email": "accounts@acme.example"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({code: 'ACME-001', name: 'Acme Pty Ltd', email: 'accounts@acme.example'})
};
fetch('https://app.saasybill.com/api/v1/customers', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.saasybill.com/api/v1/customers",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'code' => 'ACME-001',
'name' => 'Acme Pty Ltd',
'email' => 'accounts@acme.example'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.saasybill.com/api/v1/customers"
payload := strings.NewReader("{\n \"code\": \"ACME-001\",\n \"name\": \"Acme Pty Ltd\",\n \"email\": \"accounts@acme.example\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.saasybill.com/api/v1/customers")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"code\": \"ACME-001\",\n \"name\": \"Acme Pty Ltd\",\n \"email\": \"accounts@acme.example\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.saasybill.com/api/v1/customers")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"code\": \"ACME-001\",\n \"name\": \"Acme Pty Ltd\",\n \"email\": \"accounts@acme.example\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3b9f1c1e-6a52-4c0e-9d7e-1f6f2f0b7a11",
"object": "customer",
"code": "ACME-001",
"name": "Acme Pty Ltd",
"company_name": "Acme Pty Ltd",
"first_name": null,
"last_name": null,
"email": "accounts@acme.example",
"status": "active",
"xero_contact_id": "d5f2a8c1-6b73-4e09-8a1d-4c7e9b3f0a12",
"created": "2026-08-14T02:31:08.000Z"
}{
"id": "3b9f1c1e-6a52-4c0e-9d7e-1f6f2f0b7a11",
"object": "customer",
"code": "ACME-001",
"name": "Acme Pty Ltd",
"company_name": "Acme Pty Ltd",
"first_name": null,
"last_name": null,
"email": "accounts@acme.example",
"status": "active",
"xero_contact_id": "d5f2a8c1-6b73-4e09-8a1d-4c7e9b3f0a12",
"created": "2026-08-14T02:31:08.000Z"
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "Provide a valid API key as `Authorization: Bearer <key>`.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "permission_error",
"code": "xero_contact_creation_disabled",
"message": "This organisation has not allowed the API to create Xero contacts. An owner or admin can turn it on under Developer Settings.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "not_found",
"code": "customer_not_found",
"message": "There is no customer with that id.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "conflict",
"code": "idempotency_conflict",
"message": "This `Idempotency-Key` was used with a different request. Use a new key for a different request.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "invalid_request_error",
"code": "invalid_parameter",
"message": "`units`: Invalid input: expected number, received string",
"param": "units",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "Too many requests. Try again in 12 seconds.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "api_error",
"code": "internal_error",
"message": "Something went wrong on our side. Retry the request; if it keeps failing, quote the request id.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}Create a customer
Returns the customer with this code if there is one (200). Otherwise adopts the Xero contact holding that contact number, or creates the contact in Xero, and returns the new customer (201). Refused (403 xero_contact_creation_disabled) unless an owner or admin has allowed the API to create Xero contacts.
curl --request POST \
--url https://app.saasybill.com/api/v1/customers \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"code": "ACME-001",
"name": "Acme Pty Ltd",
"email": "accounts@acme.example"
}
'import requests
url = "https://app.saasybill.com/api/v1/customers"
payload = {
"code": "ACME-001",
"name": "Acme Pty Ltd",
"email": "accounts@acme.example"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({code: 'ACME-001', name: 'Acme Pty Ltd', email: 'accounts@acme.example'})
};
fetch('https://app.saasybill.com/api/v1/customers', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.saasybill.com/api/v1/customers",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'code' => 'ACME-001',
'name' => 'Acme Pty Ltd',
'email' => 'accounts@acme.example'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.saasybill.com/api/v1/customers"
payload := strings.NewReader("{\n \"code\": \"ACME-001\",\n \"name\": \"Acme Pty Ltd\",\n \"email\": \"accounts@acme.example\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.saasybill.com/api/v1/customers")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"code\": \"ACME-001\",\n \"name\": \"Acme Pty Ltd\",\n \"email\": \"accounts@acme.example\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.saasybill.com/api/v1/customers")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"code\": \"ACME-001\",\n \"name\": \"Acme Pty Ltd\",\n \"email\": \"accounts@acme.example\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3b9f1c1e-6a52-4c0e-9d7e-1f6f2f0b7a11",
"object": "customer",
"code": "ACME-001",
"name": "Acme Pty Ltd",
"company_name": "Acme Pty Ltd",
"first_name": null,
"last_name": null,
"email": "accounts@acme.example",
"status": "active",
"xero_contact_id": "d5f2a8c1-6b73-4e09-8a1d-4c7e9b3f0a12",
"created": "2026-08-14T02:31:08.000Z"
}{
"id": "3b9f1c1e-6a52-4c0e-9d7e-1f6f2f0b7a11",
"object": "customer",
"code": "ACME-001",
"name": "Acme Pty Ltd",
"company_name": "Acme Pty Ltd",
"first_name": null,
"last_name": null,
"email": "accounts@acme.example",
"status": "active",
"xero_contact_id": "d5f2a8c1-6b73-4e09-8a1d-4c7e9b3f0a12",
"created": "2026-08-14T02:31:08.000Z"
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "Provide a valid API key as `Authorization: Bearer <key>`.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "permission_error",
"code": "xero_contact_creation_disabled",
"message": "This organisation has not allowed the API to create Xero contacts. An owner or admin can turn it on under Developer Settings.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "not_found",
"code": "customer_not_found",
"message": "There is no customer with that id.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "conflict",
"code": "idempotency_conflict",
"message": "This `Idempotency-Key` was used with a different request. Use a new key for a different request.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "invalid_request_error",
"code": "invalid_parameter",
"message": "`units`: Invalid input: expected number, received string",
"param": "units",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "Too many requests. Try again in 12 seconds.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "api_error",
"code": "internal_error",
"message": "Something went wrong on our side. Retry the request; if it keeps failing, quote the request id.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}customers:write scope. See Authentication.Authorizations
An API key. Its scopes decide what it may call: customers:read (List and look up customers.) customers:write (Create customers, and Xero contacts where the organisation allows it.) catalogue:read (List plans and charges.) subscriptions:read (List and look up subscriptions.) subscriptions:write (Create subscriptions, change units, end and restore them.) invoices:read (List and look up invoices.)
Headers
1 to 255 printable characters. A retry with the same key and body returns the first response.
1 - 255"signup_8f3a2c1d"
Body
The organisation's own reference for the customer. Unique per organisation.
The name the contact is created under in Xero.
1 - 255255^(?:[A-Za-z0-9_'+\-]+\.)*[A-Za-z0-9_'+\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$Response
A customer with this code already existed.
Was this page helpful?