Skip to main content
RapidDev - Software Development Agency
lovable-integrationsEdge Function Integration

How to Integrate Lovable with Sinch

To integrate Lovable with Sinch, create a Supabase Edge Function that uses the Sinch Conversation API with OAuth2 to send SMS, MMS, RCS, and WhatsApp messages across 200+ countries. Store your Sinch Project ID, App ID, Key ID, and Key Secret in Cloud → Secrets, then generate an access token server-side before making messaging API calls. The unified Conversation API handles multi-channel routing from a single endpoint.

What you'll learn

  • How Sinch's Conversation API unifies SMS, RCS, WhatsApp, and other channels under one endpoint
  • How to create a Sinch App and obtain Project ID, App ID, Key ID, and Key Secret
  • How to store Sinch OAuth2 credentials in Lovable's Cloud → Secrets panel
  • How to write a Deno Edge Function that fetches a Sinch Bearer token and sends a message
  • How to receive inbound Sinch messages and delivery receipts via webhook callbacks
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate12 min read45 minutesCommunicationMarch 2026RapidDev Engineering Team
TL;DR

To integrate Lovable with Sinch, create a Supabase Edge Function that uses the Sinch Conversation API with OAuth2 to send SMS, MMS, RCS, and WhatsApp messages across 200+ countries. Store your Sinch Project ID, App ID, Key ID, and Key Secret in Cloud → Secrets, then generate an access token server-side before making messaging API calls. The unified Conversation API handles multi-channel routing from a single endpoint.

Multi-channel global messaging in Lovable using Sinch's Conversation API

Sinch's primary strength is its global reach and unified messaging model. Rather than building separate integrations for SMS, WhatsApp, and RCS, the Conversation API provides a single endpoint that routes messages to the appropriate channel based on recipient capabilities and your channel priority configuration. You define an App in the Sinch Dashboard that specifies which channels are available and their fallback order — for example, try RCS first, fall back to WhatsApp, then fall back to SMS. This channel-agnostic approach simplifies your Edge Function code significantly.

For Lovable projects targeting international audiences, Sinch's coverage in 200+ countries with direct carrier connections makes it more reliable than alternatives for non-US markets. The Conversation API uses OAuth2 with short-lived Bearer tokens, which are obtained by calling the token endpoint with your Key ID and Key Secret using Basic Auth. Tokens are scoped per project and expire after one hour — your Edge Function should fetch a fresh token on each invocation or cache it in Supabase with an expiry check.

Authentication in Sinch involves several identifiers that are easy to confuse: the Project ID (workspace-level), the App ID (a specific Conversation API app you configure), the Key ID (OAuth credential), and the Key Secret (OAuth credential secret). All four are required for the full integration. This guide walks through obtaining each one and structuring the Edge Function correctly.

Integration method

Edge Function Integration

Sinch has no native Lovable connector. All API calls are made through Supabase Edge Functions that authenticate with Sinch's OAuth2 endpoint using a Key ID and Key Secret, then call the Conversation API to send messages across any supported channel. The unified model means one Edge Function can handle SMS, WhatsApp, and RCS routing. Credentials stay encrypted in Cloud → Secrets.

Prerequisites

  • A Lovable project with Lovable Cloud enabled (visible in the Cloud tab)
  • A Sinch account at app.sinch.com with a project created
  • A Sinch Conversation API App configured with at least one channel (SMS, WhatsApp, or RCS) enabled
  • Your Sinch Project ID, App ID, Key ID, and Key Secret from the Sinch Dashboard
  • A Sinch phone number or Sender ID assigned to your Conversation API App

Step-by-step guide

1

Configure your Sinch Conversation API App and gather credentials

Sign in to app.sinch.com. Your Project ID is displayed in the top navigation bar or under 'Settings' — it is a UUID like 12345678-abcd-1234-ef56-789012345678. Navigate to 'Messaging' → 'Conversation API' in the left sidebar. If you do not have an App yet, click 'New App' and give it a name. Inside your App configuration, note the App ID (also a UUID). Go to the 'Channels' tab within the App and enable the channels you need. For SMS, link a Sinch number or Sender ID. For WhatsApp, you will need a WhatsApp Business Account approved through Meta — Sinch's support team can help with this setup. Next, create API credentials: go to 'Settings' → 'Access Keys' and click 'New Key'. Give the key a descriptive name and copy both the Key ID and Key Secret shown immediately after creation (the secret is only shown once). These are your OAuth2 client credentials. Store all four values — Project ID, App ID, Key ID, Key Secret — somewhere temporary before the next step where you will save them as Lovable Secrets.

Pro tip: Sinch has multiple products (Voice, Verification, Conversation API, Numbers). Make sure you are in the Conversation API section for this integration, not the older SMS-only API which has different authentication.

