Create Contact
Add a new contact to your organization. Campaigns, automations and analytics all resolve to contacts, so this is what everything else targets.
Endpoint
POST /contacts
Returns: 201 Created
Headers
| Header | Value | Required |
|---|---|---|
Authorization | Bearer <your-api-key> | Yes |
Content-Type | application/json | Yes |
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Conditional | Contact's email address. |
phone_number | string | Conditional | Phone number in E.164 format (e.g., +966512345678). |
external_id | string | Conditional | Your own identifier for this person. Max 255 characters, unique within your organization. |
first_name | string | No | First name. Max 100 characters. |
last_name | string | No | Last name. Max 100 characters. |
properties | object | No | Custom property key-value pairs. Keys must match your organization's custom property schema. |
list_ids | string[] | No | Array of list IDs to add the contact to upon creation. |
You must provide at least one of email, phone_number or external_id. You may provide any combination.
Using external_id
external_id is the identifier you already use for this person — a user ID
from your own database. It outranks email and phone_number everywhere in
Arsel, which makes it the right key for two jobs:
- Migrating from another platform. Set it during import and every later event resolves to the same contact deterministically, even for people whose email you don't have.
- People whose contact details change. An email or phone can be edited;
external_idstays put, so history stays attached to the right person.
Reusing an external_id that another contact already holds returns 409. To
find whether you have already imported someone, filter for it on
List contacts rather than searching by email.
Custom Properties
Custom properties let you store additional data on contacts. Define your property schema in the Arsel Dashboard under Contacts > Properties before sending values.
{
"properties": {
"company": "Acme Inc",
"age": 30,
"is_vip": true,
"birthdate": "1995-06-15"
}
}
Response
{
"id": "01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a",
"email": "john.doe@example.com",
"phone_number": "+201234567890",
"external_id": "user_10294",
"first_name": "John",
"last_name": "Doe",
"source": "api",
"properties": {
"company": "Acme Inc"
},
"is_suppressed": false,
"created_at": "2026-03-08T12:00:00.000Z",
"updated_at": "2026-03-08T12:00:00.000Z"
}
| Field | Type | Description |
|---|---|---|
id | string | Unique contact ID (UUIDv7) |
email | string | null | Contact email address |
phone_number | string | null | Contact phone number |
external_id | string | null | Your own identifier for this person |
first_name | string | null | First name |
last_name | string | null | Last name |
source | string | How the contact was created: api, manual, import, transactional |
properties | object | Custom property values |
is_suppressed | boolean | true if the contact has unsubscribed, bounced, been blocked by email validation, or been manually suppressed |
created_at | string | ISO 8601 timestamp |
updated_at | string | ISO 8601 timestamp |
Contacts created via the API have their source set to api automatically.
Examples
Basic Contact
- cURL
- JavaScript
- Python
- C#
- PHP
curl -X POST "https://api.arsel.sa/v1/contacts" \
-H "Authorization: Bearer be_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"email": "john.doe@example.com",
"first_name": "John",
"last_name": "Doe"
}'
const response = await fetch("https://api.arsel.sa/v1/contacts", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer be_your_api_key",
},
body: JSON.stringify({
email: "john.doe@example.com",
first_name: "John",
last_name: "Doe",
}),
});
const contact = await response.json();
console.log(contact.id);
import requests
response = requests.post(
"https://api.arsel.sa/v1/contacts",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer be_your_api_key",
},
json={
"email": "john.doe@example.com",
"first_name": "John",
"last_name": "Doe",
},
)
contact = response.json()
print(contact["id"])
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer be_your_api_key");
var payload = new
{
email = "john.doe@example.com",
first_name = "John",
last_name = "Doe"
};
var json = System.Text.Json.JsonSerializer.Serialize(payload);
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.arsel.sa/v1/contacts", content);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
<?php
$ch = curl_init("https://api.arsel.sa/v1/contacts");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer be_your_api_key"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"email" => "john.doe@example.com",
"first_name" => "John",
"last_name" => "Doe"
]));
$response = curl_exec($ch);
echo $response;
curl_close($ch);
With Custom Properties and Lists
- cURL
- JavaScript
- Python
- C#
- PHP
curl -X POST "https://api.arsel.sa/v1/contacts" \
-H "Authorization: Bearer be_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"email": "sara@example.com",
"phone_number": "+966512345678",
"first_name": "Sara",
"last_name": "Ahmed",
"properties": {
"company": "Acme Inc",
"job_title": "Marketing Manager",
"city": "Riyadh"
},
"list_ids": ["01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a"]
}'
const response = await fetch("https://api.arsel.sa/v1/contacts", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer be_your_api_key",
},
body: JSON.stringify({
email: "sara@example.com",
phone_number: "+966512345678",
first_name: "Sara",
last_name: "Ahmed",
properties: {
company: "Acme Inc",
job_title: "Marketing Manager",
city: "Riyadh",
},
list_ids: ["01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a"],
}),
});
const contact = await response.json();
console.log(contact.id);
import requests
response = requests.post(
"https://api.arsel.sa/v1/contacts",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer be_your_api_key",
},
json={
"email": "sara@example.com",
"phone_number": "+966512345678",
"first_name": "Sara",
"last_name": "Ahmed",
"properties": {
"company": "Acme Inc",
"job_title": "Marketing Manager",
"city": "Riyadh",
},
"list_ids": ["01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a"],
},
)
print(response.json())
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer be_your_api_key");
var payload = new
{
email = "sara@example.com",
phone_number = "+966512345678",
first_name = "Sara",
last_name = "Ahmed",
properties = new
{
company = "Acme Inc",
job_title = "Marketing Manager",
city = "Riyadh"
},
list_ids = new[] { "01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a" }
};
var json = System.Text.Json.JsonSerializer.Serialize(payload);
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.arsel.sa/v1/contacts", content);
Console.WriteLine(await response.Content.ReadAsStringAsync());
<?php
$ch = curl_init("https://api.arsel.sa/v1/contacts");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer be_your_api_key"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"email" => "sara@example.com",
"phone_number" => "+966512345678",
"first_name" => "Sara",
"last_name" => "Ahmed",
"properties" => [
"company" => "Acme Inc",
"job_title" => "Marketing Manager",
"city" => "Riyadh"
],
"list_ids" => ["01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a"]
]));
$response = curl_exec($ch);
echo $response;
curl_close($ch);
Error Responses
- 422 Validation Error
- 409 Conflict
- 401 Unauthorized
- 403 Usage Limit
{
"status_code": 422,
"name": "validation_error",
"message": "At least one of email or phone_number must be provided"
}
{
"status_code": 409,
"name": "conflict",
"message": "A contact with this email already exists in your organization"
}
{
"status_code": 401,
"name": "unauthorized",
"message": "Invalid or missing API key"
}
{
"status_code": 403,
"name": "usage_limit_exceeded",
"message": "You have reached your contact limit. Please upgrade your plan."
}