Retrouver les appels téléphoniques
Lire le journal des appels d'une personne, lister les appels manqués à rappeler, rattacher un appel à la bonne fiche, et appeler depuis le poste d'un utilisateur.
Le journal des appels s'écrit seul : le fournisseur de téléphonie de l'organisation signale chaque appel, entrant ou sortant, et l'appel s'y inscrit. L'API le lit, corrige le rattachement d'un appel à sa fiche, et fait sonner le poste d'un utilisateur pour qu'il appelle un numéro ; elle n'écrit pas d'appel à la main. Un appel n'a pas de droits propres : le voit qui voit son compte de téléphonie.
1Les appels d'une personne
QUERY https://staging-api.docage.com/TelephonyCalls
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{
"Filter": { "PropertyName": "ContactId", "Comparison": "=", "Value": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f" },
"SortOrder": "StartedAtUtc_Desc",
"Skip": 0,
"Take": 20
}curl -X QUERY "https://staging-api.docage.com/TelephonyCalls" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"Filter": {"PropertyName": "ContactId", "Comparison": "=", "Value": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f"},
"SortOrder": "StartedAtUtc_Desc",
"Skip": 0,
"Take": 20
}'using HttpClient client = new();
using HttpRequestMessage request = new(new HttpMethod("QUERY"), "https://staging-api.docage.com/TelephonyCalls");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
{
"Filter": {
"PropertyName": "ContactId",
"Comparison": "=",
"Value": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f"
},
"SortOrder": "StartedAtUtc_Desc",
"Skip": 0,
"Take": 20
}
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/TelephonyCalls", {
method: "QUERY",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"Filter": {"PropertyName": "ContactId", "Comparison": "=", "Value": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f"},
"SortOrder": "StartedAtUtc_Desc",
"Skip": 0,
"Take": 20
})
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;Chaque appel dit :
Name: son intitulé, « Appel entrant +33600000001 — Manqué » ;Direction:1entrant,2sortant,3interne ;Status: son état (ci-dessous) ;FromNumberetToNumber: l'appelant et l'appelé, au format international ;StartedAtUtc,AnsweredAtUtc,EndedAtUtc, etDurationSeconds, la durée de la conversation depuis le décroché ;DocageUserId: le collègue qui a décroché un appel entrant, ou passé un appel sortant ;ContactIdetCompanyId: la fiche de son correspondant.
Status |
État |
|---|---|
1 |
Sonne |
2 |
En cours |
10 |
Terminé |
11 |
Manqué |
12 |
Occupé |
13 |
Échec |
14 |
Messagerie |
Pour une société, filtrez sur CompanyId. Un numéro sans fiche se cherche sur FromNumber (un appel entrant) ou ToNumber (un
appel sortant), avec contains et ses neuf derniers chiffres.
Un appel terminé qui a un correspondant est aussi rangé dans la conversation de ce correspondant, en message « Appel » (Media 5),
avec ses emails et ses SMS : voir Lire les échanges d'une conversation.
2Les appels manqués à rappeler
QUERY https://staging-api.docage.com/TelephonyCalls
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{
"Filter": {
"OperatorType": "And",
"Operands": [
{ "PropertyName": "Direction", "Comparison": "=", "Value": 1 },
{ "PropertyName": "Status", "Comparison": "=", "Value": 11 },
{ "PropertyName": "StartedAtUtc", "Comparison": ">=", "Value": "2026-11-08T23:00:00Z" }
]
},
"SortOrder": "StartedAtUtc_Desc",
"Skip": 0,
"Take": 50
}curl -X QUERY "https://staging-api.docage.com/TelephonyCalls" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"Filter": {"OperatorType": "And", "Operands": [{"PropertyName": "Direction", "Comparison": "=", "Value": 1}, {"PropertyName": "Status", "Comparison": "=", "Value": 11}, {"PropertyName": "StartedAtUtc", "Comparison": ">=", "Value": "2026-11-08T23:00:00Z"}]},
"SortOrder": "StartedAtUtc_Desc",
"Skip": 0,
"Take": 50
}'using HttpClient client = new();
using HttpRequestMessage request = new(new HttpMethod("QUERY"), "https://staging-api.docage.com/TelephonyCalls");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
{
"Filter": {
"OperatorType": "And",
"Operands": [
{
"PropertyName": "Direction",
"Comparison": "=",
"Value": 1
},
{
"PropertyName": "Status",
"Comparison": "=",
"Value": 11
},
{
"PropertyName": "StartedAtUtc",
"Comparison": ">=",
"Value": "2026-11-08T23:00:00Z"
}
]
},
"SortOrder": "StartedAtUtc_Desc",
"Skip": 0,
"Take": 50
}
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/TelephonyCalls", {
method: "QUERY",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"Filter": {"OperatorType": "And", "Operands": [{"PropertyName": "Direction", "Comparison": "=", "Value": 1}, {"PropertyName": "Status", "Comparison": "=", "Value": 11}, {"PropertyName": "StartedAtUtc", "Comparison": ">=", "Value": "2026-11-08T23:00:00Z"}]},
"SortOrder": "StartedAtUtc_Desc",
"Skip": 0,
"Take": 50
})
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;- Les appels qui n'ont pas abouti comptent aussi les états
12(occupé) et14(messagerie) : réunissez-les par un filtreOrsurStatus. - Le correspondant de chaque appel est sa fiche,
ContactIdouCompanyId; sans fiche, son numéro,FromNumber.
3Rattacher un appel à la bonne fiche
PUT https://staging-api.docage.com/TelephonyCalls/0c5a8e31-0000-4000-8000-000000001001
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{ "ContactId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f", "CompanyId": "a4e0c7d2-0000-4000-8000-000000001101" }curl -X PUT "https://staging-api.docage.com/TelephonyCalls/0c5a8e31-0000-4000-8000-000000001001" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"ContactId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f",
"CompanyId": "a4e0c7d2-0000-4000-8000-000000001101"
}'using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Put, "https://staging-api.docage.com/TelephonyCalls/0c5a8e31-0000-4000-8000-000000001001");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
{
"ContactId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f",
"CompanyId": "a4e0c7d2-0000-4000-8000-000000001101"
}
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/TelephonyCalls/0c5a8e31-0000-4000-8000-000000001001", {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"ContactId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f",
"CompanyId": "a4e0c7d2-0000-4000-8000-000000001101"
})
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;- Ce qui se corrige : le contact, la société, l'intitulé (
Name) et les notes (Notes).nulldétache une fiche. - Le sens, l'état, les numéros, les dates, la durée, le poste et le collègue sont écrits par le serveur : envoyés, ils sont ignorés sans erreur.
422: « Contact introuvable » ou « Société introuvable » — une fiche d'une autre organisation, ou un modèle —, ou une société que l'utilisateur ne voit pas.- Le rattachement corrigé lie l'appel à la fiche ; il ne déplace pas son message « Appel » d'une conversation à une autre.
4Appeler depuis le poste d'un utilisateur
L'appel part du poste téléphonique de l'utilisateur authentifié : son poste sonne, et le numéro est composé quand il décroche. Les postes d'où il peut appeler :
GET https://staging-api.docage.com/TelephonyCalls/DialLines
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl "https://staging-api.docage.com/TelephonyCalls/DialLines" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Get, "https://staging-api.docage.com/TelephonyCalls/DialLines");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/TelephonyCalls/DialLines", {
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;[
{ "Id": "5e0b7c12-0000-4000-8000-000000001201", "Name": "Poste de Claire", "Extension": "201", "PhoneNumber": "+33100000201", "IsRegistered": true, "Provider": 2 }
]- Ce sont ses postes actifs, sur un compte de téléphonie vérifié dont le fournisseur sait lancer un appel ;
IsRegistered: un téléphone y est branché. Ceux-là viennent d'abord. - Une liste vide : l'utilisateur ne peut pas appeler.
Puis l'appel, depuis la fiche du contact à joindre :
POST https://staging-api.docage.com/TelephonyCalls/Dial
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{ "Number": "+33600000001", "ContactId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f" }curl -X POST "https://staging-api.docage.com/TelephonyCalls/Dial" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"Number": "+33600000001",
"ContactId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f"
}'using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/TelephonyCalls/Dial");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
{
"Number": "+33600000001",
"ContactId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f"
}
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/TelephonyCalls/Dial", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"Number": "+33600000001",
"ContactId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f"
})
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;{ "CallId": "0c5a8e31-0000-4000-8000-000000001002", "AlreadyRequested": false }Number: au format international, ou national du pays du compte de téléphonie. Un numéro surtaxé, à coûts partagés ou non reconnu n'est jamais composé (422).ContactId, ouCompanyIdpour une société : la fiche à laquelle l'appel se rattache, de l'organisation de l'utilisateur et visible de lui — sinon404, que la fiche n'existe pas ou qu'il ne la voie pas. Le contact apporte aussi sa société.LineId: le poste, l'un de la liste ci-dessus ; omis, le premier. Un poste qui n'est pas le sien répond422, comme un utilisateur qui n'en a aucun.- La réponse est l'appel inscrit au journal, AVANT même que le fournisseur l'accepte : lisez son état par la liste du § 1 ou
GET https://staging-api.docage.com/TelephonyCalls/{id}. Redemandé vers le même numéro depuis le même poste dans les 15 secondes, il est rendu tel quel (AlreadyRequestedàtrue) et le poste ne sonne pas une seconde fois. 409: les identifiants du compte de téléphonie du poste manquent — un administrateur doit les renseigner de nouveau.502: le fournisseur refuse l'appel, conclu alors en échec (Status13), ou ne répond pas — l'appel est peut-être en cours sur le poste, et le journal le suivra.
Comment le serveur rattache un appel
À son arrivée, un appel est rapproché d'une fiche par le numéro de son correspondant — l'appelant d'un appel entrant, l'appelé d'un appel sortant —, à l'identique, au format international :
- un seul contact dont le téléphone ou le mobile est ce numéro : ce contact, et sa société ;
- plusieurs contacts d'une même société (un standard) : la société seule ;
- sinon, une seule société dont c'est le téléphone : cette société ;
- sinon, personne — et personne non plus quand plus de 20 fiches portent ce numéro.
Un rattachement posé, par le serveur ou par vous, n'est jamais remplacé par le serveur.
Les pièges
- Créer ou dupliquer un appel est refusé : le journal ne s'écrit que par le fournisseur. Pour appeler, faites sonner le poste de l'utilisateur (§ 4) : l'appel s'inscrit alors de lui-même.
- On n'appelle que depuis son propre poste : la clé d'API ou le jeton d'un utilisateur ne fait sonner que les postes qui lui sont attribués.
- Le rapprochement compare des numéros au format international (
+33600000001), celui où les fiches les gardent : un appel à un numéro court, interne ou masqué ne se rapproche de personne. - Agir sur un appel — rappeler un appel manqué, par exemple — : un flux, déclencheurs
8(appel reçu) et9(appel manqué).