Expected result: Project ID, App ID, Key ID, and Key Secret copied from the Sinch Dashboard, ready to store in Lovable Secrets.

2

Store Sinch credentials in Cloud → Secrets

Open the Cloud tab in your Lovable editor by clicking the '+' panel icon at the top right and selecting Cloud. Scroll to the Secrets section and add four secrets by clicking the '+' button for each. Create SINCH_PROJECT_ID with your Project ID (UUID format). Create SINCH_APP_ID with your Conversation API App ID (UUID format). Create SINCH_KEY_ID with the OAuth Key ID. Create SINCH_KEY_SECRET with the OAuth Key Secret. Optionally, create SINCH_SENDER with your phone number or Sender ID in E.164 format. The names must match exactly what your Edge Function calls with Deno.env.get(). All five values are sensitive — Sinch Key ID and Key Secret together can be used to send messages and incur charges on your account. Lovable's SOC 2 Type II certified infrastructure encrypts these at rest. Never paste these credentials into Lovable chat messages or into application code directly — the Secrets panel is the only safe place for them.

Pro tip: Sinch Key Secrets cannot be retrieved after initial creation — if you lose it, you must create a new key. Keep a copy in a secure password manager in addition to Lovable Secrets.

Expected result: Four or five secrets saved in Cloud → Secrets: SINCH_PROJECT_ID, SINCH_APP_ID, SINCH_KEY_ID, SINCH_KEY_SECRET, and optionally SINCH_SENDER.

3

Build the Sinch Conversation API message sending Edge Function

The Edge Function must: (1) obtain a Bearer token from Sinch's OAuth endpoint using Basic Auth with Key ID and Key Secret, then (2) call the Conversation API send message endpoint with the project ID, app ID, recipient contact, and message content. Sinch's token endpoint is https://auth.sinch.com/oauth2/token with grant_type=client_credentials. Use Basic Auth with the Key ID as username and Key Secret as password. The response contains access_token and expires_in. The Conversation API message endpoint is https://us.conversation.api.sinch.com/v1/projects/{projectId}/messages:send (use eu.conversation.api.sinch.com for European data residency). The request body specifies app_id, recipient (identified by phone number as a channel_identities entry), and message (with text_message.text). The API returns a message_id you can use to track delivery. Ask Lovable to generate this function with the full OAuth token flow and message send logic. Review the deployed code in the Code panel to verify the Deno.env.get() calls match your exact secret names.

Lovable Prompt

Create a Supabase Edge Function called sinch-message that: 1) Reads SINCH_PROJECT_ID, SINCH_APP_ID, SINCH_KEY_ID, SINCH_KEY_SECRET, and SINCH_SENDER from env. 2) Calls https://auth.sinch.com/oauth2/token with grant_type=client_credentials using Basic Auth (btoa of keyId:keySecret) to get a Bearer access token. 3) Accepts POST body with: to (E.164 phone number) and text (message string). 4) POSTs to https://us.conversation.api.sinch.com/v1/projects/{projectId}/messages:send with app_id, recipient phone number, and text_message. 5) Returns the message_id from Sinch's response. Handle errors and include CORS headers.

Paste this in Lovable chat

supabase/functions/sinch-message/index.ts
1// supabase/functions/sinch-message/index.ts
2import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
3
4const corsHeaders = {
5 'Access-Control-Allow-Origin': '*',
6 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
7};
8
9async function getSinchToken(): Promise<string> {
10 const keyId = Deno.env.get('SINCH_KEY_ID')!;
11 const keySecret = Deno.env.get('SINCH_KEY_SECRET')!;
12 const credentials = btoa(`${keyId}:${keySecret}`);
13
14 const res = await fetch('https://auth.sinch.com/oauth2/token', {
15 method: 'POST',
16 headers: {
17 'Authorization': `Basic ${credentials}`,
18 'Content-Type': 'application/x-www-form-urlencoded',
19 },
20 body: 'grant_type=client_credentials',
21 });
22
23 if (!res.ok) throw new Error(`Sinch auth error: ${await res.text()}`);
24 const data = await res.json();
25 return data.access_token;
26}
27
28serve(async (req) => {
29 if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders });
30
31 try {
32 const projectId = Deno.env.get('SINCH_PROJECT_ID')!;
33 const appId = Deno.env.get('SINCH_APP_ID')!;
34 const sender = Deno.env.get('SINCH_SENDER')!;
35 const { to, text } = await req.json();
36
37 const token = await getSinchToken();
38
39 const msgRes = await fetch(
40 `https://us.conversation.api.sinch.com/v1/projects/${projectId}/messages:send`,
41 {
42 method: 'POST',
43 headers: {
44 'Authorization': `Bearer ${token}`,
45 'Content-Type': 'application/json',
46 },
47 body: JSON.stringify({
48 app_id: appId,
49 recipient: {
50 identified_by: {
51 channel_identities: [
52 { channel: 'SMS', identity: to },
53 ],
54 },
55 },
56 message: { text_message: { text } },
57 }),
58 }
59 );
60
61 const data = await msgRes.json();
62 if (!msgRes.ok) throw new Error(`Sinch send error: ${JSON.stringify(data)}`);
63
64 return new Response(
65 JSON.stringify({ messageId: data.message_id, status: 'sent' }),
66 { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
67 );
68 } catch (error) {
69 return new Response(
70 JSON.stringify({ error: error.message }),
71 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
72 );
73 }
74});

