Why use Inbound Apps?
With Inbound Apps, AI agents and third-party applications can securely access 10x CRM data with proper authentication and authorization.
Defining CRM-Specific OAuth Scopes
For our 10x CRM, we defined granular scopes to control access to different parts of the application. We createdcontacts:read
anddeals:read
scopes, with clear descriptions of what they allow.

Designing the 10x CRM Consent Flow
We designed a user-friendly consent flow that clearly explains to users what permissions they're granting to third-party applications. The flow first checks if the user is logged in, then presents a consent screen that describes the scopes the application is requesting access to.

After setting up our Inbound App in the Descope Console, we implemented the necessary code in our 10x CRM application to validate tokens and enforce scope-based permissions:
OAuth Middleware for CRM API Protection
We implemented a custom OAuth middleware that validates JWT tokens issued by Descope, extracts the scopes, and ensures that API requests have the necessary permissions.
// OAuth middleware for 10x CRM API protection
import { NextRequest, NextResponse } from "next/server"
import descopeSdk from '@descope/node-sdk'
export function withOAuth(handler, requiredScopes = []) {
return async function (req: NextRequest) {
const token = req.headers.get('authorization')?.replace('Bearer ', '')
if (!token) {
return NextResponse.json({ error: 'Missing token' }, { status: 401 })
}
try {
const validationResponse = await sdk.validateSession(token)
const scopes = (validationResponse.token.scope as string).split(' ')
if (requiredScopes.some(scope => !scopes.includes(scope))) {
return NextResponse.json({
error: 'Insufficient permissions',
requiredScopes
}, { status: 403 })
}
return handler(req, { scopes })
} catch (error) {
return NextResponse.json({ error: 'Invalid token' }, { status: 401 })
}
}
}
Protected CRM API Routes
We applied the OAuth middleware to our CRM API routes, ensuring that each endpoint requires the appropriate scopes. For example, the contacts API requires the "contacts:read" scope for GET requests.
// Example protected CRM API route
export const GET = withOAuth(
async (req, context) => {
// Your API logic here
return NextResponse.json({ data: "Your response" })
},
["contacts:read"]
)