Connect API Integration Guide
Overview
The DIDfarm API is a REST API that returns JSON. All endpoints are served over HTTPS at https://didfarm.com/api/v1/. Authentication uses Bearer tokens (API keys). Rate limits are generous for partner accounts.
The API lets you automate the full number lifecycle: search inventory, purchase numbers, create SIP trunks, assign routing, query billing, and receive real-time events via webhooks.
Authentication
All API requests require a Bearer token in the Authorization header.
Generate an API Key
- Log in to didfarm.com/my-numbers
- Go to Account → API Keys
- Click Generate Key
- Copy the key immediately — it is shown only once
Authorization: Bearer df_live_abc123def456...
Error Responses
All errors return a consistent JSON structure:
{
"error": {
"code": "insufficient_balance",
"message": "Wallet balance too low for this purchase.",
"status": 422
}
}| Status Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Resource created |
| 401 | Invalid or missing API key |
| 403 | Forbidden (insufficient permissions) |
| 404 | Resource not found |
| 422 | Validation error (check error message) |
| 429 | Rate limit exceeded |
| 500 | Server error (retry with backoff) |
Provision Numbers for Clients
Provisioning a number for a client involves three steps: search, purchase, and assign.
Search Available Numbers
curl "https://didfarm.com/api/v1/numbers/search?country=NL&type=local&city=Amsterdam" \ -H "Authorization: Bearer $API_KEY"
{
"data": [
{
"number": "+31201234567",
"country": "NL",
"type": "local",
"city": "Amsterdam",
"monthly_rate": 150,
"setup_fee": 0,
"sms_enabled": false,
"requires_regulatory": true
}
],
"meta": { "total": 48, "page": 1 }
}monthly_rate and setup_fee fields are returned in EUR cents. Divide by 100 for display (e.g., 150 = EUR 1.50/month).
Purchase a Number
curl -X POST "https://didfarm.com/api/v1/wallet/checkout" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"number": "+31201234567",
"country": "NL",
"type": "local"
}
]
}'The purchase is atomic: wallet debit, order creation, and provisioning happen in a single transaction. If any step fails, the wallet is not debited.
Assign to a Trunk
curl -X POST "https://didfarm.com/api/v1/portal/did-routing" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"did_number_id": 42,
"sip_trunk_id": 7
}'Create Client SIP Trunks
Each client should have their own SIP trunk with isolated credentials.
curl -X POST "https://didfarm.com/api/v1/portal/sip-trunks" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"auth_type": "digest"
}'{
"data": {
"uuid": "trk_a1b2c3d4",
"name": "Acme Corp",
"sip_server": "sip.didfarm.com",
"username": "acme_xk9f2",
"password": "Rn8kP2mQ...",
"status": "provisioning"
}
}password field is returned only in the creation response. Store it securely and share it with your client. You can regenerate credentials later, but this will disconnect the client's PBX.
List Trunks
curl "https://didfarm.com/api/v1/portal/sip-trunks" \ -H "Authorization: Bearer $API_KEY"
Regenerate Credentials
curl -X POST "https://didfarm.com/api/v1/portal/sip-trunks/trk_a1b2c3d4/regenerate-credentials" \ -H "Authorization: Bearer $API_KEY"
Send Messages for Clients
If a number supports SMS, you can send outbound messages via the API:
curl -X POST "https://didfarm.com/api/v1/messages/send" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "+31612345678",
"to": "+31687654321",
"body": "Your appointment is confirmed for tomorrow at 10:00."
}'sms_enabled field in the number search response. SMS rates are charged per message and deducted from your wallet.
Billing per Client
Use the transaction history API to calculate per-client costs:
curl "https://didfarm.com/api/v1/wallet/transactions?page=1" \ -H "Authorization: Bearer $API_KEY"
Each transaction includes a reference_type and reference_id that links back to the order, number, or trunk. Use this to aggregate costs by client trunk and generate your own invoices.
Check Wallet Balance
curl "https://didfarm.com/api/v1/wallet/balance" \
-H "Authorization: Bearer $API_KEY"
# Response: { "balance": "142.50", "currency": "EUR" }Webhooks
DIDfarm can send real-time HTTP POST notifications to your server when events occur. Configure your webhook URL in the portal under Account → Webhooks.
| Event | Description |
|---|---|
number.activated | A number has been provisioned and is live |
number.suspended | A number was suspended (e.g., renewal failed) |
number.released | A number was returned to the carrier pool |
regulatory.approved | Regulatory documents were approved |
regulatory.rejected | Regulatory documents were rejected |
trunk.registered | A SIP trunk successfully registered |
trunk.offline | A SIP trunk lost registration |
message.received | An inbound SMS was received on a number |
{
"event": "number.activated",
"timestamp": "2026-04-05T14:30:00Z",
"data": {
"number": "+31201234567",
"country": "NL",
"type": "local",
"trunk_uuid": "trk_a1b2c3d4",
"order_id": 1042
}
}X-DIDfarm-Signature header. Validate the HMAC-SHA256 signature using your webhook secret to ensure the request is genuine.
Code Examples
Below is a complete Node.js example that provisions a number and creates a trunk for a new client.
const API_KEY = process.env.DIDFARM_API_KEY;
const BASE = 'https://didfarm.com/api/v1';
async function api(method, path, body) {
const res = await fetch(`${BASE}${path}`, {
method,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) throw new Error(`API ${res.status}: ${await res.text()}`);
return res.json();
}
async function onboardClient(clientName, country, type) {
// 1. Search for an available number
const search = await api('GET',
`/numbers/search?country=${country}&type=${type}`);
const number = search.data[0];
console.log(`Found: ${number.number} at ${number.monthly_rate} cents/mo`);
// 2. Purchase the number
const order = await api('POST', '/wallet/checkout', {
items: [{ number: number.number, country, type }],
});
console.log(`Order #${order.data.order_id} created`);
// 3. Create a SIP trunk for the client
const trunk = await api('POST', '/portal/sip-trunks', {
name: clientName,
auth_type: 'digest',
});
console.log(`Trunk: ${trunk.data.username} / ${trunk.data.password}`);
// 4. Assign the number to the trunk
await api('POST', '/portal/did-routing', {
did_number_id: order.data.items[0].did_number_id,
sip_trunk_id: trunk.data.id,
});
console.log(`${number.number} assigned to ${clientName}`);
return { number: number.number, trunk: trunk.data };
}
// Usage
onboardClient('Acme Corp', 'NL', 'local')
.then(result => console.log('Done:', result))
.catch(err => console.error('Error:', err));Frequently Asked Questions
What are the rate limits?
Partner accounts are allowed 60 requests per minute by default. If you need higher limits for bulk provisioning, contact the partnerships team to increase your quota.
Is there a sandbox environment?
A sandbox environment is on the roadmap. For now, test with low-cost numbers (e.g., US local numbers) on the live API. Refunds for test numbers can be arranged with support.
Can I use the API with Sanctum cookie auth instead of API keys?
Yes, if you are building a frontend that authenticates users via the DIDfarm login flow. Sanctum cookie auth works for browser-based SPA requests. For server-to-server integration, use API keys.
How do I handle regulatory requirements via API?
When a number requires regulatory documents, the checkout response includes a requires_regulatory: true flag. Use the POST /api/regulatory/submit endpoint to upload documents and submit for review. The regulatory.approved webhook fires when documents are approved.
What happens if my webhook endpoint is down?
DIDfarm retries webhook deliveries with exponential backoff for up to 24 hours. After that, the event is dropped. Monitor your webhook endpoint uptime to avoid missing events.
Start building with the API
Generate an API key and make your first request in minutes.