Pro tip: Change the channel from 'SMS' to 'WHATSAPP' or 'RCS' in the channel_identities array to send through a different channel. You can also include multiple channel_identities entries to let Sinch route to the best available channel.

Expected result: A deployed sinch-message Edge Function that sends messages via the Sinch Conversation API and returns a message ID.

4

Set up inbound message and delivery receipt webhooks

To receive replies and delivery status updates, configure a webhook callback URL in your Sinch Conversation API App. First, create a second Edge Function in Lovable to receive these callbacks. The function should parse the incoming JSON body, identify the trigger type (MESSAGE_INBOUND for incoming messages, MESSAGE_DELIVERY for delivery receipts), and store the data in your Supabase database. Sinch's callback body for inbound messages contains app_id, contact_id, conversation_id, and the message with its text and direction. For delivery receipts, the body contains message_id and delivery_status. Create a Supabase table with columns matching these fields. Once the Edge Function is deployed, copy its URL from Cloud → Edge Functions in the Lovable editor. Back in app.sinch.com, navigate to your Conversation API App → 'Webhooks' tab. Click 'Add Webhook' and paste the Edge Function URL. Select the triggers you want to receive — 'Message Inbound' and 'Message Delivery' are the most useful. Save the webhook. For complex webhook processing with multiple event types and conditional routing, RapidDev's team can help architect a robust handler.

Lovable Prompt

Create a Supabase Edge Function called sinch-webhook that: 1) Accepts POST requests from Sinch Conversation API callbacks. 2) Parses the trigger field from the request body. 3) For MESSAGE_INBOUND triggers: inserts a row into a sinch_messages table with columns direction='inbound', from_contact, message_text, conversation_id, app_id, received_at. 4) For MESSAGE_DELIVERY triggers: updates the row in sinch_messages matching the message_id with the delivery_status. 5) Returns HTTP 200 to acknowledge receipt.

Paste this in Lovable chat

Pro tip: Sinch expects your webhook endpoint to respond within 5 seconds. If processing takes longer, return 200 immediately and process the event asynchronously using Supabase Edge Function background execution.

Expected result: A webhook receiver Edge Function deployed and registered in Sinch Dashboard, storing inbound messages and delivery receipts in Supabase.

Common use cases

Global SMS verification code sender

Send OTP verification codes to users in 200+ countries during signup or authentication. Sinch's direct carrier connections improve delivery rates for international numbers compared to aggregator-based providers. The Edge Function generates a code, sends it via Sinch, and stores the hashed code in Supabase for verification.

Lovable Prompt

Build a phone number verification flow. When a user enters their phone number and clicks 'Send Code', call my sinch-message Edge Function to send a 6-digit OTP via Sinch SMS. Store a hashed version of the code in a verification_codes table with a 10-minute expiry. Add a verification step that checks the entered code against the stored hash.

Copy this prompt to try it in Lovable

WhatsApp business notifications with SMS fallback

Send transactional notifications through WhatsApp for users who have opted in, automatically falling back to SMS for users without WhatsApp. Configure the Sinch App with channel priority (WhatsApp first, SMS fallback) and your Edge Function handles the routing transparently.

Lovable Prompt

Create a notification system that sends order status updates to customers. Use my sinch-message Edge Function with channel priority WhatsApp then SMS. Send 'Your order #[ID] has shipped and will arrive [date].' Log each notification to a notifications table with channel used, status, and message ID.

Copy this prompt to try it in Lovable

Two-way conversational SMS support channel

Build a customer support inbox where agents can reply to incoming SMS messages from a Lovable dashboard. Inbound messages come in via the Sinch webhook callback Edge Function, appear in a real-time conversation view using Supabase Realtime, and agents send replies by calling the outbound message Edge Function.

Lovable Prompt

Create a support inbox that shows incoming SMS messages from customers in real-time. Messages come in via my sinch-inbound Edge Function and are stored in a conversations table. Build a conversation view where I can type and send replies by calling my sinch-message Edge Function. Group messages by customer phone number.

