curl --request POST \
--url https://api.withallo.com/v2/api/crm/companies/attributes \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"label": "Industry segment",
"type": "DROPDOWN",
"options": [
{
"label": "SMB"
},
{
"label": "Enterprise"
}
],
"prompt": "Pick the segment that matches the company size mentioned on the call."
}
'import requests
url = "https://api.withallo.com/v2/api/crm/companies/attributes"
payload = {
"label": "Industry segment",
"type": "DROPDOWN",
"options": [{ "label": "SMB" }, { "label": "Enterprise" }],
"prompt": "Pick the segment that matches the company size mentioned on the call."
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
label: 'Industry segment',
type: 'DROPDOWN',
options: [{label: 'SMB'}, {label: 'Enterprise'}],
prompt: 'Pick the segment that matches the company size mentioned on the call.'
})
};
fetch('https://api.withallo.com/v2/api/crm/companies/attributes', 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.withallo.com/v2/api/crm/companies/attributes",
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([
'label' => 'Industry segment',
'type' => 'DROPDOWN',
'options' => [
[
'label' => 'SMB'
],
[
'label' => 'Enterprise'
]
],
'prompt' => 'Pick the segment that matches the company size mentioned on the call.'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"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.withallo.com/v2/api/crm/companies/attributes"
payload := strings.NewReader("{\n \"label\": \"Industry segment\",\n \"type\": \"DROPDOWN\",\n \"options\": [\n {\n \"label\": \"SMB\"\n },\n {\n \"label\": \"Enterprise\"\n }\n ],\n \"prompt\": \"Pick the segment that matches the company size mentioned on the call.\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<api-key>")
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.withallo.com/v2/api/crm/companies/attributes")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"label\": \"Industry segment\",\n \"type\": \"DROPDOWN\",\n \"options\": [\n {\n \"label\": \"SMB\"\n },\n {\n \"label\": \"Enterprise\"\n }\n ],\n \"prompt\": \"Pick the segment that matches the company size mentioned on the call.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.withallo.com/v2/api/crm/companies/attributes")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"label\": \"Industry segment\",\n \"type\": \"DROPDOWN\",\n \"options\": [\n {\n \"label\": \"SMB\"\n },\n {\n \"label\": \"Enterprise\"\n }\n ],\n \"prompt\": \"Pick the segment that matches the company size mentioned on the call.\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"key": "industry_segment",
"label": "Industry segment",
"type": "DROPDOWN",
"options": [
{
"key": "smb",
"label": "SMB"
},
{
"key": "enterprise",
"label": "Enterprise"
}
],
"is_visible": true,
"prompt": "Pick the segment that matches the company size mentioned on the call.",
"should_prompt_override": false,
"source": "allo",
"created_at": "2026-10-07T14:21:08",
"updated_at": "2026-10-07T14:21:08",
"already_existed": false
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}Créer un attribut d'entreprise
Creates a custom attribute on your team’s companies. The key is derived from the label. The call is idempotent: if an attribute with the same key and type already exists, it returns 201 with that attribute and already_existed set to true. options is required for DROPDOWN attributes and rejected for other types.
curl --request POST \
--url https://api.withallo.com/v2/api/crm/companies/attributes \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"label": "Industry segment",
"type": "DROPDOWN",
"options": [
{
"label": "SMB"
},
{
"label": "Enterprise"
}
],
"prompt": "Pick the segment that matches the company size mentioned on the call."
}
'import requests
url = "https://api.withallo.com/v2/api/crm/companies/attributes"
payload = {
"label": "Industry segment",
"type": "DROPDOWN",
"options": [{ "label": "SMB" }, { "label": "Enterprise" }],
"prompt": "Pick the segment that matches the company size mentioned on the call."
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
label: 'Industry segment',
type: 'DROPDOWN',
options: [{label: 'SMB'}, {label: 'Enterprise'}],
prompt: 'Pick the segment that matches the company size mentioned on the call.'
})
};
fetch('https://api.withallo.com/v2/api/crm/companies/attributes', 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.withallo.com/v2/api/crm/companies/attributes",
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([
'label' => 'Industry segment',
'type' => 'DROPDOWN',
'options' => [
[
'label' => 'SMB'
],
[
'label' => 'Enterprise'
]
],
'prompt' => 'Pick the segment that matches the company size mentioned on the call.'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"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.withallo.com/v2/api/crm/companies/attributes"
payload := strings.NewReader("{\n \"label\": \"Industry segment\",\n \"type\": \"DROPDOWN\",\n \"options\": [\n {\n \"label\": \"SMB\"\n },\n {\n \"label\": \"Enterprise\"\n }\n ],\n \"prompt\": \"Pick the segment that matches the company size mentioned on the call.\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<api-key>")
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.withallo.com/v2/api/crm/companies/attributes")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"label\": \"Industry segment\",\n \"type\": \"DROPDOWN\",\n \"options\": [\n {\n \"label\": \"SMB\"\n },\n {\n \"label\": \"Enterprise\"\n }\n ],\n \"prompt\": \"Pick the segment that matches the company size mentioned on the call.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.withallo.com/v2/api/crm/companies/attributes")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"label\": \"Industry segment\",\n \"type\": \"DROPDOWN\",\n \"options\": [\n {\n \"label\": \"SMB\"\n },\n {\n \"label\": \"Enterprise\"\n }\n ],\n \"prompt\": \"Pick the segment that matches the company size mentioned on the call.\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"key": "industry_segment",
"label": "Industry segment",
"type": "DROPDOWN",
"options": [
{
"key": "smb",
"label": "SMB"
},
{
"key": "enterprise",
"label": "Enterprise"
}
],
"is_visible": true,
"prompt": "Pick the segment that matches the company size mentioned on the call.",
"should_prompt_override": false,
"source": "allo",
"created_at": "2026-10-07T14:21:08",
"updated_at": "2026-10-07T14:21:08",
"already_existed": false
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"type": "<string>",
"code": "<string>",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"retry_after_seconds": 123
}
}CRM_WRITEChamps du body
| Champ | Type | Requis | Description |
|---|---|---|---|
label | string | oui | Nom affiché, de 1 à 255 caractères, avec au moins une lettre ou un chiffre |
type | string | oui | TEXT, NUMBER, DATE, BOOLEAN ou DROPDOWN |
options | array | DROPDOWN uniquement | Au moins un objet { label, key? }. Refusé pour les autres types |
prompt | string | non | Consigne que l’AI CRM Manager suit pour remplir cet attribut à partir des appels |
should_prompt_override | boolean | non | Indique si l’IA peut écraser une valeur déjà renseignée |
Dérivation de la clé
Lakey est dérivée du label : minuscules, accents supprimés, séquences de caractères non alphanumériques remplacées par _, tronquée à 64 caractères. Industry segment devient industry_segment. Les clés d’options sont dérivées de la même façon.
Un label qui correspond à une clé réservée est refusé avec ATTRIBUTE_KEY_RESERVED. Les clés réservées sont : id, name, phone_numbers, website, industry, logo_url, last_activity, activity_history, sms_content, created_at et updated_at.
Réponse
Renvoie201 avec l’attribut créé. source vaut allo et already_existed vaut false.
{
"data": {
"key": "industry_segment",
"label": "Industry segment",
"type": "DROPDOWN",
"options": [
{ "key": "smb", "label": "SMB" },
{ "key": "enterprise", "label": "Enterprise" }
],
"is_visible": true,
"prompt": "Choisis le segment correspondant à la taille de l'entreprise mentionnée pendant l'appel.",
"should_prompt_override": false,
"source": "allo",
"created_at": "2026-10-07T14:21:08",
"updated_at": "2026-10-07T14:21:08",
"already_existed": false
}
}
Créer deux fois le même attribut
L’appel peut être répété sans risque. Si un attribut avec la même clé et le même type existe déjà, l’endpoint renvoie201 avec cet attribut et already_existed à true. Rien n’est créé et la définition existante reste inchangée. Si la clé existe avec un autre type, la requête renvoie 409 ATTRIBUTE_KEY_CONFLICT.
Limites
Chaque type dispose d’un nombre fixe d’emplacements par équipe. Lorsqu’un type est plein, créer un autre attribut de ce type renvoie409 ATTRIBUTE_SLOTS_EXHAUSTED. Supprimez un attribut inutilisé ou choisissez un autre type.
Les attributs personnalisés ne sont disponibles que pour les équipes qui utilisent le nouveau modèle de contacts. Les autres équipes reçoivent 404 TEAM_NOT_FOUND.
Erreurs
| Statut | Code | Quand |
|---|---|---|
400 | ATTRIBUTE_LABEL_INVALID | label est vide, dépasse 255 caractères ou ne contient ni lettre ni chiffre |
400 | ATTRIBUTE_KEY_RESERVED | Le label correspond à une clé réservée |
400 | ATTRIBUTE_OPTION_INVALID | Un attribut DROPDOWN n’a aucune option, ou un label d’option est vide, dépasse 255 caractères ou est en doublon |
400 | ATTRIBUTE_OPTIONS_NOT_SUPPORTED | options envoyé pour un type autre que DROPDOWN |
400 | INVALID_REQUEST_BODY | type est absent ou inconnu |
403 | API_KEY_INSUFFICIENT_SCOPE | La clé n’a pas le scope CRM_WRITE |
404 | TEAM_NOT_FOUND | Le workspace n’utilise pas le nouveau modèle de contacts |
409 | ATTRIBUTE_KEY_CONFLICT | Un attribut avec cette clé existe déjà avec un autre type |
409 | ATTRIBUTE_SLOTS_EXHAUSTED | Aucun emplacement libre pour ce type |
409 | ATTRIBUTE_CONCURRENT_MODIFICATION | Les attributs ont été modifiés en même temps. Relancez la requête |
Autorisations
Corps
Display name of the attribute. The attribute key is derived from it: lowercase, accents removed, runs of non-alphanumeric characters replaced by _, truncated to 64 characters. Labels that resolve to a reserved key are rejected.
"Industry segment"
TEXT, NUMBER, DATE, BOOLEAN, DROPDOWN "DROPDOWN"
Allowed values. Required, with at least one option, for DROPDOWN; rejected for other types.
Show child attributes
Show child attributes
Instruction the AI CRM Manager follows to fill this attribute from calls. A blank value clears it.
"Pick the segment that matches the company size mentioned on the call."
Whether the AI may overwrite a value that is already set
Réponse
The attribute that was created, or the existing one when already_existed is true
A custom attribute on companies
Show child attributes
Show child attributes
Cette page vous a-t-elle été utile ?