{"id":211313,"date":"2026-09-25T10:24:19","date_gmt":"2026-09-25T10:24:19","guid":{"rendered":"https:\/\/sinch.com\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/"},"modified":"2026-09-25T10:24:19","modified_gmt":"2026-09-25T10:24:19","slug":"comment-j-ai-construit-stack-communication-cursor-plugin-sinch","status":"publish","type":"blog","link":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/","title":{"rendered":"Comment j&rsquo;ai construit une stack de communication dans Cursor avec le plugin Sinch"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Une int\u00e9gration de communication est rarement lente \u00e0 cause de l&rsquo;envoi en lui-m\u00eame. Le temps passe dans la structure des charges utiles, un\u00a0syst\u00e8me d&rsquo;authentification diff\u00e9rent pour chaque produit, et le d\u00e9calage entre ce qui fonctionne en environnement de test et\u00a0ce qui fonctionne sur le t\u00e9l\u00e9phone que vous avez entre les mains.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">J&rsquo;en ai suffisamment d\u00e9velopp\u00e9 sur les API Sinch pour savoir o\u00f9 passe ce temps, ce qui rend le\u00a0<a href=\"https:\/\/cursor.com\/marketplace\/sinch\/sinch-cursor-plugin\" target=\"_blank\" rel=\"noreferrer noopener\">plugin Sinch pour Cursor<\/a> valait la peine d&rsquo;\u00eatre test\u00e9\u00a0correctement\u00a0: je peux d\u00e9terminer si le code fourni par un agent est exact. Je me suis donc accord\u00e9 une apr\u00e8s-midi\u00a0et une banque de d\u00e9monstration sur laquelle m&rsquo;appuyer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">RockBank est cette banque de d\u00e9monstration\u00a0: un pr\u00eateur fictif dont la client\u00e8le ignore les rappels de paiement par courrier direct.\u00a0L&rsquo;objectif \u00e9tait de transf\u00e9rer les rappels vers les canaux que les destinataires lisent r\u00e9ellement, et de conserver l&rsquo;ensemble de la\u00a0messagerie derri\u00e8re un seul chemin d&rsquo;envoi et un seul point de terminaison des webhooks. Ainsi, l&rsquo;ajout d&rsquo;un canal devient une modification de\u00a0configuration et non une r\u00e9\u00e9criture compl\u00e8te.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ce que fait le flux<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Un rappel de paiement par SMS envoy\u00e9 via l&rsquo;API de conversation de Sinch<\/li>\n\n\n\n<li>Le m\u00eame rappel mis \u00e0 niveau vers le RCS, avec un exp\u00e9diteur v\u00e9rifi\u00e9 et un bouton \u00ab\u00a0Payer maintenant\u00a0\u00bb dans la discussion<\/li>\n\n\n\n<li>Un rappel vocal automatis\u00e9 si le paiement est toujours en attente<\/li>\n\n\n\n<li>Une v\u00e9rification d&rsquo;identit\u00e9 \u00e0 l&rsquo;aide d&rsquo;un mot de passe \u00e0 usage unique avant la validation du paiement<\/li>\n\n\n\n<li>Une confirmation par email via Mailgun une fois le paiement effectu\u00e9<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Il s&rsquo;agit donc de la messagerie, de la voix, de la v\u00e9rification et de l&#8217;email derri\u00e8re un seul chemin d&rsquo;envoi et un webhook entrant.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Les pr\u00e9requis de la configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Le code de RockBank n&rsquo;est pas publi\u00e9, il ne s&rsquo;agit donc pas d&rsquo;un mod\u00e8le pr\u00eat \u00e0 cloner. Voici la configuration du compte\u00a0sur laquelle s&rsquo;appuient les extraits, et celle dont vous auriez besoin pour concevoir votre propre version\u00a0:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Cursor avec le plugin Sinch install\u00e9, ainsi qu&rsquo;un num\u00e9ro de t\u00e9l\u00e9phone de test vous appartenant<\/li>\n\n\n\n<li>Une application de l&rsquo;API de conversation\u00a0: ID du projet, ID de la cl\u00e9, secret de la cl\u00e9 et ID de l&rsquo;application<\/li>\n\n\n\n<li>Des identifiants distincts par produit\u00a0: une cl\u00e9 et un secret d&rsquo;application vocale avec un num\u00e9ro attribu\u00e9,\u00a0ainsi qu&rsquo;une cl\u00e9 et un secret d&rsquo;application de v\u00e9rification, qui est elle-m\u00eame une application \u00e0 part enti\u00e8re<\/li>\n\n\n\n<li>Un agent RCS approuv\u00e9 pour votre march\u00e9, avec le SMS configur\u00e9 sur la m\u00eame application pour le repli. L&rsquo;approbation des op\u00e9rateurs\u00a0prend du temps, alors lancez-vous t\u00f4t. Un exp\u00e9diteur SMS ne le remplace pas\u00a0: sans agent approuv\u00e9,\u00a0chaque rappel arrive sous forme de texte brut.<\/li>\n\n\n\n<li><a href=\"https:\/\/developers.sinch.com\/docs\/10dlc-registration\/\" target=\"_blank\" rel=\"noreferrer noopener\">Enregistrement 10DLC<\/a> en cours si vous envoyez vers des\u00a0num\u00e9ros am\u00e9ricains. Pour en savoir plus, consultez la section Pi\u00e8ges \u00e0 \u00e9viter.<\/li>\n<\/ul>\n\n\n\r\n    <nav data-content-type=\"longform\" class=\"toc-block longform-spacings px-5 py-6 px-md-6 px-lg-7 py-md-7 bg-light fs-sm rounded-lg\" aria-labelledby=\"toc-title-1776\"><p class=\"h5 m-0\" id=\"toc-title-1776\">Table des mati\u00e8res<\/p><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">01<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#ce-que-fait-le-flux\">Ce que fait le flux<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">02<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#les-prerequis-de-la-configuration\">Les pr\u00e9requis de la configuration<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">03<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#configuration-du-plugin\">Configuration du plugin<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">04<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#creation-du-chemin-denvoi-sur-lapi-de-conversation\">Cr\u00e9ation du chemin d&rsquo;envoi sur l&rsquo;API de conversation<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">05<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#test-depuis-lediteur\">Test depuis l&rsquo;\u00e9diteur<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">06<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#liaison-du-webhook-entrant\">Liaison du webhook entrant<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">07<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#ajout-du-rcs\">Ajout du RCS<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">08<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#voix-verification-et-email\">Voix, v\u00e9rification et email<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">09<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#pieges-a-eviter\">Pi\u00e8ges \u00e0 \u00e9viter<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">10<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#ce-que-jai-finalement-obtenu\">Ce que j&rsquo;ai finalement obtenu<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">11<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#essayez-par-vous-meme\">Essayez par vous-m\u00eame<\/a><\/div><\/div><div class=\"d-flex mt-3\"><div class=\"w-auto fw-bold text-accent d-flex me-2\">12<\/div><div class=\"d-flex flex-column\"><a class=\"fw-bold scrollme link-body-color text-accent\" href=\"#ressources-complementaires\">Ressources compl\u00e9mentaires<\/a><\/div><\/div><\/nav>\n\n\n<h2 class=\"wp-block-heading\">Configuration du plugin<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Installez-le depuis la Marketplace Cursor ou via la palette de commandes\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-light\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p><!-- \/wp:paragraph --><\/p><p><!-- \/wp:paragraph --><\/p><p class=\"mb-0\">\/add-plugin sinch-cursor-plugin<\/p><\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Le plugin se compose de trois parties qui ont des r\u00f4les diff\u00e9rents\u00a0:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Les <strong>Skills<\/strong> chargent les connaissances de l&rsquo;API Sinch dans le contexte de l&rsquo;agent avant qu&rsquo;il n&rsquo;\u00e9crive le code\u00a0: points de terminaison, sch\u00e9mas d&rsquo;authentification, formes des charges utiles, propri\u00e9t\u00e9s des canaux. C&rsquo;est cette partie qui d\u00e9termine si le code g\u00e9n\u00e9r\u00e9 se compile avec la v\u00e9ritable API ou avec une invention \u00e0 l&rsquo;apparence plausible.<\/li>\n\n\n\n<li>Les <strong>commandes<\/strong> (comme <code>\/send-message<\/code> et <code>\/list-webhooks<\/code>) appellent les API Sinch directement depuis le chat, sans aucun code interm\u00e9diaire.<\/li>\n\n\n\n<li><strong>Les serveurs MCP<\/strong>, au nombre de deux. <code>sinch<\/code> ex\u00e9cute <code>npx -y @sinch\/mcp<\/code> localement et connecte l&rsquo;agent \u00e0 votre compte en direct pour lui permettre d&rsquo;envoyer des messages et d&rsquo;inspecter la configuration pendant que vous travaillez. <code>sinch-docs<\/code> est distant, \u00e0 l&rsquo;adresse <code>developers.sinch.com\/mcp<\/code>, pour la recherche dans la documentation. Seul le premier n\u00e9cessite des identifiants.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Les identifiants constituent l&rsquo;unique \u00e9tape de la configuration pour laquelle il convient d&rsquo;\u00eatre pr\u00e9cis. Cinq variables doivent figurer dans un bloc <code>env<\/code> de <code>~\/.cursor\/settings.json<\/code>, et Cursor doit \u00eatre red\u00e9marr\u00e9 pour les prendre en compte\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                Shell                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copier dans le presse-papiers\" data-clipboard-success-label=\"Copi\u00e9\u00a0!\" data-clipboard-error-label=\"Impossible de copier\" data-tooltip=\"Copier dans le presse-papiers\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copier dans le presse-papiers\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-shell\">\r\n                                <code id=\"code_0_content\" class=\"language-shell\">\r\n\r\n                                    {\r\n  &quot;env&quot;: {\r\n    &quot;CONVERSATION_PROJECT_ID&quot;: &quot;your-project-id&quot;,\r\n    &quot;CONVERSATION_KEY_ID&quot;: &quot;your-key-id&quot;,\r\n    &quot;CONVERSATION_KEY_SECRET&quot;: &quot;your-key-secret&quot;,\r\n    &quot;CONVERSATION_REGION&quot;: &quot;us&quot;,\r\n    &quot;CONVERSATION_APP_ID&quot;: &quot;your-app-id&quot;\r\n  }\r\n}\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Si le serveur signale toujours des identifiants manquants, exportez ces cinq m\u00eames variables dans votre\u00a0profil shell, puis quittez et relancez compl\u00e8tement l&rsquo;application\u00a0: elle s&rsquo;ex\u00e9cute avec la commande <code>npx -y @sinch\/mcp<\/code> et h\u00e9rite de l&rsquo;environnement\u00a0du shell \u00e0 partir duquel Cursor a d\u00e9marr\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ces cinq variables appartiennent au serveur MCP et couvrent l&rsquo;API de conversation. Ainsi, la cl\u00e9 et le secret vocaux\u00a0ne sont jamais saisis dans Cursor. Les appels vocaux se d\u00e9clenchent uniquement lors de l&rsquo;ex\u00e9cution de votre propre code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Confirmez la connexion avant d&rsquo;\u00e9crire quoi que ce soit\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-light\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p class=\"mb-0\">\/send-message &#8211;to=+15551234567 &#8211;message=\u00a0\u00bbRockBank test\u00a0\u00bb<\/p><\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">La m\u00eame commande accepte une cha\u00eene de repli, ce qui permet de pr\u00e9visualiser de mani\u00e8re utile ce que le chemin d&rsquo;envoi fera\u00a0par la suite\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-light\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p class=\"mb-0\">\/send-message &#8211;to=+15551234567 &#8211;message=\u00a0\u00bbRockBank test\u00a0\u00bb &#8211;fallback=RCS,SMS<\/p><\/div>\r\n    <\/div>\r\n\n\n\n<h2 class=\"wp-block-heading\">Cr\u00e9ation du chemin d&rsquo;envoi sur l&rsquo;API de conversation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La forme du chemin d&rsquo;envoi est la d\u00e9cision dont d\u00e9pend tout le reste. L&rsquo;API de conversation prend\u00a0un corps de message g\u00e9n\u00e9rique et le transcode par canal. Elle traite la s\u00e9lection des canaux et le repli\u00a0comme des donn\u00e9es li\u00e9es \u00e0 la requ\u00eate et non comme des ramifications dans votre code. Pour RockBank, c&rsquo;est toute l&rsquo;architecture de la conception\u00a0:\u00a0le rappel part en RCS suivi d&rsquo;un SMS, la voix s&rsquo;y ajoute plus tard, et le site d&rsquo;appel n&rsquo;en sait\u00a0jamais rien.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il est pr\u00e9f\u00e9rable de pr\u00e9ciser cela dans le prompt plut\u00f4t que de laisser le syst\u00e8me le d\u00e9duire. Demandez \u00e0 un agent d&rsquo;\u00ab\u00a0envoyer un SMS\u00a0\u00e0 la client\u00e8le \u00e0 l&rsquo;\u00e9ch\u00e9ance d&rsquo;un paiement\u00a0\u00bb et vous obtiendrez probablement un chemin d&rsquo;envoi bas\u00e9 sur un seul canal,\u00a0car c&rsquo;est ce que vous avez demand\u00e9. Indiquez plut\u00f4t l&rsquo;intention et laissez le Skill g\u00e9rer le m\u00e9canisme\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-sand\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p class=\"mb-0\">Impl\u00e9mentez le chemin d&rsquo;envoi des rappels sur l&rsquo;API de conversation de Sinch. Envoyez un RCS avec un repli sur le SMS, et maintenez la stabilit\u00e9 de l&rsquo;interface pour que l&rsquo;ajout ult\u00e9rieur d&rsquo;un canal ne modifie aucun site d&rsquo;appel. G\u00e9rez \u00e9galement les accus\u00e9s de r\u00e9ception et enregistrez-les.<\/p><\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Aucun nom de champ, aucun point de terminaison, aucun sch\u00e9ma d&rsquo;authentification. Le Skill <code>conversation-api<\/code> indique d\u00e9j\u00e0 comment fonctionne le repli\u00a0: ajoutez un tableau <code>channel_priority_order<\/code> et listez chaque identit\u00e9 de canal sur le destinataire. C&rsquo;est cette\u00a0division que vous devez viser dans vos propres prompts. Vous d\u00e9crivez le comportement souhait\u00e9 et les\u00a0contraintes \u00e0 respecter, et le Skill fournit la charge utile.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cette derni\u00e8re clause accomplit un v\u00e9ritable travail. Sans elle, vous obtenez un chemin d&rsquo;envoi sans observabilit\u00e9, et vous d\u00e9couvrez\u00a0son fonctionnement la premi\u00e8re fois qu&rsquo;une personne de votre client\u00e8le vous signale que le rappel n&rsquo;est jamais arriv\u00e9. Gr\u00e2ce \u00e0 elle, l&rsquo;agent doit\u00a0prendre en compte ce qui se passe une fois que <code>messages:send<\/code> renvoie un code 200, l\u00e0 o\u00f9 se trouvent les\u00a0\u00e9checs int\u00e9ressants.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le r\u00e9sultat renvoy\u00e9 est une m\u00e9thode priv\u00e9e qui prend n&rsquo;importe quel corps de message de l&rsquo;API de conversation, avec le repli\u00a0exprim\u00e9 par <code>channel_priority_order<\/code> et les deux identit\u00e9s de canal sur le destinataire\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                Shell                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copier dans le presse-papiers\" data-clipboard-success-label=\"Copi\u00e9\u00a0!\" data-clipboard-error-label=\"Impossible de copier\" data-tooltip=\"Copier dans le presse-papiers\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copier dans le presse-papiers\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-shell\">\r\n                                <code id=\"code_0_content\" class=\"language-shell\">\r\n\r\n                                    async def _send(\r\n    self,\r\n    to: str,\r\n    message: dict[str, Any],\r\n    channel_properties: dict[str, str] | None = None,\r\n) -&gt; dict[str, Any]:\r\n    &quot;&quot;&quot;Send any Conversation API message over RCS, falling back to SMS.&quot;&quot;&quot;\r\n    properties = {&quot;SMS_SENDER&quot;: self.sms_sender} if self.sms_sender else {}\r\n    properties.update(channel_properties or {})\r\n\r\n    payload: dict[str, Any] = {\r\n        &quot;app_id&quot;: self.app_id,\r\n        &quot;recipient&quot;: {\r\n            &quot;identified_by&quot;: {\r\n                &quot;channel_identities&quot;: [\r\n                    {&quot;channel&quot;: &quot;RCS&quot;, &quot;identity&quot;: to},\r\n                    {&quot;channel&quot;: &quot;SMS&quot;, &quot;identity&quot;: to},\r\n                ]\r\n            }\r\n        },\r\n        &quot;channel_priority_order&quot;: [&quot;RCS&quot;, &quot;SMS&quot;],\r\n        &quot;message&quot;: message,\r\n    }\r\n    if properties:\r\n        payload[&quot;channel_properties&quot;] = properties\r\n\r\n    path = f&quot;\/v1\/projects\/{self.project_id}\/messages:send&quot;\r\n    response = await self.client.post(path, auth=self._auth, json=payload)\r\n    if response.is_error:\r\n        raise SinchClientError(\r\n            f&quot;messages:send failed ({response.status_code}): {response.text}&quot;\r\n        )\r\n    return response.json()\r\n\r\n\r\nasync def send_reminder(self, to: str, text: str) -&gt; dict[str, Any]:\r\n    return await self._send(to, {&quot;text_message&quot;: {&quot;text&quot;: text}})\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Chaque canal qui suit celui-ci modifie le corps du <code>message<\/code> qu&rsquo;il transmet \u00e0 <code>_send<\/code>, et rien d&rsquo;autre.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La propri\u00e9t\u00e9 du canal <code>SMS_SENDER<\/code> est pr\u00e9sente, car le SMS a besoin d&rsquo;un exp\u00e9diteur, \u00e0 moins que l&rsquo;application n&rsquo;en ait un par d\u00e9faut. Cela vient \u00e9galement du Skill.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test depuis l&rsquo;\u00e9diteur<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">C&rsquo;est pr\u00e9cis\u00e9ment le r\u00f4le du serveur MCP. Une fois le client r\u00e9dig\u00e9 et le fichier toujours ouvert, j&rsquo;ai demand\u00e9\u00a0un v\u00e9ritable envoi\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-sand\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p><!-- \/wp:paragraph --><\/p><p class=\"mb-0\">Envoyez un rappel de test via ce service vers mon num\u00e9ro, avec une \u00e9ch\u00e9ance dans trois jours et un montant de 240\u00a0$.<\/p><\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Un message en direct via un compte en direct, depuis l&rsquo;\u00e9diteur\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                Shell                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copier dans le presse-papiers\" data-clipboard-success-label=\"Copi\u00e9\u00a0!\" data-clipboard-error-label=\"Impossible de copier\" data-tooltip=\"Copier dans le presse-papiers\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copier dans le presse-papiers\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-shell\">\r\n                                <code id=\"code_0_content\" class=\"language-shell\">\r\n\r\n                                    {\r\n  &quot;message_id&quot;: &quot;01KY292FGM58WNNBETKE1Q4NF8&quot;,\r\n  &quot;accepted_time&quot;: &quot;2026-07-21T12:05:38.964Z&quot;\r\n}\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Cette r\u00e9ponse signifie qu&rsquo;il a \u00e9t\u00e9 accept\u00e9, et non livr\u00e9. La livraison s&rsquo;effectue de mani\u00e8re asynchrone et s&rsquo;affiche sur le\u00a0webhook. L&rsquo;\u00e9tape suivante consiste donc \u00e0 s&rsquo;assurer que le webhook y est bien inscrit.<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Liaison du webhook entrant<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Un seul point de terminaison achemine le trafic dans les deux sens\u00a0: <code>MESSAGE_DELIVERY<\/code> pour les accus\u00e9s de r\u00e9ception et\u00a0<code>MESSAGE_INBOUND<\/code> pour les r\u00e9ponses de la client\u00e8le. Enregistrez-le avec une commande\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-light\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p class=\"mb-0\">\/create-webhook &#8211;target=https:\/\/rockbank.example.com\/sinch\/callbacks \\<br>  &#8211;triggers=MESSAGE_DELIVERY,MESSAGE_INBOUND &#8211;secret=$SINCH_WEBHOOK_SECRET<\/p><\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">La cible doit \u00eatre en HTTPS et accessible depuis Internet. Par cons\u00e9quent, pour un travail en local, placez un tunnel devant\u00a0votre gestionnaire et enregistrez l&rsquo;URL du tunnel. Le secret est facultatif sur l&rsquo;API, mais transmettez-en un\u00a0: c&rsquo;est\u00a0lui qui signe les rappels, il doit correspondre \u00e0 la valeur que votre gestionnaire v\u00e9rifie, et il n&rsquo;y a aucune\u00a0signature \u00e0 contr\u00f4ler sans lui. Une application prend en charge jusqu&rsquo;\u00e0 cinq webhooks, et le r\u00e9enregistrement de la m\u00eame cible\u00a0renvoie une erreur 400.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">V\u00e9rifiez ensuite \u00e0 quoi l&rsquo;application est r\u00e9ellement inscrite, ce qui n&rsquo;est pas toujours ce que vous pensez avoir demand\u00e9\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-light\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p class=\"mb-0\">\/list-webhooks<br>\/list-webhook-triggers<\/p><\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Il est conseill\u00e9 de le faire avant de faire confiance \u00e0 quoi que ce soit en aval. Un webhook enregistr\u00e9 uniquement avec <code>MESSAGE_INBOUND<\/code>\u00a0accepte votre inscription sans probl\u00e8me. Votre gestionnaire d&rsquo;accus\u00e9s de r\u00e9ception reste alors inactif et ne s&rsquo;ex\u00e9cute jamais.\u00a0Le chemin d&rsquo;envoi semble sain et le journal de livraison reste vide.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le gestionnaire lui-m\u00eame n\u00e9cessite une v\u00e9rification de la signature avant d&rsquo;examiner le corps\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-sand\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p class=\"mb-0\">\u00c9crivez le gestionnaire FastAPI pour le point de terminaison <code>\/sinch\/callbacks<\/code>. V\u00e9rifiez la signature HMAC-SHA256 envoy\u00e9e par Sinch \u00e0 chaque rappel avant de traiter le corps. Rejetez les requ\u00eates contenant un horodatage obsol\u00e8te ou r\u00e9utilis\u00e9, et routez les accus\u00e9s de r\u00e9ception et les messages entrants vers des gestionnaires distincts.<\/p><\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Sinch signe les <a href=\"https:\/\/developers.sinch.com\/docs\/conversation\/callbacks\" target=\"_blank\" rel=\"noreferrer noopener\">rappels<\/a>  avec HMAC-SHA256\u00a0sur le corps brut, le nonce et l&rsquo;horodatage\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                Shell                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copier dans le presse-papiers\" data-clipboard-success-label=\"Copi\u00e9\u00a0!\" data-clipboard-error-label=\"Impossible de copier\" data-tooltip=\"Copier dans le presse-papiers\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copier dans le presse-papiers\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-shell\">\r\n                                <code id=\"code_0_content\" class=\"language-shell\">\r\n\r\n                                    import base64\r\nimport hashlib\r\nimport hmac\r\nimport time\r\n\r\nMAX_CALLBACK_AGE_SECONDS = 300\r\n\r\n@router.post(&quot;\/sinch\/callbacks&quot;)\r\nasync def handle_callback(request: Request) -&gt; Response:\r\n    raw_body = await request.body()\r\n    signature = request.headers.get(&quot;x-sinch-webhook-signature&quot;, &quot;&quot;)\r\n    nonce = request.headers.get(&quot;x-sinch-webhook-signature-nonce&quot;, &quot;&quot;)\r\n    timestamp = request.headers.get(&quot;x-sinch-webhook-signature-timestamp&quot;, &quot;&quot;)\r\n\r\n    try:\r\n        age = abs(time.time() - int(timestamp))\r\n    except ValueError:\r\n        raise HTTPException(status_code=401, detail=&quot;Bad timestamp&quot;)\r\n    if age &gt; MAX_CALLBACK_AGE_SECONDS:\r\n        raise HTTPException(status_code=401, detail=&quot;Stale callback&quot;)\r\n\r\n    signed_data = raw_body + b&quot;.&quot; + nonce.encode() + b&quot;.&quot; + timestamp.encode()\r\n    expected = base64.b64encode(\r\n        hmac.new(\r\n            settings.webhook_secret.encode(), signed_data, hashlib.sha256\r\n        ).digest()\r\n    ).decode()\r\n    if not hmac.compare_digest(expected, signature):\r\n        raise HTTPException(status_code=401, detail=&quot;Invalid signature&quot;)\r\n\r\n    payload = json.loads(raw_body)\r\n    if &quot;message_delivery_report&quot; in payload:\r\n        await record_delivery(payload[&quot;message_delivery_report&quot;])\r\n    elif &quot;message&quot; in payload:\r\n        await record_inbound_message(payload[&quot;message&quot;])\r\n    return Response(status_code=200)\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Il y a quelques points \u00e0 bien ma\u00eetriser ici. La cha\u00eene sign\u00e9e est le corps brut, le nonce et l&rsquo;horodatage\u00a0reli\u00e9s par des points, dans cet ordre, et le condens\u00e9 est en base64 plut\u00f4t qu&rsquo;en hexad\u00e9cimal. Calculez-le sur les octets\u00a0bruts, et non sur un dictionnaire res\u00e9rialis\u00e9, sinon il ne correspondra jamais. La cl\u00e9 correspond au secret que vous avez d\u00e9fini lors de la cr\u00e9ation de votre\u00a0webhook, et <code>x-sinch-webhook-signature-algorithm<\/code> vous indique l&rsquo;algorithme utilis\u00e9\u00a0(<code>HmacSHA256<\/code> aujourd&rsquo;hui).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une signature valide \u00e0 elle seule ne vous garantit pas que la requ\u00eate est r\u00e9cente. Toute personne qui capture un rappel\u00a0sign\u00e9 peut l&rsquo;envoyer \u00e0 nouveau et le HMAC sera toujours valide. C&rsquo;est pr\u00e9cis\u00e9ment la fonction de l&rsquo;horodatage et du nonce\u00a0: Sinch d\u00e9crit le nonce comme \u00e9tant unique par rappel pour cette raison exacte. La fen\u00eatre de\u00a0cinq minutes ci-dessus est mon propre choix, et non une valeur document\u00e9e. Il s&rsquo;agit de la solution de facilit\u00e9. Si vous\u00a0avez besoin d&rsquo;un traitement strict \u00e0 usage unique, mettez en cache les nonces que vous avez d\u00e9j\u00e0 accept\u00e9s pendant la dur\u00e9e de cette\u00a0fen\u00eatre et rejetez les r\u00e9p\u00e9titions. La gestion idempotente est \u00e9galement recommand\u00e9e, car Sinch effectue de nouvelles tentatives avec\u00a0un d\u00e9lai de r\u00e9tablissement exponentiel et vous verrez l\u00e9gitimement le m\u00eame accus\u00e9 de r\u00e9ception deux fois. Toutefois, l&rsquo;idempotence ne constitue pas\u00a0une protection contre la relecture\u00a0: elle emp\u00eache un rappel rejou\u00e9 de corrompre votre \u00e9tat sans jamais vous indiquer\u00a0qu&rsquo;il a \u00e9t\u00e9 rejou\u00e9.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ajout du RCS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Le chemin d&rsquo;envoi \u00e9tant d\u00e9j\u00e0 ind\u00e9pendant du canal, le RCS est un type de message et une propri\u00e9t\u00e9 de canal\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-sand\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p class=\"mb-0\">Mettez \u00e0 niveau le rappel vers une rich card RCS avec l&rsquo;exp\u00e9diteur v\u00e9rifi\u00e9 RockBank, un r\u00e9capitulatif de paiement et un bouton \u00ab\u00a0Payer maintenant\u00a0\u00bb qui ouvre notre page de paiement h\u00e9berg\u00e9e. Conservez le repli sur le SMS pour les appareils qui ne prennent pas en charge le RCS.<\/p><\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">La carte est un <code>card_message<\/code> avec un choix <code>url_message<\/code> dessus, transmis au m\u00eame <code>_send<\/code> qu&rsquo;auparavant\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                Shell                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copier dans le presse-papiers\" data-clipboard-success-label=\"Copi\u00e9\u00a0!\" data-clipboard-error-label=\"Impossible de copier\" data-tooltip=\"Copier dans le presse-papiers\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copier dans le presse-papiers\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-shell\">\r\n                                <code id=\"code_0_content\" class=\"language-shell\">\r\n\r\n                                        async def send_reminder_card(    \r\n        self,    \r\n        to: str,    \r\n        due_date: date,    \r\n        amount: Decimal,    \r\n        account_last4: str,    \r\n        payment_url: str,    \r\n    ) -&gt; dict[str, Any]:    \r\n        card: dict[str, Any] = {    \r\n            &quot;title&quot;: f&quot;Payment due {due_date:%d %b}&quot;,    \r\n            &quot;description&quot;: f&quot;Amount: ${amount:,.2f}. Account ending {account_last4}.&quot;,    \r\n            &quot;choices&quot;: [    \r\n                {&quot;url_message&quot;: {&quot;title&quot;: &quot;Pay now&quot;, &quot;url&quot;: payment_url}}    \r\n            ],    \r\n        }    \r\n        return await self._send(    \r\n            to,    \r\n            {&quot;card_message&quot;: card},    \r\n            channel_properties={&quot;RCS_WEBVIEW_MODE&quot;: &quot;TALL&quot;},    \r\n        )   \r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\"><code>channel_properties<\/code> est une structure plate unique, de sorte que la cl\u00e9 RCS et le <code>SMS_SENDER<\/code> dont le repli a besoin finissent\u00a0dans le m\u00eame dictionnaire. C&rsquo;est en raison de cette fusion que <code>_send<\/code> prend les propri\u00e9t\u00e9s comme argument au lieu de\u00a0les construire en ligne. <code>RCS_WEBVIEW_MODE<\/code> contr\u00f4le la taille de l&rsquo;\u00e9cran que la page de paiement occupe \u00e0 son\u00a0ouverture\u00a0: <code>FULL<\/code>, <code>HALF<\/code> ou <code>TALL<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deux d\u00e9tails concernant le RCS sur lesquels il est facile de se tromper\u00a0:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Le RCS ne traite pas le paiement.<\/strong> Le bouton \u00ab\u00a0Payer maintenant\u00a0\u00bb est une action d&rsquo;URL. Il ouvre votre page de paiement h\u00e9berg\u00e9e\u00a0dans une webview par-dessus la discussion, la personne y effectue son paiement, et le contr\u00f4le revient \u00e0 la\u00a0conversation. Ce que le RCS vous apporte, c&rsquo;est l&rsquo;exp\u00e9diteur v\u00e9rifi\u00e9, la carte de marque et une personne qui ne\u00a0quitte jamais son application de messagerie. Le paiement transite toujours par votre fournisseur de paiement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Les \u00e9checs de repli arrivent tardivement.<\/strong> Chaque canal que vous nommez dans <code>channel_priority_order<\/code> doit \u00eatre\u00a0configur\u00e9 sur l&rsquo;application, sinon la demande est purement et simplement rejet\u00e9e. C&rsquo;est le cas le plus facile \u00e0 d\u00e9boguer. Le\u00a0cas le plus difficile concerne un canal configur\u00e9 qui ne parvient pas \u00e0 livrer\u00a0: <code>messages:send<\/code> renvoie le code 200, et le\u00a0r\u00e9sultat s&rsquo;affiche plus tard sous la forme d&rsquo;un rappel <code>MESSAGE_DELIVERY<\/code>, o\u00f9 <code>SWITCHING_CHANNEL<\/code> marque le moment\u00a0o\u00f9 le repli s&rsquo;est d\u00e9clench\u00e9. Une raison suppl\u00e9mentaire de configurer correctement les d\u00e9clencheurs de webhook avant de s&rsquo;appuyer sur le chemin\u00a0de repli.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Voix, v\u00e9rification et email<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Les quatre autres \u00e9l\u00e9ments ont n\u00e9cessit\u00e9 un prompt chacun.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Voix.<\/strong> Si le rappel reste sans r\u00e9ponse et que le paiement est toujours en attente, RockBank appelle. Une\u00a0diffusion par synth\u00e8se vocale ne n\u00e9cessite aucun serveur de flux d&rsquo;appels, juste une requ\u00eate POST vers l&rsquo;API vocale\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-sand\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p class=\"mb-0\">Ajoutez un rappel vocal pour les paiements qui sont toujours en attente deux jours apr\u00e8s l&rsquo;envoi du message. Utilisez une diffusion par synth\u00e8se vocale via l&rsquo;API vocale de Sinch avec nos identifiants d&rsquo;application vocale, et d\u00e9finissez l&rsquo;identifiant de l&rsquo;appelant sur notre num\u00e9ro enregistr\u00e9.<\/p><\/div>\r\n    <\/div>\r\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                Shell                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copier dans le presse-papiers\" data-clipboard-success-label=\"Copi\u00e9\u00a0!\" data-clipboard-error-label=\"Impossible de copier\" data-tooltip=\"Copier dans le presse-papiers\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copier dans le presse-papiers\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-shell\">\r\n                                <code id=\"code_0_content\" class=\"language-shell\">\r\n\r\n                                    async def place_reminder_call(self, to: str, message: str) -&gt; dict[str, Any]:\r\n    &quot;&quot;&quot;Place a text-to-speech reminder call via the Voice API.&quot;&quot;&quot;\r\n    tts: dict[str, Any] = {\r\n        &quot;destination&quot;: {&quot;type&quot;: &quot;number&quot;, &quot;endpoint&quot;: to},\r\n        &quot;cli&quot;: self.caller_id,\r\n        &quot;locale&quot;: &quot;en-US&quot;,\r\n        &quot;text&quot;: message,\r\n    }\r\n    response = await self.client.post(\r\n        &quot;\/calling\/v1\/callouts&quot;,\r\n        auth=(self.voice_app_key, self.voice_app_secret),\r\n        json={&quot;method&quot;: &quot;ttsCallout&quot;, &quot;ttsCallout&quot;: tts},\r\n    )\r\n    if response.is_error:\r\n        raise SinchClientError(\r\n            f&quot;Voice callout failed ({response.status_code}): {response.text}&quot;\r\n        )\r\n    return response.json()\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Notez la diff\u00e9rence d&rsquo;authentification\u00a0: la voix utilise une cl\u00e9 et un secret d&rsquo;application, et non les\u00a0identifiants au niveau du projet qu&rsquo;utilise l&rsquo;API de conversation. Produit diff\u00e9rent, sch\u00e9ma d&rsquo;authentification diff\u00e9rent, et les Skills\u00a0couvrent les deux. Le prompt n&rsquo;a donc eu besoin d&rsquo;expliquer ni l&rsquo;un ni l&rsquo;autre.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La <code>destination<\/code> d&rsquo;un appel t\u00e9l\u00e9phonique est <code>{\"type\": \"number\", \"endpoint\": \"+46...\"}<\/code> en E.164, et la\u00a0r\u00e9ponse vous donne un <code>callId<\/code>. La r\u00e9f\u00e9rence de l&rsquo;API indique que le param\u00e8tre <code>cli<\/code> est facultatif, mais configurez-le tout de m\u00eame sur un num\u00e9ro\u00a0v\u00e9rifi\u00e9 ou attribu\u00e9 depuis votre tableau de bord\u00a0: sans un identifiant d&rsquo;appelant utilisable, vous risquez de recevoir un ID d&rsquo;appel\u00a0pour un appel qui n&rsquo;atteint jamais le t\u00e9l\u00e9phone.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>V\u00e9rification.<\/strong> Un mot de passe \u00e0 usage unique avant que la page de paiement n&rsquo;accepte quoi que ce soit\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-sand\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p class=\"mb-0\">Avant que la page de paiement n&rsquo;accepte quoi que ce soit, v\u00e9rifiez l&rsquo;identit\u00e9 de la personne avec un mot de passe \u00e0 usage unique par SMS via l&rsquo;API de v\u00e9rification de Sinch. Exposez la m\u00e9thode pour que nous puissions la changer \u00e0 chaque requ\u00eate.<\/p><\/div>\r\n    <\/div>\r\n\n\n    <div data-content-type=\"longform\" class=\"code-snippet longform-spacings rounded-lg overflow-hidden shadow \" data-count=\"1\">\r\n        <ul class=\"nav nav-buttons code-snippet__tabs longform-except position-relative d-flex gap-2 flex-wrap p-3\" role=\"tablist\">\r\n\r\n                            \r\n                    \r\n                        <li class=\"nav-item\" role=\"presentation\">\r\n                            <button class=\"nav-link active\" data-bs-toggle=\"tab\" data-bs-target=\"#code_0\" type=\"button\" role=\"tab\" aria-controls=\"code_0\" aria-selected=\"true\">\r\n                                Shell                            <\/button>\r\n                        <\/li>\r\n\r\n                    \r\n                            \r\n        <\/ul>\r\n        <div class=\"code-snippet__tab-content tab-content\">\r\n\r\n                            \r\n                    <div class=\"tab-pane show active\" id=\"code_0\" role=\"tabpanel\" aria-labelledby=\"code_0\">\r\n                        <div class=\"code-snippet__code-wrapper\">\r\n                            <div class=\"nav nav-buttons code-snippet__copy-wrapper\">\r\n                                <button type=\"button\" class=\"nav-link code-snippet__copy\" data-clipboard-target=\"#code_0_content\" data-clipboard-label=\"Copier dans le presse-papiers\" data-clipboard-success-label=\"Copi\u00e9\u00a0!\" data-clipboard-error-label=\"Impossible de copier\" data-tooltip=\"Copier dans le presse-papiers\" data-tooltip-placement=\"left\" role=\"tooltip\" aria-label=\"Copier dans le presse-papiers\">\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--default\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/file-stack.svg\"><\/svg>\r\n                                    <svg class=\"code-snippet__copy-icon code-snippet__copy-icon--success\" width=\"24\" height=\"24\" aria-hidden=\"true\" data-url=\"https:\/\/sinch.com\/wp-content\/plugins\/sinch-core\/assets\/icons\/sinch\/badge-check.svg\"><\/svg>\r\n                                <\/button>\r\n                            <\/div>\r\n                            <pre class=\"w-100 h-100 m-0 line-numbers language-shell\">\r\n                                <code id=\"code_0_content\" class=\"language-shell\">\r\n\r\n                                    async def start(self, phone: str, method: str = &quot;sms&quot;) -&gt; dict[str, Any]:\r\n    response = await self.client.post(\r\n        &quot;\/verification\/v1\/verifications&quot;,\r\n        auth=self._verification_auth,\r\n        json={&quot;identity&quot;: {&quot;type&quot;: &quot;number&quot;, &quot;endpoint&quot;: phone}, &quot;method&quot;: method},\r\n    )\r\n    ...\r\n\r\nasync def verify(self, reference: str, code: str) -&gt; dict[str, Any]:\r\n    path = f&quot;\/verification\/v1\/verifications\/id\/{quote(reference, safe=&#039;&#039;)}&quot;\r\n    response = await self.client.put(\r\n        path,\r\n        auth=self._verification_auth,\r\n        json={&quot;method&quot;: &quot;sms&quot;, &quot;sms&quot;: {&quot;code&quot;: code}},\r\n    )\r\n    ...\r\n                                <\/code>\r\n                            <\/pre>\r\n                        <\/div>\r\n                    <\/div>\r\n                                    <\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\"><code>_verification_auth<\/code> repr\u00e9sente un troisi\u00e8me ensemble d&rsquo;identifiants\u00a0: la v\u00e9rification s&rsquo;authentifie avec la cl\u00e9 et le\u00a0secret de sa propre application dans le tableau de bord. Il ne s&rsquo;agit donc ni des cl\u00e9s au niveau du projet qu&rsquo;utilise l&rsquo;API de conversation, ni de celles de l&rsquo;application vocale.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>start<\/code> renvoie un <code>id<\/code>, et c&rsquo;est par rapport \u00e0 celui-ci que vous signalez le code. Les valeurs de la m\u00e9thode sont la partie\u00a0qu&rsquo;il convient de conna\u00eetre\u00a0: <code>sms<\/code> pour un mot de passe par message, <code>callout<\/code> pour une lecture lors d&rsquo;un appel t\u00e9l\u00e9phonique, auxquels s&rsquo;ajoutent\u00a0<code>flashcall<\/code>, <code>seamless<\/code> et <code>whatsapp<\/code>. Le corps du rapport est index\u00e9 par la m\u00eame m\u00e9thode, donc\u00a0<code>{\"method\": \"sms\", \"sms\": {\"code\": \"1234\"}}<\/code> pour le message et\u00a0<code>{\"method\": \"callout\", \"callout\": {\"code\": \"1234\"}}<\/code> pour l&rsquo;appel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les v\u00e9rifications expirent au bout de quelques minutes. Par cons\u00e9quent, traitez une erreur 400 lors du signalement comme un appel \u00e0 \u00ab\u00a0en d\u00e9marrer une nouvelle\u00a0\u00bb plut\u00f4t que\u00a0comme une tentative \u00e9chou\u00e9e. Basculer une personne vers <code>callout<\/code> lorsque le message n&rsquo;arrive pas constitue un second\u00a0appel <code>start<\/code> avec une m\u00e9thode diff\u00e9rente, et d\u00e9cider quand le proposer rel\u00e8ve de la logique de l&rsquo;application\u00a0: le temps\u00a0que vous attendez, le nombre de tentatives que vous autorisez, et si c&rsquo;est la personne qui le demande ou si vous d\u00e9cidez pour elle.\u00a0Il est pr\u00e9f\u00e9rable de concevoir cela d\u00e9lib\u00e9r\u00e9ment plut\u00f4t que de s&rsquo;en remettre \u00e0 une boucle de relance.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Email.<\/strong> La confirmation est envoy\u00e9e via Mailgun, qui se trouve dans le m\u00eame plugin\u00a0:<\/p>\n\n\n    <div data-content-type=\"longform\"  class=\"callout text-body-color px-5 py-6 px-md-6 px-lg-7 py-md-7 longform-spacings rounded-lg bg-sand\" data-theme=\"light\">\r\n\r\n        <div class=\"content-body\"> <p class=\"mb-0\">Lorsqu&rsquo;un paiement ou un \u00e9ch\u00e9ancier de paiement est confirm\u00e9, envoyez un email de confirmation via Mailgun contenant le montant, la date et un num\u00e9ro de r\u00e9f\u00e9rence.<\/p><\/div>\r\n    <\/div>\r\n\n\n\n<p class=\"wp-block-paragraph\">Mailgun constitue un quatri\u00e8me ensemble d&rsquo;identifiants, et il fait figure d&rsquo;exception\u00a0: il s&rsquo;authentifie par domaine et par cl\u00e9, plut\u00f4t que par application comme les trois autres. Il s&rsquo;agit de l&rsquo;authentification Basic par rapport \u00e0 <code>POST \/v3\/{domain}\/messages<\/code>, avec <code>api<\/code> comme nom d&rsquo;utilisateur et une cl\u00e9 API comme mot de passe. <code>o:testmode=yes<\/code> accepte un envoi sans le livrer, ce qu&rsquo;il est utile de savoir pendant que vous configurez la confirmation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Pi\u00e8ges \u00e0 \u00e9viter<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Lancez l&rsquo;enregistrement 10DLC avant d&rsquo;\u00e9crire le code d&rsquo;envoi.<\/strong> L&rsquo;envoi de SMS vers des num\u00e9ros am\u00e9ricains l&rsquo;exige. L&rsquo;approbation prend plusieurs jours et aucune vitesse d&rsquo;agent ne peut r\u00e9duire ce d\u00e9lai. C&rsquo;est la raison la plus fr\u00e9quente\u00a0pour laquelle une conception pr\u00e9vue sur une apr\u00e8s-midi est repouss\u00e9e \u00e0 la semaine suivante.<\/li>\n\n\n\n<li><strong>Une incompatibilit\u00e9 de r\u00e9gion ressemble \u00e0 un probl\u00e8me d&rsquo;authentification.<\/strong> Toutes les URL de l&rsquo;API de conversation sont sp\u00e9cifiques \u00e0 la r\u00e9gion. Si\u00a0<code>CONVERSATION_REGION<\/code> ne correspond pas \u00e0 l&rsquo;endroit o\u00f9 se trouve l&rsquo;application, vous obtenez une erreur 404 ou une erreur pointant vers\u00a0vos identifiants. V\u00e9rifiez la r\u00e9gion dans le tableau de bord avant de reg\u00e9n\u00e9rer une cl\u00e9 tout \u00e0 fait valide.<\/li>\n\n\n\n<li><strong>Testez le chemin de repli avec autant de rigueur que le chemin nominal.<\/strong> La disponibilit\u00e9 du RCS varie selon le march\u00e9, l&rsquo;op\u00e9rateur\u00a0et l&rsquo;appareil, et votre propre t\u00e9l\u00e9phone ne prouve que le chemin nominal. Effectuez un envoi vers un appareil d\u00e9pourvu du RCS et confirmez\u00a0que le SMS arrive bien. <code>messages:transcode<\/code> pr\u00e9visualise la mani\u00e8re dont une carte se d\u00e9grade sans l&rsquo;envoyer.<\/li>\n\n\n\n<li><strong>Le serveur MCP effectue des actions r\u00e9elles sur un compte r\u00e9el.<\/strong> C&rsquo;est l\u00e0 tout l&rsquo;int\u00e9r\u00eat, ce qui implique de\u00a0le pointer vers un num\u00e9ro de test au lieu d&rsquo;une liste, et de r\u00e9fl\u00e9chir aux identifiants qui se trouvent\u00a0dans votre profil shell.<\/li>\n\n\n\n<li><strong>Maintenez l&rsquo;agent \u00e0 l&rsquo;int\u00e9rieur de la racine du projet.<\/strong> Lors d&rsquo;une ex\u00e9cution, l&rsquo;agent a effectu\u00e9 une recherche en dehors du dossier\u00a0ouvert, a trouv\u00e9 une ancienne impl\u00e9mentation de la m\u00eame fonctionnalit\u00e9 dans un r\u00e9pertoire adjacent, et l&rsquo;a reproduite\u00a0au lieu d&rsquo;utiliser les Skills. Le code fonctionnait. Mais il s&rsquo;agissait du mauvais code. Ouvrez le projet dans lequel vous avez l&rsquo;intention de\u00a0travailler.<\/li>\n\n\n\n<li><strong>Lisez ce que l&rsquo;agent a \u00e9crit.<\/strong> L&rsquo;avantage d&rsquo;un agent qui \u00e9crit un v\u00e9ritable code d&rsquo;int\u00e9gration, au lieu de\u00a0renvoyer un r\u00e9sultat opaque, est que le code est directement accessible pour \u00eatre examin\u00e9. L&rsquo;\u00e9cart li\u00e9 au d\u00e9clencheur de webhook\u00a0et l&rsquo;identifiant d&rsquo;appelant manquant \u00e9taient tous deux visibles dans le diff.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Ce que j&rsquo;ai finalement obtenu<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Du c\u00f4t\u00e9 de la client\u00e8le\u00a0: un rappel arrive trois jours avant l&rsquo;\u00e9ch\u00e9ance du paiement. Sur un appareil qui\u00a0prend en charge le RCS, il s&rsquo;agit d&rsquo;une carte de marque provenant d&rsquo;un exp\u00e9diteur v\u00e9rifi\u00e9, avec un bouton \u00ab\u00a0Payer maintenant\u00a0\u00bb. La personne clique dessus, effectue une v\u00e9rification\u00a0avec un mot de passe, paie sans quitter la discussion, puis re\u00e7oit une confirmation par email. Si le paiement est\u00a0toujours en attente, le parcours se poursuit avec un rappel vocal automatis\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Au final, le parcours couvre la messagerie, la v\u00e9rification, la voix et l&#8217;email, le tout construit via le m\u00eame\u00a0flux de travail Cursor. Le temps que l&rsquo;agent m&rsquo;a fait gagner correspondait \u00e0 du temps de lecture de la documentation\u00a0: comprendre les formes des charges utiles et les\u00a0sch\u00e9mas d&rsquo;authentification sur quatre produits, auxquels s&rsquo;ajoutent les cycles de d\u00e9ploiement et de v\u00e9rification qui s&rsquo;\u00e9coulent g\u00e9n\u00e9ralement entre le moment o\u00f9 une\u00a0charge utile est incorrecte et celui o\u00f9 je m&rsquo;en rends compte. Saisir le code n&rsquo;a jamais \u00e9t\u00e9 l&rsquo;\u00e9tape la plus longue.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Essayez par vous-m\u00eame<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Installez le plugin depuis la  <a href=\"https:\/\/cursor.com\/marketplace\/sinch\/sinch-cursor-plugin\" target=\"_blank\" rel=\"noreferrer noopener\">Marketplace Cursor<\/a>,\u00a0exportez les identifiants, relancez Cursor, et transmettez \u00e0 l&rsquo;agent le prompt de l&rsquo;API de conversation ci-dessus\u00a0vers un num\u00e9ro de test. Le plugin est open source sur\u00a0<a href=\"https:\/\/github.com\/sinch\/sinch-plugins\" target=\"_blank\" rel=\"noreferrer noopener\">sinch-plugins sur GitHub<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ressources compl\u00e9mentaires<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/cursor.com\/marketplace\/sinch\/sinch-cursor-plugin\" target=\"_blank\" rel=\"noreferrer noopener\">Plugin Sinch Cursor sur la Marketplace Cursor<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/sinch\/sinch-plugins\" target=\"_blank\" rel=\"noreferrer noopener\">sinch-plugins sur GitHub<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developers.sinch.com\/docs\/conversation\/\" target=\"_blank\" rel=\"noreferrer noopener\">Documentation de l&rsquo;API de conversation de Sinch<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developers.sinch.com\/docs\/voice\/\" target=\"_blank\" rel=\"noreferrer noopener\">Documentation de l&rsquo;API vocale de Sinch<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developers.sinch.com\/docs\/verification\/\" target=\"_blank\" rel=\"noreferrer noopener\">Documentation de l&rsquo;API de v\u00e9rification de Sinch<\/a><\/li>\n<\/ul>\n","protected":false},"author":93,"featured_media":210360,"menu_order":0,"template":"","meta":{"_acf_changed":false,"footnotes":""},"tags":[],"blog_category":[751],"class_list":["post-211313","blog","type-blog","status-publish","has-post-thumbnail","hentry","blog_category-developpeurs"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Cr\u00e9ation d&#039;une stack de communication dans Cursor avec Sinch<\/title>\n<meta name=\"description\" content=\"Comment j&#039;ai con\u00e7u une stack de communication client dans Cursor \u00e0 l&#039;aide du plugin Sinch, allant des rappels RCS et SMS \u00e0 la voix, en passant par la v\u00e9rification et l&#039;email.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/\" \/>\n<meta property=\"og:locale\" content=\"fr_FR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Cr\u00e9ation d&#039;une stack de communication dans Cursor avec Sinch\" \/>\n<meta property=\"og:description\" content=\"Comment j&#039;ai con\u00e7u une stack de communication client dans Cursor \u00e0 l&#039;aide du plugin Sinch, allant des rappels RCS et SMS \u00e0 la voix, en passant par la v\u00e9rification et l&#039;email.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/\" \/>\n<meta property=\"og:site_name\" content=\"Sinch\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/wearesinch\/\" \/>\n<meta property=\"og:image\" content=\"https:\/\/sinch.com\/wp-content\/uploads\/2026\/09\/SI-Blog-how-i-built-communications-stack-cursor-sinch-plugin.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1296\" \/>\n\t<meta property=\"og:image:height\" content=\"788\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:site\" content=\"@WeAreSinch\" \/>\n<meta name=\"twitter:label1\" content=\"Dur\u00e9e de lecture estim\u00e9e\" \/>\n\t<meta name=\"twitter:data1\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/\"},\"author\":{\"name\":\"Gunnar Grosch\",\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/#\\\/schema\\\/person\\\/b2bde1c2728a21bc953ea0e129936c08\"},\"headline\":\"Comment j\u2019ai construit une stack de communication dans Cursor avec le plugin Sinch\",\"datePublished\":\"2026-09-25T10:24:19+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/\"},\"wordCount\":3363,\"publisher\":{\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/sinch.com\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/SI-Blog-how-i-built-communications-stack-cursor-sinch-plugin.png\",\"inLanguage\":\"fr-FR\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/\",\"url\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/\",\"name\":\"Cr\u00e9ation d'une stack de communication dans Cursor avec Sinch\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/sinch.com\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/SI-Blog-how-i-built-communications-stack-cursor-sinch-plugin.png\",\"datePublished\":\"2026-09-25T10:24:19+00:00\",\"description\":\"Comment j'ai con\u00e7u une stack de communication client dans Cursor \u00e0 l'aide du plugin Sinch, allant des rappels RCS et SMS \u00e0 la voix, en passant par la v\u00e9rification et l'email.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/#breadcrumb\"},\"inLanguage\":\"fr-FR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/#primaryimage\",\"url\":\"https:\\\/\\\/sinch.com\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/SI-Blog-how-i-built-communications-stack-cursor-sinch-plugin.png\",\"contentUrl\":\"https:\\\/\\\/sinch.com\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/SI-Blog-how-i-built-communications-stack-cursor-sinch-plugin.png\",\"width\":1296,\"height\":788},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/sinch.com\\\/fr\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Blog\",\"item\":\"https:\\\/\\\/sinch.com\\\/fr\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Comment j\u2019ai construit une stack de communication dans Cursor avec le plugin Sinch\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/#website\",\"url\":\"https:\\\/\\\/sinch.com\\\/fr\\\/\",\"name\":\"Sinch\",\"description\":\"Messagerie SMS, Voix, Email, Vid\u00e9o & API de v\u00e9rification | Sinch\",\"publisher\":{\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/sinch.com\\\/fr\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fr-FR\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/#organization\",\"name\":\"Sinch\",\"url\":\"https:\\\/\\\/sinch.com\\\/fr\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/sinch.com\\\/wp-content\\\/uploads\\\/2024\\\/09\\\/Thumbnail-Logo-Honey.png\",\"contentUrl\":\"https:\\\/\\\/sinch.com\\\/wp-content\\\/uploads\\\/2024\\\/09\\\/Thumbnail-Logo-Honey.png\",\"width\":800,\"height\":496,\"caption\":\"Sinch\"},\"image\":{\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/wearesinch\\\/\",\"https:\\\/\\\/x.com\\\/WeAreSinch\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/sinch\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UCZZ2u_B2afTxA0v-xcgfsaw\",\"https:\\\/\\\/www.instagram.com\\\/wearesinch\\\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/sinch.com\\\/fr\\\/#\\\/schema\\\/person\\\/b2bde1c2728a21bc953ea0e129936c08\",\"name\":\"Gunnar Grosch\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fr-FR\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/8f177695c01082754db3f0858d534fdaceedba31f74932d7de39991e5e9d7e8d?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/8f177695c01082754db3f0858d534fdaceedba31f74932d7de39991e5e9d7e8d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/8f177695c01082754db3f0858d534fdaceedba31f74932d7de39991e5e9d7e8d?s=96&d=mm&r=g\",\"caption\":\"Gunnar Grosch\"},\"description\":\"Gunnar Grosch is a Principal Developer Advocate at Sinch and an engineer with over 25 years of experience building software, scaling developer communities, and making complex technology accessible. He shares code, insights, and lessons through conference talks, blog posts, and open source.\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Cr\u00e9ation d'une stack de communication dans Cursor avec Sinch","description":"Comment j'ai con\u00e7u une stack de communication client dans Cursor \u00e0 l'aide du plugin Sinch, allant des rappels RCS et SMS \u00e0 la voix, en passant par la v\u00e9rification et l'email.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/","og_locale":"fr_FR","og_type":"article","og_title":"Cr\u00e9ation d'une stack de communication dans Cursor avec Sinch","og_description":"Comment j'ai con\u00e7u une stack de communication client dans Cursor \u00e0 l'aide du plugin Sinch, allant des rappels RCS et SMS \u00e0 la voix, en passant par la v\u00e9rification et l'email.","og_url":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/","og_site_name":"Sinch","article_publisher":"https:\/\/www.facebook.com\/wearesinch\/","og_image":[{"width":1296,"height":788,"url":"https:\/\/sinch.com\/wp-content\/uploads\/2026\/09\/SI-Blog-how-i-built-communications-stack-cursor-sinch-plugin.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_site":"@WeAreSinch","twitter_misc":{"Dur\u00e9e de lecture estim\u00e9e":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/#article","isPartOf":{"@id":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/"},"author":{"name":"Gunnar Grosch","@id":"https:\/\/sinch.com\/fr\/#\/schema\/person\/b2bde1c2728a21bc953ea0e129936c08"},"headline":"Comment j\u2019ai construit une stack de communication dans Cursor avec le plugin Sinch","datePublished":"2026-09-25T10:24:19+00:00","mainEntityOfPage":{"@id":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/"},"wordCount":3363,"publisher":{"@id":"https:\/\/sinch.com\/fr\/#organization"},"image":{"@id":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/#primaryimage"},"thumbnailUrl":"https:\/\/sinch.com\/wp-content\/uploads\/2026\/09\/SI-Blog-how-i-built-communications-stack-cursor-sinch-plugin.png","inLanguage":"fr-FR"},{"@type":"WebPage","@id":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/","url":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/","name":"Cr\u00e9ation d'une stack de communication dans Cursor avec Sinch","isPartOf":{"@id":"https:\/\/sinch.com\/fr\/#website"},"primaryImageOfPage":{"@id":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/#primaryimage"},"image":{"@id":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/#primaryimage"},"thumbnailUrl":"https:\/\/sinch.com\/wp-content\/uploads\/2026\/09\/SI-Blog-how-i-built-communications-stack-cursor-sinch-plugin.png","datePublished":"2026-09-25T10:24:19+00:00","description":"Comment j'ai con\u00e7u une stack de communication client dans Cursor \u00e0 l'aide du plugin Sinch, allant des rappels RCS et SMS \u00e0 la voix, en passant par la v\u00e9rification et l'email.","breadcrumb":{"@id":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/#breadcrumb"},"inLanguage":"fr-FR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/"]}]},{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/#primaryimage","url":"https:\/\/sinch.com\/wp-content\/uploads\/2026\/09\/SI-Blog-how-i-built-communications-stack-cursor-sinch-plugin.png","contentUrl":"https:\/\/sinch.com\/wp-content\/uploads\/2026\/09\/SI-Blog-how-i-built-communications-stack-cursor-sinch-plugin.png","width":1296,"height":788},{"@type":"BreadcrumbList","@id":"https:\/\/sinch.com\/fr\/blog\/comment-j-ai-construit-stack-communication-cursor-plugin-sinch\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/sinch.com\/fr\/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https:\/\/sinch.com\/fr\/blog\/"},{"@type":"ListItem","position":3,"name":"Comment j\u2019ai construit une stack de communication dans Cursor avec le plugin Sinch"}]},{"@type":"WebSite","@id":"https:\/\/sinch.com\/fr\/#website","url":"https:\/\/sinch.com\/fr\/","name":"Sinch","description":"Messagerie SMS, Voix, Email, Vid\u00e9o & API de v\u00e9rification | Sinch","publisher":{"@id":"https:\/\/sinch.com\/fr\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/sinch.com\/fr\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fr-FR"},{"@type":"Organization","@id":"https:\/\/sinch.com\/fr\/#organization","name":"Sinch","url":"https:\/\/sinch.com\/fr\/","logo":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/sinch.com\/fr\/#\/schema\/logo\/image\/","url":"https:\/\/sinch.com\/wp-content\/uploads\/2024\/09\/Thumbnail-Logo-Honey.png","contentUrl":"https:\/\/sinch.com\/wp-content\/uploads\/2024\/09\/Thumbnail-Logo-Honey.png","width":800,"height":496,"caption":"Sinch"},"image":{"@id":"https:\/\/sinch.com\/fr\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/wearesinch\/","https:\/\/x.com\/WeAreSinch","https:\/\/www.linkedin.com\/company\/sinch","https:\/\/www.youtube.com\/channel\/UCZZ2u_B2afTxA0v-xcgfsaw","https:\/\/www.instagram.com\/wearesinch\/"]},{"@type":"Person","@id":"https:\/\/sinch.com\/fr\/#\/schema\/person\/b2bde1c2728a21bc953ea0e129936c08","name":"Gunnar Grosch","image":{"@type":"ImageObject","inLanguage":"fr-FR","@id":"https:\/\/secure.gravatar.com\/avatar\/8f177695c01082754db3f0858d534fdaceedba31f74932d7de39991e5e9d7e8d?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/8f177695c01082754db3f0858d534fdaceedba31f74932d7de39991e5e9d7e8d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/8f177695c01082754db3f0858d534fdaceedba31f74932d7de39991e5e9d7e8d?s=96&d=mm&r=g","caption":"Gunnar Grosch"},"description":"Gunnar Grosch is a Principal Developer Advocate at Sinch and an engineer with over 25 years of experience building software, scaling developer communities, and making complex technology accessible. He shares code, insights, and lessons through conference talks, blog posts, and open source."}]}},"_links":{"self":[{"href":"https:\/\/sinch.com\/fr\/wp-json\/wp\/v2\/blog\/211313","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sinch.com\/fr\/wp-json\/wp\/v2\/blog"}],"about":[{"href":"https:\/\/sinch.com\/fr\/wp-json\/wp\/v2\/types\/blog"}],"author":[{"embeddable":true,"href":"https:\/\/sinch.com\/fr\/wp-json\/wp\/v2\/users\/93"}],"version-history":[{"count":0,"href":"https:\/\/sinch.com\/fr\/wp-json\/wp\/v2\/blog\/211313\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sinch.com\/fr\/wp-json\/wp\/v2\/media\/210360"}],"wp:attachment":[{"href":"https:\/\/sinch.com\/fr\/wp-json\/wp\/v2\/media?parent=211313"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sinch.com\/fr\/wp-json\/wp\/v2\/tags?post=211313"},{"taxonomy":"blog_category","embeddable":true,"href":"https:\/\/sinch.com\/fr\/wp-json\/wp\/v2\/blog_category?post=211313"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}