Copy this prompt to try it in Lovable

Troubleshooting

Edge Function returns 401 Unauthorized when calling the Sinch OAuth token endpoint

Cause: The Key ID or Key Secret in Cloud → Secrets is incorrect, or the btoa encoding of keyId:keySecret has extra spaces or newlines.

Solution: Go to Cloud → Secrets and verify SINCH_KEY_ID and SINCH_KEY_SECRET have no leading or trailing spaces. In the Sinch Dashboard, go to Settings → Access Keys and verify the key is still active (not deleted or rotated). If the key was created long ago, create a new one and update the secrets.

typescript
1const credentials = btoa(`${Deno.env.get('SINCH_KEY_ID')!.trim()}:${Deno.env.get('SINCH_KEY_SECRET')!.trim()}`);

Message API returns 404 with 'app not found' or project-related error

Cause: The SINCH_PROJECT_ID or SINCH_APP_ID secret does not match the actual project or app, or you are calling the US endpoint but your project is on the EU data region.

Solution: Verify the Project ID and App ID in the Sinch Dashboard match your secrets exactly. If your Sinch account is based in Europe, change the API base URL from us.conversation.api.sinch.com to eu.conversation.api.sinch.com in your Edge Function.

typescript
1const baseUrl = 'https://eu.conversation.api.sinch.com'; // for EU data residency

Messages are accepted by the API (200 returned) but never delivered to the recipient

Cause: The recipient phone number is not in E.164 format, the SMS channel is not enabled in the Conversation API App, or the sender number is not verified for the destination country.

Solution: Ensure recipient numbers are in E.164 format (+[country code][number]). In the Sinch Dashboard, open your Conversation API App → Channels and verify SMS is enabled and a valid number/sender is assigned. Check the Sinch Dashboard → Conversation API → Logs for delivery status details.

Sinch webhook callbacks never arrive at the Edge Function

Cause: The webhook URL is pointing to the Lovable preview URL instead of the deployed Edge Function URL, or the webhook trigger type is not enabled for the events you expect.

Solution: In app.sinch.com → your App → Webhooks, verify the registered URL matches your deployed Edge Function URL exactly (from Cloud → Edge Functions). The deployed URL looks like https://[ref].supabase.co/functions/v1/sinch-webhook. Also confirm the MESSAGE_INBOUND trigger is checked in the webhook configuration.

Best practices

  • Cache the Sinch Bearer token in Supabase with its expiry timestamp (token expires in 3600 seconds) to avoid a new OAuth call on every message send
  • Always use E.164 format for phone numbers in the channel_identities array — Sinch will reject national or local number formats
  • Configure channel priority in your Sinch App to automatically try the best channel (RCS → WhatsApp → SMS) rather than hardcoding a single channel in your Edge Function
  • Store the returned message_id in your Supabase database so you can match delivery receipt callbacks to original messages
  • Use EU data residency (eu.conversation.api.sinch.com) if your users are primarily in Europe to comply with GDPR data locality requirements
  • Rotate your Sinch Access Keys periodically and update Cloud → Secrets immediately — compromised keys can send messages and incur charges on your account
  • Test with a sandbox project in Sinch before switching to production credentials to avoid sending accidental messages to real phone numbers

Alternatives

Frequently asked questions

Does Sinch support WhatsApp Business messaging in Lovable?

Yes — WhatsApp is one of the channels supported by the Sinch Conversation API. However, activating WhatsApp requires a Meta-approved WhatsApp Business Account linked to your Sinch App, which involves a verification process. Contact Sinch support to begin WhatsApp enablement. Once approved, you send WhatsApp messages by setting the channel to WHATSAPP in the channel_identities array.

How does Sinch pricing compare to Twilio for SMS?

Sinch and Twilio have comparable SMS pricing for US numbers, but Sinch is often more cost-effective for international messaging due to its direct carrier agreements in more markets. Both have pay-as-you-go pricing with per-message rates varying by destination country. Check Sinch's pricing page at sinch.com/pricing for current rates.

Can I send RCS messages to Android users with Sinch?

Yes — RCS (Rich Communication Services) is supported by the Sinch Conversation API on supported Android devices and carriers. To use RCS, enable it as a channel in your Sinch App and set the channel to RCS in your message send request. Sinch automatically falls back to SMS for recipients whose devices or carriers do not support RCS.

Does the Sinch integration work differently for EU-based Lovable projects?

The integration logic is identical, but you should use the EU regional endpoint (eu.conversation.api.sinch.com instead of us.conversation.api.sinch.com) if your Sinch account is configured with European data residency. This ensures message data stays within EU data centers for GDPR compliance. Check your Sinch account region under Settings → Account.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.