Chargement…
Espace développeur
Créez une clé dans Agent téléphonique, puis appelez l’API depuis votre serveur pour gérer le profil de l’agent et relier vos outils d’entreprise.
Les clés de test, le profil brouillon, les connecteurs simulés, les registres de contact et leur prévalidation sont disponibles. La prévalidation vérifie seulement la plage fédérale de base selon le fuseau fourni; elle ne vérifie pas la Liste nationale ni les règles locales plus strictes et n’autorise jamais un appel. Le profil de l’agent est partagé par le compte ou l’organisation entre les environnements. Les appels téléphoniques réels et les opérations CRM en production restent désactivés.
01 · Démarrage
curl --request GET 'https://reismaker.ca/api/v1/telephone/capabilities' \
--header "Authorization: Bearer <VOTRE_CLE_TEST>"Une clé porte un environnement (test ou production), une date d’expiration et des permissions. Vous pouvez la révoquer dans le portail; le secret complet ne sera plus récupérable.
const apiKey = process.env.REISMAKER_API_KEY;
if (!apiKey?.startsWith("rmk_tel_test_")) {
throw new Error("Set a ReisMaker Test key in REISMAKER_API_KEY.");
}
const idempotencyKey = process.env.REISMAKER_IDEMPOTENCY_KEY;
if (!idempotencyKey || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/.test(idempotencyKey)) {
throw new Error("Set a stable operation ID in REISMAKER_IDEMPOTENCY_KEY.");
}
const apiBaseUrl = "https://reismaker.ca/api/v1/telephone";
async function request(path, init = {}) {
const response = await fetch(apiBaseUrl + path, {
...init,
headers: {
Authorization: "Bearer " + apiKey,
"Content-Type": "application/json",
...(init.headers ?? {})
}
});
const payload = await response.json().catch(() => null);
if (!response.ok) {
const code = typeof payload?.detail?.code === "string" ? payload.detail.code : "request_failed";
throw new Error("ReisMaker API request failed (" + response.status + ", " + code + ")");
}
return payload;
}
await request("/capabilities");
const created = await request("/calls", {
method: "POST",
headers: { "Idempotency-Key": idempotencyKey },
body: JSON.stringify({
direction: "outbound",
scenario: "Synthetic demo: the prospect already uses a service and asks for facts before discussing."
})
});
if (created.call?.simulated !== true || typeof created.call.id !== "string") {
throw new Error("Expected a simulated test call.");
}
const { call } = await request("/calls/" + encodeURIComponent(created.call.id));
console.log({ id: call.id, status: call.status, simulated: call.simulated });Définissez REISMAKER_API_KEY dans le gestionnaire de secrets du serveur et REISMAKER_IDEMPOTENCY_KEY avec un identifiant stable et unique pour cette opération. Cette clé Test doit avoir les permissions telephone:catalog:read, telephone:calls:write et telephone:calls:read. Enregistrez durablement l’identifiant avant l’envoi et réutilisez-le après un délai ou une erreur réseau; créez-en un nouveau pour une nouvelle opération. Le script ne contacte aucun numéro ni service externe.
En plus du polling, une organisation peut enregistrer des webhooks HTTPS pour recevoir les événements. La livraison réseau exige l’activation du worker avec SAAS_TELEPHONE_WEBHOOK_DELIVERY_ENABLED=true et reste fermée par défaut.
{
"name": "Alex",
"business_name": "Entreprise Démo",
"language": "fr-CA",
"voice": "marin",
"offer_summary": "Service de démonstration. Confirmez les détails avec les faits sourcés ci-dessous.",
"offer_facts": [
{
"claim": "Un essai de 14 jours est offert.",
"source": "Exemple de conditions, section 2"
}
],
"human_contact": {
"name": "Alise Dupont",
"phone": "+15145550123",
"email": "alise.dupont@example.com",
"postal_address": "100, rue Exemple, Montréal (Québec)"
},
"opening_line": "Bonjour, je peux vous présenter le service. Est-ce un bon moment?",
"disclosure": "Je suis une IA, l’assistant virtuel de l’Entreprise Démo."
}curl --request PUT 'https://reismaker.ca/api/v1/telephone/agent' \
--header "Authorization: Bearer <VOTRE_CLE_TEST>" \
--header 'Content-Type: application/json' \
--data-binary @agent.jsonChaque affirmation peut être liée à une source fournie par l’entreprise. ReisMaker ne vérifie pas cette source; gardez le profil à jour et n’y ajoutez que des faits autorisés. Les voix acceptées sont marin, cedar, alloy et verse; Marin et Cedar sont recommandées pour la meilleure qualité. Le champ facultatif human_contact accepte le nom d’un représentant, son téléphone E.164 et son courriel ou son adresse postale. L’agent ne communique ces coordonnées que si la personne demande un représentant ou comment joindre l’entreprise; aucun transfert n’est effectué.
02 · Référence
Base URL : https://reismaker.ca/api/v1/telephone
| Méthode et route | Permission | Usage |
|---|---|---|
GET /capabilities | telephone:catalog:read | Lire les langues, voix, permissions et états disponibles. |
GET /agentPUT /agent | telephone:agents:readtelephone:agents:write | Lire ou modifier le profil brouillon, avec le nom de l’entreprise et la divulgation IA. La publication exige une clé Production d’organisation (owner/manager) et des garde-fous serveur prêts; les appels sortants restent désactivés. |
POST /callsGET /callsGET /calls/{call_id} | telephone:calls:writetelephone:calls:read | Créer une demande simulée ou consulter l’état, les secondes vocales, les tokens Luna et le montant réglé d’un appel. Les pages suivent l’environnement de la clé. |
GET /events | telephone:events:read + clé d’organisation | Lire les événements conservés même sans webhook. Utiliser after avec le curseur suivant; les flux Test et Production sont séparés. |
POST /calls/{call_id}/reconcile | telephone:calls:write + rôle owner/manager | Autoriser une réserve CAD plus élevée pour un reçu complet déjà mesuré qui dépasse la réserve initiale. Le coût exact est ensuite réglé sans relancer l’appel. |
GET /integrationsPOST /integrationsDELETE /integrations/{integration_id} | telephone:integrations:readtelephone:integrations:write | Lister, déclarer ou révoquer une intégration de l’entreprise. |
POST /integrations/{id}/operations/{operation_id} | telephone:integrations:execute + permission métier | Lancer une opération déclarée avec une clé d’idempotence et son schéma d’entrée. |
GET /webhooksPOST /webhooksDELETE /webhooks/{endpoint_id} | telephone:webhooks:readtelephone:webhooks:write + rôle owner/manager | Gérer une destination HTTPS de l’organisation. Le secret de signature est affiché une seule fois à la création. |
GET /webhooks/{endpoint_id}/deliveries | telephone:webhooks:read + rôle owner/manager | Consulter le statut et les tentatives récentes sans exposer le contenu des événements. |
POST /contacts/do-not-call | telephone:contacts:write + clé Production d’organisation et rôle owner/manager | Ajouter un numéro à la liste interne d’exclusion. L’ajout est idempotent et permanent dans la V1. |
POST /contacts/consents | telephone:contacts:write + clé Production d’organisation et rôle owner/manager | Enregistrer des métadonnées de provenance et une attestation de consentement pour un appel de vente par IA. |
POST /contacts/preflight | telephone:contacts:read + clé Production d’organisation et rôle owner/manager | Vérifier l’exclusion interne, les métadonnées du même marché et la fenêtre fédérale de base. campaign_sector et call_reason ajoutent un contrôle limité des appels funéraires au Québec. La route n’autorise jamais un appel. |
GET /transport/sip-trunkPUT /transport/sip-trunkDELETE /transport/sip-trunk | telephone:transport:readtelephone:transport:write + rôle owner/manager | Configurer ou révoquer le trunk SIP de l’organisation avec une clé Production. Les identifiants sont chiffrés et ne sont jamais retournés. |
GET /transport/inbound-routesPOST /transport/inbound-routesDELETE /transport/inbound-routes/{route_id} | telephone:transport:readtelephone:transport:write + rôle owner/manager | Créer, lister ou révoquer les secrets de routage SIP entrant. Le secret n’est révélé qu’une fois. |
Vous pouvez aussi créer et révoquer une route dans Agent téléphonique → Connecter votre système téléphonique, après avoir sélectionné une organisation. Copiez le secret dans le coffre de votre PBX; le PBX doit supprimer tout en-tête fourni par l’appelant puis insérer lui-même X-ReisMaker-Inbound-Route. Le portail ne déclenche pas d’appel.
Les permissions contacts, rendez-vous et commandes s’ajoutent selon les données touchées. Une opération doit posséder à la fois la permission d’exécution et sa permission métier.
Le flux GET /events conserve les événements même si aucun webhook n’est configuré. La clé doit avoir telephone:events:read et appartenir à une organisation. Parcourez-les dans l’ordre avec after et next_cursor; utilisez has_more pour savoir s’il reste une page immédiate.
Les événements disponibles sont telephone.call.created, telephone.call.status_changed, telephone.call.usage_settled et telephone.integration.execution.completed. Chaque requête est signée dans X-ReisMaker-Signature (HMAC-SHA256 sur timestamp, identifiant et corps brut) et contient un Idempotency-Key. Vérifiez la signature sur le corps brut et dédupliquez les livraisons; un événement peut être répété. Les charges utiles excluent les numéros, les transcriptions et les réponses brutes du CRM.
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyWebhook(rawBody, eventId, signatureHeader, secret) {
const [timestampField, signatureField] = String(signatureHeader || "").split(",");
const timestampText = timestampField?.startsWith("t=") ? timestampField.slice(2) : "";
const suppliedHex = signatureField?.startsWith("v1=") ? signatureField.slice(3) : "";
if (!/^\d{1,12}$/.test(timestampText) || !/^[0-9a-f]{64}$/.test(suppliedHex)) return false;
const timestamp = Number(timestampText);
if (!Number.isSafeInteger(timestamp) || Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${timestampText}.${eventId}.`, "utf8")
.update(rawBody)
.digest();
const supplied = Buffer.from(suppliedHex, "hex");
return expected.length === supplied.length && timingSafeEqual(expected, supplied);
}{
"call": {
"id": "<CALL_ID>",
"environment": "live",
"direction": "outbound",
"status": "completed",
"transfer_status": "not_requested",
"scenario": "",
"billing_status": "usage_settled",
"customer_price_usd": 0.06012,
"customer_charge_cents": 9,
"billing_currency": "CAD",
"usage": {
"voice_seconds": 18,
"reasoning": {
"model": "gpt-6-luna",
"response_count": 1,
"input_tokens": 50,
"cached_input_tokens": 0,
"cache_write_tokens": 0,
"output_tokens": 50
}
},
"created_at": "2026-10-08T12:00:00Z",
"updated_at": "2026-10-08T12:00:18Z",
"simulated": false
}
}Exemple synthétique du format : les secondes vocales et les tokens sont détaillés quand le reçu est complet. `usage` vaut null si l’usage final n’est pas vérifié; le montant CAD représente la part arrondie dans le cumul de l’organisation. Les appels réels restent désactivés.
La voix GPT-Live est mesurée à la seconde; le raisonnement GPT-6 Luna est mesuré par tokens. La facturation visée additionne le coût fournisseur réel des composantes, le multiplie par 4 et le convertit en CAD au taux enregistré avec l’appel. Un reçu incomplet ou supérieur à la réserve ne déclenche aucun débit et garde les fonds réservés. Les appels réels sont encore désactivés.
curl --request POST 'https://reismaker.ca/api/v1/telephone/contacts/do-not-call' \
--header "Authorization: Bearer <CLE_PRODUCTION_GESTIONNAIRE>" \
--header 'Content-Type: application/json' \
--data '{"phone_number":"+14165550123","reason":"caller_request"}'Envoyez un numéro E.164 avec reason caller_request ou business_policy. Seule une empreinte HMAC est conservée; le numéro n’est pas retourné. Ne journalisez pas le corps de la requête. Aucun endpoint ne retire une exclusion en V1. Cette liste ne remplace pas le consentement ni la vérification réglementaire, et les appels réels restent fermés.
curl --request POST 'https://reismaker.ca/api/v1/telephone/contacts/consents' \
--header "Authorization: Bearer <CLE_PRODUCTION_GESTIONNAIRE>" \
--header 'Content-Type: application/json' \
--data-binary '{"source_event_id":"<UUID_EVENEMENT_SOURCE>","phone_number":"+14165550123","jurisdiction":"CA-QC","contact_type":"consumer","phone_source":"customer_provided","phone_collected_at":"2026-09-30T15:30:00Z","phone_source_evidence_sha256":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","consent_method":"web_form","consented_at":"2026-10-01T12:00:00Z","consent_scope":"ai_sales_call","consent_evidence_sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","consent_text_sha256":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","attestations":{"number_specific":true,"named_advertiser":true,"sales_calls":true,"ai_voice":true,"phone_source_documented":true}}'La clé Production doit appartenir à une organisation et à un owner/manager. Le client conserve les preuves dans ses systèmes et transmet seulement leurs SHA-256, la provenance déclarée du numéro, la date de collecte, la source et la date du consentement. Les cinq attestations confirment l'accord pour ce numéro et la documentation de sa provenance. Cet enregistrement est une déclaration du client, pas une validation juridique ni une autorisation d’appel. La liste « ne pas appeler » l’emporte toujours; les appels réels restent désactivés.
curl --request POST 'https://reismaker.ca/api/v1/telephone/contacts/preflight' \
--header "Authorization: Bearer <CLE_PRODUCTION_GESTIONNAIRE>" \
--header 'Content-Type: application/json' \
--data '{"phone_number":"+14165550123","jurisdiction":"CA-QC","contact_type":"consumer","recipient_timezone":"America/Toronto","campaign_sector":"business_services","call_reason":"unsolicited_outbound"}'La réponse compare le consentement et l’exclusion pour le même numéro, le même marché et le même type de contact. campaign_sector et call_reason ajoutent un signal limité : la sollicitation funéraire non demandée d’un consommateur au Québec est bloquée, mais une demande déclarée n’est pas vérifiée. sector_specific_rules retourne notamment blocked_no_customer_request ou customer_request_unverified; les autres règles de secteur, l’horaire régional, la Liste nationale, l’identification de l’annonceur et les preuves originales restent à vérifier. La route retourne toujours call_authorized: false. Ne composez pas sur cette base et ne journalisez pas le corps de la requête.
curl --request PUT 'https://reismaker.ca/api/v1/telephone/transport/sip-trunk' \
--header "Authorization: Bearer <CLE_PRODUCTION_GESTIONNAIRE>" \
--header 'Content-Type: application/json' \
--data-binary @sip-trunk.jsonLe fichier sip-trunk.json doit contenir provider_url (sips:), username, password et caller_number en format E.164. Envoyez les identifiants uniquement depuis un serveur de confiance; n’utilisez jamais le JavaScript du navigateur. Cette configuration prépare le transport et n’active pas encore les appels réels.
curl --request POST 'https://reismaker.ca/api/v1/telephone/calls/<CALL_ID>/reconcile' --header "Authorization: Bearer <CLE_PRODUCTION_GESTIONNAIRE>" \ --header 'Content-Type: application/json' --data '{"maximum_customer_charge_cents":22}'Cette route est réservée à une clé Production appartenant à une organisation et à un propriétaire ou gestionnaire. Le montant est un plafond en cents CAD; le débit demeure limité au coût exact du reçu conservé. Une répétition ne crée pas un second débit.
03 · Intégrations
Déclarez un domaine HTTPS et des opérations précises. Chaque opération fixe son chemin, sa méthode, ses schémas d’entrée et de sortie et les permissions requises. L’agent ne peut pas choisir une URL ou un chemin arbitraire à l’exécution.
{
"name": "CRM test",
"base_url": "https://api.example.com",
"auth_mode": "bearer",
"credential": "",
"operations": [{
"operation_id": "find_contact",
"endpoint": "/v1/contacts/search",
"method": "POST",
"input_schema": {
"type": "object",
"properties": {"email": {"type": "string", "maxLength": 254}},
"required": ["email"],
"additionalProperties": false
},
"output_schema": {
"type": "object",
"properties": {"found": {"type": "boolean"}},
"required": ["found"],
"additionalProperties": false
},
"required_scopes": ["telephone:contacts:read"],
"voice_enabled": false
}]
}curl --request POST 'https://reismaker.ca/api/v1/telephone/integrations/<ID>/operations/find_contact' --header 'Authorization: Bearer <CLE_TEST>' --header 'Idempotency-Key: crm-contact-lookup-001' --header 'Content-Type: application/json' --data '{"email":"demo@example.com"}'Les tests valident la configuration et les permissions sans se connecter au CRM. Les intégrations réelles restent fermées par défaut. Lorsqu’une écriture externe a un résultat incertain, ne la rejouez pas : consultez l’état de l’exécution.
Pour permettre une consultation vocale, définissez voice_enabled à true seulement sur une opération GET qui utilise exclusivement des permissions en lecture. Le champ vaut false par défaut; les opérations de test et toutes les écritures restent exclues. La réponse indique les opérations inscrites dans voice_enabled_operation_ids. Cette fonction vocale reste désactivée dans l’état actuel du service.
04 · Contrôle
401 — clé absente, invalide, expirée ou révoquée403 — permission insuffisante404 — ressource absente ou appartenant à un autre compte409 — conflit d’idempotence ou résultat d’écriture incertain422 — requête non conforme au schéma429 — limite de fréquence; respectez Retry-After503 — fonction réelle désactivéeLes réponses d’erreur ne contiennent pas le secret de la clé ni le corps brut d’une réponse CRM.