Integrating Custom SMS Auth in Next.js + Clerk in 5 Minutes via Webhook (Zero Paperwork)
2026-05-11T01:02:21.146Z
The Dilemma of SMS Authentication in Side Projects
If you are a Next.js developer building a side project or a startup MVP, you have likely chosen Clerk as your primary authentication solution. It is universally loved for its seamless integration of social logins and email magic links. However, when you decide to implement Phone Number Login (SMS OTP)—especially when targeting regions with strict telecom regulations like South Korea—you hit a massive roadblock.
Traditional SMS API providers demand an exhausting amount of paperwork before you can send a single text message:
- Business Registration Certificates
- Proof of Telecom Service Subscription
- Mandatory Sender ID Pre-registration (which can take 1 to 3 business days to be approved)
As a solo developer or a freelance engineer, you just want to launch your MVP this weekend. You don't have the time or the business entity set up to deal with days of bureaucratic red tape. So, is there a way to integrate custom SMS authentication instantly, with zero paperwork?
The Solution: Clerk Custom Webhooks + EasyAuth
The answer is a resounding yes. You can build a robust, production-ready SMS authentication flow in under 5 minutes with absolutely no paperwork.
The secret lies in combining Clerk's Custom SMS Webhook feature with EasyAuth, a developer-centric SMS API designed for speed and simplicity.
In this architecture, Clerk handles the heavy lifting of user management, OTP code generation, and verification. It simply delegates the actual delivery of the SMS message to your Next.js server via a webhook. Your server then forwards this message to EasyAuth, which dispatches the SMS instantly.
Why EasyAuth is the Ultimate Developer Tool
- Zero Paperwork: Forget about submitting business licenses or telecom proofs.
- Instant Setup: Sign up, get your API key, and complete the API integration in 5 minutes.
- Automated Sender ID: No need to pre-register and wait days for a sender number approval.
- Highly Cost-Effective: At just 15~25 KRW per message, it is significantly cheaper than legacy providers (which charge 30~50 KRW). Plus, you get 10 free SMS upon signup to test your MVP immediately.
Step-by-Step Implementation Guide
Step 1. Configure the Webhook in the Clerk Dashboard
First, we need to instruct Clerk to notify our Next.js application whenever it generates an OTP that needs to be sent via SMS.
- Navigate to your Clerk Dashboard and open the [Webhooks] section.
- Click on Add Endpoint.
- Enter your Endpoint URL. If your Next.js app is running locally, use a tool like
ngrokto expose your localhost (e.g.,https://your-ngrok-url.app/api/webhooks/clerk-sms). - Under 'Message Filtering', select the
sms.createdevent. - Once the webhook is created, copy the Signing Secret provided by Clerk. You will need to save this in your
.env.localfile.
CLERK_WEBHOOK_SECRET=whsec_your_clerk_secret
EASYAUTH_API_KEY=ea_your_easyauth_api_key
Step 2. Create the Next.js Webhook Route
We will be using the Next.js App Router. Create a new file at app/api/webhooks/clerk-sms/route.ts.
Security is paramount when dealing with webhooks. To ensure that the incoming requests are legitimately coming from Clerk and not a malicious actor, we will use the svix package to verify the webhook signatures.
npm install svix @clerk/nextjs
Step 3. Dispatch the SMS via EasyAuth API (Complete Code)
EasyAuth offers a beautifully simple two-endpoint API architecture:
POST /send- To dispatch an SMS.POST /verify- To verify an OTP.
Because Clerk natively handles the verification of the OTP when using the webhook integration, we only need to utilize EasyAuth's POST /send endpoint.
Below is the complete, copy-pasteable code for your webhook route:
// app/api/webhooks/clerk-sms/route.ts
import { Webhook } from 'svix';
import { WebhookEvent } from '@clerk/nextjs/server';
export async function POST(req: Request) {
const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) {
throw new Error('Please add CLERK_WEBHOOK_SECRET from Clerk Dashboard to .env or .env.local');
}
// 1. Extract Svix headers from the incoming request
const svix_id = req.headers.get("svix-id");
const svix_timestamp = req.headers.get("svix-timestamp");
const svix_signature = req.headers.get("svix-signature");
if (!svix_id || !svix_timestamp || !svix_signature) {
return new Response('Error: Missing Svix headers', {
status: 400
});
}
// 2. Parse the request body and verify the signature
const payload = await req.json();
const body = JSON.stringify(payload);
const wh = new Webhook(WEBHOOK_SECRET);
let evt: WebhookEvent;
try {
evt = wh.verify(body, {
"svix-id": svix_id,
"svix-timestamp": svix_timestamp,
"svix-signature": svix_signature,
}) as WebhookEvent;
} catch (err) {
console.error('Error verifying webhook:', err);
return new Response('Error verifying webhook signature', { status: 400 });
}
// 3. Process the sms.created event and trigger EasyAuth
if (evt.type === 'sms.created') {
const { phone_number, message } = evt.data;
try {
// Call EasyAuth's /send endpoint
const easyAuthRes = await fetch('https://api.easyauth.co.kr/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.EASYAUTH_API_KEY}`
},
body: JSON.stringify({
to: phone_number,
text: message // This contains the OTP generated by Clerk
})
});
if (!easyAuthRes.ok) {
throw new Error('Failed to dispatch SMS via EasyAuth');
}
console.log(`SMS successfully dispatched to ${phone_number}`);
} catch (error) {
console.error('Failed to send SMS:', error);
// Returning a 500 status will let Clerk know the delivery failed
return new Response('Failed to deliver SMS', { status: 500 });
}
}
// Acknowledge receipt of the webhook
return new Response('Webhook processed successfully', { status: 200 });
}
Tips & Best Practices
- Never Skip Security Verification: Your webhook endpoint is public. If you skip the Svix signature verification, anyone who discovers your endpoint URL could exploit it to send SMS messages at your expense.
- Handle Errors Gracefully: Always wrap your external API calls in a
try-catchblock. If the EasyAuth API call fails (due to invalid phone numbers or unexpected network issues), returning a non-200 status code allows Clerk to mark the SMS delivery as failed in their dashboard. - Fully Custom Auth Flows: If your project scales and you decide to migrate away from Clerk to build a 100% custom authentication system, you don't need to change your SMS provider. EasyAuth provides an equally simple
POST /verifyendpoint, allowing you to manage the entire generation, dispatch, and validation lifecycle internally with just a few lines of code.
Conclusion
By leveraging Clerk's Custom SMS Webhooks alongside EasyAuth, you can completely sidestep the bureaucratic nightmare of traditional telecom APIs.
As developers, our time is best spent writing business logic and shipping core features—not filling out business registration forms or waiting days for sender ID approvals. If you are a freelancer, a solo dev, or part of a startup team trying to move fast, EasyAuth is the perfect zero-paperwork solution designed explicitly for you.
Start immediately, launch your MVP in 5 minutes, and take advantage of the 10 free SMS credits available upon signup. Happy coding!
비트베이크에서 광고를 시작해보세요
광고 문의하기