LeadConnector Integration
Connecting NumberHub to LeadConnector assigns your purchased phone numbers to LeadConnector sub-accounts (locations), causing inbound SMS to flow directly into LeadConnector Conversations. When a message arrives from an unknown number, LeadConnector automatically creates a new contact record so every conversation is captured and attributed — no manual data entry required.
Prerequisites
- An active LeadConnector Agency account with at least one sub-account (location) configured
- A NumberHub account with a positive credit balance
- At least one purchased phone number in your NumberHub dashboard
Step-by-Step Setup
Log in to your NumberHub dashboard.
Visit numberhub.ai and sign in to your account.
Navigate to the LeadConnector integration.
Go to Settings → Integrations → LeadConnector.
Start the OAuth connection flow.
Click "Connect with LeadConnector" — you will be redirected to the LeadConnector OAuth authorization screen.
Authorize the permissions.
Review and approve the requested permissions, then click Authorize.
Confirm the connection.
You will be redirected back to NumberHub with a success confirmation — the integration is now active.
Assign a number to a LeadConnector location.
In the Numbers table, locate the phone number you want to assign. Use the "Assign to Sub-Account" dropdown to map it to a specific LeadConnector location.
Send a test SMS.
Send a test SMS to your NumberHub phone number — it should appear in the LeadConnector Conversations inbox within seconds.
Permissions Requested
During OAuth authorization, NumberHub requests the following scopes from LeadConnector:
- Contacts: read and write — to create or update contact records when inbound messages arrive from unknown numbers
- Conversations: read and write — to post inbound SMS into the LeadConnector Conversations inbox and associate them with the correct contact
- Locations: read — to populate the sub-account assignment dropdown in the NumberHub Numbers table
Disconnecting
To revoke access, navigate to Settings → Integrations → LeadConnector and click "Disconnect." Existing number assignments and conversation history are preserved in LeadConnector; new inbound messages will no longer sync to LeadConnector after disconnection.
Troubleshooting
- OAuth scope rejection — If LeadConnector shows a permissions error during authorization, ensure your LeadConnector account has Agency-level access. Sub-account-only users cannot authorize agency-scoped OAuth apps.
- Number not appearing in LeadConnector Conversations — Verify the number is assigned to the correct LeadConnector location in the NumberHub Numbers table. Check that the integration status shows "Connected" in Settings → Integrations.
- Duplicate contacts — LeadConnector deduplicates contacts by phone number. If a contact already exists with the same number, LeadConnector will attach the conversation to the existing record rather than create a new one. This is expected behavior.
- Inbound SMS delayed — NumberHub delivers webhooks in real time. If messages appear delayed in LeadConnector, check your LeadConnector account's conversation processing status or contact NumberHub support.
Webhook Integration (Custom Backends)
Webhooks let you receive real-time event notifications — inbound SMS, delivery confirmations, and number lifecycle events — at any HTTPS endpoint you control. This is the recommended approach for custom backends, AI agents, and any workflow tool that can accept an HTTP POST.
Registering a Webhook
To register a webhook, navigate to Settings → Webhooks in your NumberHub dashboard. Enter your HTTPS endpoint URL and select which event types to subscribe to. There is currently no REST API endpoint for webhook registration — use the dashboard.
Webhook Payload Structure
All events share a common envelope with a top-level event type, an ISO 8601 timestamp, and a data object whose shape varies by event type:
{
"event": "message.inbound",
"timestamp": "2024-01-15T10:30:00.000Z",
"data": { ... }
}Signature Verification
NumberHub signs every webhook with an HMAC-SHA256 signature sent in the X-NumberHub-Signature header. Always verify this before processing the payload to ensure the request originated from NumberHub and has not been tampered with:
Always verify webhook signatures
Never process webhook payloads without first validating the X-NumberHub-Signature header. Skipping this check opens your endpoint to spoofed events from third parties.
const crypto = require('crypto');
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-numberhub-signature'];
const expected = crypto
.createHmac('sha256', process.env.NUMBERHUB_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
console.log('Received event:', event.event);
res.status(200).send('OK');
});Retry Behavior
If your endpoint returns a non-2xx status code or does not respond within the 10-second timeout, NumberHub retries delivery 3 times with exponential backoff (30 seconds, 2 minutes, then 10 minutes). After 3 failed attempts the event is marked as failed and logged in your dashboard under Settings → Webhooks → Event Log.
Full Inbound SMS Handler
The following Express.js route verifies the signature, acknowledges receipt immediately (always respond before doing async work), and echoes a reply to the sender:
app.post('/webhook/sms', express.raw({ type: 'application/json' }), async (req, res) => {
// Always acknowledge first
res.status(200).send('OK');
const { event, data } = JSON.parse(req.body);
if (event !== 'message.inbound') return;
const { from, to, body, numberId } = data;
console.log(`SMS from ${from}: ${body}`);
// Send a reply using the REST API
await fetch('https://api.numberhub.ai/api/integration/send', {
method: 'POST',
headers: {
'X-Hub-Api-Key': process.env.NUMBERHUB_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ from: to, to: from, body: 'Thanks for reaching out!' }),
});
});REST API Integration
For direct REST API consumers, no SDK is required. Any HTTP client — curl, fetch, axios, Python requests, or any language with HTTP support — can integrate with NumberHub using the X-Hub-Api-Key header and JSON request/response bodies.
Quick Setup
Generate an API key.
Open Dashboard → Settings → API Keys and create a new key.
Set the key as an environment variable.
Add the key to your runtime environment:
NUMBERHUB_API_KEY=nh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxStart making authenticated requests.
Make requests to https://api.numberhub.ai/api/integration with the X-Hub-Api-Key header on every request.
Buy a Number and Send an SMS
The following Node.js example walks through the complete flow: searching available inventory, purchasing a number, and sending an outbound SMS — all in a single script:
const API_BASE = 'https://api.numberhub.ai/api/integration';
const headers = {
'X-Hub-Api-Key': process.env.NUMBERHUB_API_KEY,
'Content-Type': 'application/json',
};
// Search for available numbers (must be a 3-digit US area code)
const searchRes = await fetch(`${API_BASE}/numbers/search?areaCode=415`, { headers });
const { numbers } = await searchRes.json();
const phoneNumber = numbers[0].phoneNumber;
// Purchase the number
const purchaseRes = await fetch(`${API_BASE}/numbers/purchase`, {
method: 'POST',
headers,
body: JSON.stringify({ phoneNumber }),
});
const purchased = await purchaseRes.json();
console.log('Purchased:', purchased.phoneNumber);
// Send an SMS
const smsRes = await fetch(`${API_BASE}/send`, {
method: 'POST',
headers,
body: JSON.stringify({ from: purchased.phoneNumber, to: '+15559876543', body: 'Hello!' }),
});
const message = await smsRes.json();
console.log('Message ID:', message.messageId);Error Handling
Always check the HTTP status code for errors. On failure the response body contains an error string describing the problem. Use the HTTP status code for programmatic branching:
const res = await fetch(`${API_BASE}/numbers/purchase`, {
method: 'POST',
headers,
body: JSON.stringify({ phoneNumber }),
});
const json = await res.json();
if (!res.ok) {
console.error(`Error ${res.status}: ${json.error}`);
if (res.status === 402) {
// Insufficient credit balance
}
}A 402 status indicates insufficient credit balance. Top up your balance from the Billing section of the dashboard before retrying the request.
List Responses
List endpoints such as GET /numbers return a flat JSON object with an array field — for example { numbers: [...] }. All active numbers for the authenticated sub-account are returned in a single response.
