curl --request POST \
--url https://api.routera.io/contacts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"fields": {
"external_id": "ext-001",
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com"
}
}
'import requests
url = "https://api.routera.io/contacts"
payload = { "fields": {
"external_id": "ext-001",
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com"
} }
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({
fields: {
external_id: 'ext-001',
first_name: 'Ada',
last_name: 'Lovelace',
email: 'ada@example.com'
}
})
};
fetch('https://api.routera.io/contacts', 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://api.routera.io/contacts",
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([
'fields' => [
'external_id' => 'ext-001',
'first_name' => 'Ada',
'last_name' => 'Lovelace',
'email' => 'ada@example.com'
]
]),
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://api.routera.io/contacts"
payload := strings.NewReader("{\n \"fields\": {\n \"external_id\": \"ext-001\",\n \"first_name\": \"Ada\",\n \"last_name\": \"Lovelace\",\n \"email\": \"ada@example.com\"\n }\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://api.routera.io/contacts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"fields\": {\n \"external_id\": \"ext-001\",\n \"first_name\": \"Ada\",\n \"last_name\": \"Lovelace\",\n \"email\": \"ada@example.com\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.routera.io/contacts")
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 \"fields\": {\n \"external_id\": \"ext-001\",\n \"first_name\": \"Ada\",\n \"last_name\": \"Lovelace\",\n \"email\": \"ada@example.com\"\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"external_id": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"email": "<string>",
"contact_owner": "jane@example.com",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"archived": true,
"archived_by": "<string>"
}{
"message": "<string>",
"error": true,
"field": "<string>"
}{
"message": "<string>",
"error": true,
"field": "<string>"
}{
"message": "<string>",
"error": true,
"field": "<string>"
}{
"message": "<string>",
"error": true,
"field": "<string>"
}Create contact
POST /contacts — create a new object
curl --request POST \
--url https://api.routera.io/contacts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"fields": {
"external_id": "ext-001",
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com"
}
}
'import requests
url = "https://api.routera.io/contacts"
payload = { "fields": {
"external_id": "ext-001",
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com"
} }
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({
fields: {
external_id: 'ext-001',
first_name: 'Ada',
last_name: 'Lovelace',
email: 'ada@example.com'
}
})
};
fetch('https://api.routera.io/contacts', 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://api.routera.io/contacts",
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([
'fields' => [
'external_id' => 'ext-001',
'first_name' => 'Ada',
'last_name' => 'Lovelace',
'email' => 'ada@example.com'
]
]),
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://api.routera.io/contacts"
payload := strings.NewReader("{\n \"fields\": {\n \"external_id\": \"ext-001\",\n \"first_name\": \"Ada\",\n \"last_name\": \"Lovelace\",\n \"email\": \"ada@example.com\"\n }\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://api.routera.io/contacts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"fields\": {\n \"external_id\": \"ext-001\",\n \"first_name\": \"Ada\",\n \"last_name\": \"Lovelace\",\n \"email\": \"ada@example.com\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.routera.io/contacts")
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 \"fields\": {\n \"external_id\": \"ext-001\",\n \"first_name\": \"Ada\",\n \"last_name\": \"Lovelace\",\n \"email\": \"ada@example.com\"\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"external_id": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"email": "<string>",
"contact_owner": "jane@example.com",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"archived": true,
"archived_by": "<string>"
}{
"message": "<string>",
"error": true,
"field": "<string>"
}{
"message": "<string>",
"error": true,
"field": "<string>"
}{
"message": "<string>",
"error": true,
"field": "<string>"
}{
"message": "<string>",
"error": true,
"field": "<string>"
}Endpoint
POST https://api.routera.io/contacts
/contactsAuthentication: Bearer JWT
Request body
The body is a JSON object with a singlefields property. Each key inside fields is a field internal_name (the same names returned in GET responses, not nested under fields in the response).
Include every field you want to set on the new record.
{
"fields": {
"external_id": "ext-001",
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com"
}
}
| Key | Description |
|---|---|
fields | Required. Map of internal_name → value for this object type. |
fields.external_id | Optional. External identifier for sync with outside systems. |
fields.
email, first_name, or last_name. See API introduction for field types and validation rules.Default response fields
201 returns a flat object with these properties:| Property | Description |
|---|---|
id | Internal object ID |
first_name | First name |
last_name | Last name |
email | User email (read-only) |
contact_owner | Assigned owner email |
external_id | External identifier (nullable) |
archived | false when active; ISO timestamp when archived |
created_at and updated_at are always included in every response (ISO 8601 UTC), even when not listed above. When an object is archived, archived_by is also included automatically.Response example
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"external_id": "ext-001",
"created_at": "2026-05-01T10:00:00.000Z",
"updated_at": "2026-05-28T12:00:00.000Z",
"archived": false,
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com",
"contact_owner": "jane@example.com"
}
Errors
| Status | When |
|---|---|
400 | Invalid JSON body, unknown field name, validation failure, or forbidden system field |
401 | Missing account context |
409 | Duplicate active record (email, website_url, or external_id) |
500 | Internal server error |
Authorizations
Bearer JWT with valid account context.
Body
Write payload for POST create and PATCH update. Keys inside fields match field internal names returned in GET responses.
Field values keyed by internal_name. Custom account fields use the same key names.
Hide child attributes
Hide child attributes
Optional external identifier for sync with outside systems
"ext-001"
Value for first_name (field internal_name)
"Ada"
Value for last_name (field internal_name)
"Lovelace"
Value for email (field internal_name)
"ada@example.com"
Response
Created
Flat JSON object; custom fields at top level alongside id and external_id. created_at and updated_at are always present.
Assigned owner email
"jane@example.com"
false when active, or ISO timestamp when archived
Present when archived
Was this page helpful?
