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 Audience > 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 $ARSEL_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: {
Authorization: `Bearer ${process.env.ARSEL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
email: "john.doe@example.com",
first_name: "John",
last_name: "Doe",
}),
});
const data = await response.json();
if (!response.ok) {
throw new Error(`${data.name}: ${data.message}`);
}
console.log(data);
import os
import requests
response = requests.post(
"https://api.arsel.sa/v1/contacts",
headers={"Authorization": f"Bearer {os.environ['ARSEL_API_KEY']}"},
json={
"email": "john.doe@example.com",
"first_name": "John",
"last_name": "Doe",
},
)
response.raise_for_status()
print(response.json())
using System.Net.Http.Headers;
using System.Text;
var client = new HttpClient(); // share one instance across requests
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("ARSEL_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.arsel.sa/v1/contacts")
{
Content = new StringContent(
"""
{
"email": "john.doe@example.com",
"first_name": "John",
"last_name": "Doe"
}
""",
Encoding.UTF8,
"application/json"),
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException($"{(int)response.StatusCode}: {body}");
Console.WriteLine(body);
<?php
$ch = curl_init("https://api.arsel.sa/v1/contacts");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ARSEL_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"email" => "john.doe@example.com",
"first_name" => "John",
"last_name" => "Doe",
]),
]);
$data = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) {
throw new Exception("{$data['name']}: {$data['message']}");
}
print_r($data);
With Custom Properties and Lists
- cURL
- JavaScript
- Python
- C#
- PHP
curl -X POST "https://api.arsel.sa/v1/contacts" \
-H "Authorization: Bearer $ARSEL_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: {
Authorization: `Bearer ${process.env.ARSEL_API_KEY}`,
"Content-Type": "application/json",
},
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 data = await response.json();
if (!response.ok) {
throw new Error(`${data.name}: ${data.message}`);
}
console.log(data);
import os
import requests
response = requests.post(
"https://api.arsel.sa/v1/contacts",
headers={"Authorization": f"Bearer {os.environ['ARSEL_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"],
},
)
response.raise_for_status()
print(response.json())
using System.Net.Http.Headers;
using System.Text;
var client = new HttpClient(); // share one instance across requests
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("ARSEL_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.arsel.sa/v1/contacts")
{
Content = new StringContent(
"""
{
"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"]
}
""",
Encoding.UTF8,
"application/json"),
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException($"{(int)response.StatusCode}: {body}");
Console.WriteLine(body);
<?php
$ch = curl_init("https://api.arsel.sa/v1/contacts");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ARSEL_API_KEY"),
"Content-Type: application/json",
],
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"],
]),
]);
$data = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) {
throw new Exception("{$data['name']}: {$data['message']}");
}
print_r($data);
Error Responses
- 422 Validation Error
- 409 Conflict
- 401 Unauthorized
{
"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 API Key"
}