Unified Messaging API - Client Development Guide
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ 🤖 Creating API clients with AI engines? │
│ │
│ Download this guide: │
│ https://apiaccess.digify.no/client-guide │
│ │
│ This document is optimized for Claude Code and other AI tools │
│ to help generate production-ready API client implementations. │
│ │
└─────────────────────────────────────────────────────────────────────┘#Quick Start
Base URL: https://apiaccess.digify.no/api/v1
Authentication: All endpoints require an AccessToken provided via:
- Query parameter: ?AccessToken=YOUR_TOKEN
- Header: X-Access-Token: YOUR_TOKEN
- Header: Authorization: Bearer YOUR_TOKEN
Response Format: All responses are JSON with consistent structure:
- Success: {"status": "success", ...}
- Error: {"status": "error", "error": {...}}
#Table of Contents
1. Authentication
2. Endpoints
- Send Messages
- Get SMS Logs
- Get Email Logs
- Search Companies
- Get Company by OrgNr
- Health Check
3. Error Handling
4. Rate Limiting
5. TypeScript Types
6. Example Implementations
#Authentication
The API uses token-based authentication. Obtain your AccessToken from the administrator.
Token Requirements:
- Length: 22 characters
- Case-sensitive
- Must be included in every request (except health checks)
Example:
GET /api/v1/companies/search?search=DNB&AccessToken=yYuX5ZtV4kCpbXcZgBA#Endpoints
#1. Send Messages
Send SMS and/or Email messages through a unified endpoint.
Method: POST
Path: /api/v1/messages/send
Auth: Required
#### Request Body Schema
{
"messages": [
{
"type": "sms" | "email",
// SMS-specific fields or Email-specific fields
}
]
}#### SMS Message Object
{
"type": "sms",
"id": "optional-client-reference",
"destAddr": "4712345678",
"msg": "Your message text here",
"sourceAddr": "1234",
"sourceAddrTon": 1,
"sourceAddrNpi": 1,
"esmClass": 0,
"protocolId": 0,
"priority": 0,
"dataCoding": 1,
"sendWhen": "2025-01-01T12:00:00Z",
"scheduleDeliveryTime": "2025-01-01T12:00:00Z",
"validityPeriod": "2025-01-02T12:00:00Z"
}Required Fields: type, destAddr, msg
Field Descriptions:
| Field | Type | Required | Max Length | Description |
|---|---|---|---|---|
| type | string | ✓ | - | Must be "sms" |
| id | string | - | - | Client-provided reference ID |
| destAddr | string | ✓ | 15 | Recipient phone number |
| msg | string | ✓ | 1600 | Message content |
| sourceAddr | string | - | 15 | Sender address (default: "1234567890") |
| sourceAddrTon | number | - | - | Type of Number (0-255, default: 1) |
| sourceAddrNpi | number | - | - | Numbering Plan Indicator (0-255, default: 1) |
| esmClass | number | - | - | ESM class (0-255, default: 0) |
| protocolId | number | - | - | Protocol ID (0-255, default: 0) |
| priority | number | - | - | Priority level (0-255, default: 0) |
| dataCoding | number | - | - | Data coding scheme (0-255, default: 1) |
| sendWhen | string/date | - | - | When to send (ISO 8601 format) |
| scheduleDeliveryTime | string/date | - | - | Scheduled delivery time |
| validityPeriod | string/date | - | - | Message validity period |
#### Email Message Object
{
"type": "email",
"sourceAddr": "noreply@example.com",
"sourceAddrName": "Company Name",
"destAddr": "user@example.com",
"destAddrName": "User Name",
"subject": "Email Subject",
"body": "<html><body><h1>Email Content</h1></body></html>",
"bodyFormat": "html",
"emailProviderId": 2
}Required Fields: type, sourceAddr, destAddr, subject, body
Field Descriptions:
| Field | Type | Required | Max Length | Description |
|---|---|---|---|---|
| type | string | ✓ | - | Must be "email" |
| sourceAddr | string | ✓ | 100 | Sender email address (must be valid email) |
| sourceAddrName | string | - | 200 | Sender display name |
| destAddr | string | ✓ | 500 | Recipient email address (must be valid email) |
| destAddrName | string | - | 200 | Recipient display name |
| subject | string | ✓ | 500 | Email subject line |
| body | string | ✓ | 1048576 | Email body (1MB max) |
| bodyFormat | string | - | - | "html" or "text" (default: "html") |
| emailProviderId | number | - | - | Email provider ID (default: 2) |
#### Success Response (200 OK)
{
"status": "success",
"simulationMode": false,
"results": [
{
"id": "optional-client-reference",
"type": "sms",
"status": "queued",
"messageId": 123456,
"msgCount": 1
},
{
"type": "email",
"status": "queued",
"emailId": 789
}
],
"summary": {
"total": 2,
"sms": 1,
"email": 1,
"creditsUsed": 1
}
}#### Example Request
curl -X POST "https://apiaccess.digify.no/api/v1/messages/send?AccessToken=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"type": "sms",
"destAddr": "4712345678",
"msg": "Hello from API!"
},
{
"type": "email",
"sourceAddr": "noreply@example.com",
"destAddr": "user@example.com",
"subject": "Test Email",
"body": "<h1>Hello!</h1>"
}
]
}'#2. Get SMS Logs
Retrieve SMS message logs for your system.
Method: GET
Path: /api/v1/logs/sms
Auth: Required
#### Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| limit | number | - | 50 | Number of records (1-100) |
| offset | number | - | 0 | Records to skip |
| status | string | - | - | Filter by status |
| startDate | string | - | - | Filter start date (ISO 8601) |
| endDate | string | - | - | Filter end date (ISO 8601) |
#### Success Response (200 OK)
{
"status": "success",
"data": [
{
"messageId": 10798634,
"destAddr": "4791748750",
"msg": "Test SMS",
"msgCount": 1,
"status": "queued",
"createdAt": "2025-10-18T11:34:15.000Z",
"sendWhen": "2025-10-18T11:34:15.000Z",
"sourceAddr": "1234567890"
}
],
"pagination": {
"total": 150,
"limit": 50,
"offset": 0,
"hasMore": true
}
}#### Example Request
curl "https://apiaccess.digify.no/api/v1/logs/sms?AccessToken=YOUR_TOKEN&limit=10&offset=0"#3. Get Email Logs
Retrieve email message logs for your system.
Method: GET
Path: /api/v1/logs/email
Auth: Required
#### Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| limit | number | - | 50 | Number of records (1-100) |
| offset | number | - | 0 | Records to skip |
| status | string | - | - | Filter by status |
| startDate | string | - | - | Filter start date (ISO 8601) |
| endDate | string | - | - | Filter end date (ISO 8601) |
#### Success Response (200 OK)
{
"status": "success",
"data": [
{
"emailId": 549870,
"sourceAddr": "noreply@example.com",
"destAddr": "user@example.com",
"subject": "Test Email",
"status": "queued",
"createdAt": "2025-10-18T11:34:18.000Z",
"bodyFormat": "html"
}
],
"pagination": {
"total": 75,
"limit": 50,
"offset": 0,
"hasMore": true
}
}#### Example Request
curl "https://apiaccess.digify.no/api/v1/logs/email?AccessToken=YOUR_TOKEN&limit=10&offset=0"#4. Search Companies
Search for Norwegian companies by name or organization number.
Method: GET
Path: /api/v1/companies/search
Auth: Required
#### Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| search | string | ✓ | Company name or 9-digit organization number |
Search Behavior:
- If
search is exactly 9 numeric digits → searches by organization number- Otherwise → searches by company name (prefix match)
- Returns maximum 5 results
- Name searches exclude hidden and deleted companies
#### Success Response (200 OK)
{
"status": "success",
"companies": [
{
"orgNr": "987654321",
"navn": "Example Company AS",
"forrAdrPostSted": "Oslo"
},
{
"orgNr": "123456789",
"navn": "Another Example AS",
"forrAdrPostSted": "Bergen"
}
]
}Response Fields:
| Field | Type | Description |
|---|---|---|
| orgNr | string | 9-digit organization number |
| navn | string | Company name |
| forrAdrPostSted | string | City/postal location |
#### Example Requests
# Search by company name
curl "https://apiaccess.digify.no/api/v1/companies/search?search=DNB&AccessToken=YOUR_TOKEN"Search by organization number
curl "https://apiaccess.digify.no/api/v1/companies/search?search=984851006&AccessToken=YOUR_TOKEN"#5. Get Company by Organization Number
Get a specific company by its 9-digit organization number.
Method: GET
Path: /api/v1/companies/:orgNr
Auth: Required
#### URL Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| orgNr | string | ✓ | Exactly 9 numeric digits |
#### Success Response (200 OK)
Returns comprehensive company information with all available fields:
{
"status": "success",
"companies": [
{
"orgnr": 984851006,
"navn": "DNB BANK ASA",
"organisasjonsform": "ASA",
"forretningsadr": "Dronning Eufemias gate 30",
"forradrpostnr": "0191",
"forradrpoststed": "OSLO",
"forradrkommnr": 301,
"forradrkommnavn": "OSLO",
"forradrland": "Norge",
"postadresse": "Postboks 1600 Sentrum",
"ppostnr": "0021",
"ppoststed": "OSLO",
"ppostland": "Norge",
"regifr": true,
"regimva": true,
"nkode1": "64.190",
"nkode2": null,
"nkode3": null,
"sektorkode": 3200,
"konkurs": false,
"avvikling": false,
"tvangsavvikling": false,
"regiaa": false,
"regifriv": false,
"regdato": "2002-09-12T00:00:00.000Z",
"stiftelsesdato": "2002-09-10T00:00:00.000Z",
"tlf": null,
"tlf_mobil": "4791504800",
"url": "www.dnb.no",
"regnskap": 2023,
"hovedenhet": null,
"ansatte_antall": 8343,
"ansatte_dato": "2025-02-12T00:00:00.000Z",
"Slettet": false,
"Hidden": false
}
]
}Field Descriptions:
| Field | Type | Description |
|---|---|---|
| orgnr | number | Organization number |
| navn | string | Company name |
| organisasjonsform | string | Organization form (AS, ASA, etc.) |
| forretningsadr | string | Business address street |
| forradrpostnr | string | Business address postal code |
| forradrpoststed | string | Business address city |
| forradrkommnr | number | Business address municipality number |
| forradrkommnavn | string | Business address municipality name |
| forradrland | string | Business address country |
| postadresse | string | Postal address |
| ppostnr | string | Postal code |
| ppoststed | string | Postal city |
| ppostland | string | Postal country |
| regifr | boolean | Registered in Foretaksregisteret |
| regimva | boolean | Registered for VAT |
| nkode1 | string | Primary industry code (NACE) |
| nkode2 | string | Secondary industry code |
| nkode3 | string | Tertiary industry code |
| sektorkode | number | Sector code |
| konkurs | boolean | In bankruptcy |
| avvikling | boolean | In liquidation |
| tvangsavvikling | boolean | In forced liquidation |
| regiaa | boolean | Registered in Employer Register |
| regifriv | boolean | Registered for voluntary organizations |
| regdato | string | Registration date (ISO 8601) |
| stiftelsesdato | string | Foundation date (ISO 8601) |
| tlf | string | Phone number |
| tlf_mobil | string | Mobile phone |
| url | string | Website URL |
| regnskap | number | Latest accounting year |
| hovedenhet | number | Parent organization number |
| ansatte_antall | number | Number of employees |
| ansatte_dato | string | Employee count date (ISO 8601) |
| Slettet | boolean | Deleted status |
| Hidden | boolean | Hidden status |
#### Example Request
curl "https://apiaccess.digify.no/api/v1/companies/984851006?AccessToken=YOUR_TOKEN"#6. Health Check
Check if the API service is healthy and operational.
Method: GET
Path: /health
Auth: Not required
#### Success Response (200 OK)
{
"status": "healthy",
"timestamp": "2025-10-18T12:00:00.000Z",
"environment": "production",
"database": "connected"
}#### Unhealthy Response (503 Service Unavailable)
{
"status": "unhealthy",
"timestamp": "2025-10-18T12:00:00.000Z",
"environment": "production",
"database": "disconnected",
"error": "Connection timeout"
}#### Example Request
curl "https://apiaccess.digify.no/health"#Error Handling
All errors follow a consistent JSON structure:
{
"status": "error",
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"retryAfter": 60,
"details": {}
}
}#Error Codes
| HTTP Status | Error Code | Description | Retry Strategy |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Invalid request data | Fix request and retry |
| 401 | AUTHENTICATION_FAILED | Invalid/missing AccessToken | Check token and retry |
| 402 | INSUFFICIENT_CREDIT | Not enough SMS credits | Add credits or wait |
| 404 | NOT_FOUND | Endpoint not found | Check URL |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests | Wait retryAfter seconds |
| 500 | DATABASE_ERROR | Database operation failed | Retry with backoff |
| 500 | INTERNAL_SERVER_ERROR | Unexpected error | Retry with backoff |
#Example Error Responses
Validation Error (400)
{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request data",
"details": [
{
"code": "invalid_type",
"expected": "string",
"received": "number",
"path": ["messages", 0, "destAddr"],
"message": "Expected string, received number"
}
]
}
}Authentication Error (401)
{
"status": "error",
"error": {
"code": "AUTHENTICATION_FAILED",
"message": "Invalid AccessToken"
}
}Rate Limit Error (429)
{
"status": "error",
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded for SMS. Limit: 60. Current: 60. Try again in 15 seconds.",
"retryAfter": 15,
"details": {
"messageType": "SMS",
"limit": 60,
"currentCount": 60
}
}
}Note: Rate limit responses include a Retry-After header (in seconds).
Insufficient Credit Error (402)
{
"status": "error",
"error": {
"code": "INSUFFICIENT_CREDIT",
"message": "Insufficient SMS credits. Required: 5, Available: 2",
"details": {
"creditsNeeded": 5,
"creditsAvailable": 2
}
}
}#Rate Limiting
The API enforces rate limits based on your system's environment configuration.
#Default Limits
| Environment | Type | Per Minute | Per Hour | Per Day |
|---|---|---|---|---|
| Development | SMS | 6 | 60 | 500 |
| Development | 60 | 500 | 5,000 | |
| Production | SMS | 60 | 3,000 | 50,000 |
| Production | 300 | 10,000 | 100,000 |
#Handling Rate Limits
When you receive a 429 error:
1. Extract retryAfter from the error response
2. Wait the specified number of seconds
3. Retry the request
4. Implement exponential backoff for repeated 429 responses
Example retry logic:
async function sendWithRetry(request, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await sendRequest(request);
} catch (error) {
if (error.status === 429 && attempt < maxRetries - 1) {
const retryAfter = error.error.retryAfter || 60;
await sleep(retryAfter * 1000);
continue;
}
throw error;
}
}
}#TypeScript Types
Use these TypeScript interfaces when building clients:
// ============================================
// Request Types
// ============================================interface SMSMessage {
type: 'sms';
id?: string;
destAddr: string;
msg: string;
sourceAddr?: string;
sourceAddrTon?: number;
sourceAddrNpi?: number;
esmClass?: number;
protocolId?: number;
priority?: number;
dataCoding?: number;
sendWhen?: string | Date;
scheduleDeliveryTime?: string | Date;
validityPeriod?: string | Date;
}
interface EmailMessage {
type: 'email';
sourceAddr: string;
sourceAddrName?: string;
destAddr: string;
destAddrName?: string;
subject: string;
body: string;
bodyFormat?: 'html' | 'text';
emailProviderId?: number;
}
type Message = SMSMessage | EmailMessage;
interface SendMessagesRequest {
messages: Message[];
}
interface SMSLogsQuery {
limit?: number;
offset?: number;
status?: string;
startDate?: string;
endDate?: string;
}
interface EmailLogsQuery {
limit?: number;
offset?: number;
status?: string;
startDate?: string;
endDate?: string;
}
interface CompanySearchQuery {
search: string;
}
// ============================================
// Response Types
// ============================================
interface SMSResult {
id?: string;
type: 'sms';
status: 'queued' | 'simulated';
messageId: number;
msgCount: number;
}
interface EmailResult {
type: 'email';
status: 'queued' | 'simulated';
emailId: number;
}
type MessageResult = SMSResult | EmailResult;
interface SendMessagesResponse {
status: 'success';
simulationMode: boolean;
results: MessageResult[];
summary: {
total: number;
sms: number;
email: number;
creditsUsed: number;
};
}
interface SMSLog {
messageId: number;
destAddr: string;
msg: string;
msgCount: number;
status: string;
createdAt: string;
sendWhen: string;
sourceAddr: string;
}
interface EmailLog {
emailId: number;
sourceAddr: string;
destAddr: string;
subject: string;
status: string;
createdAt: string;
bodyFormat: string;
}
interface Pagination {
total: number;
limit: number;
offset: number;
hasMore: boolean;
}
interface SMSLogsResponse {
status: 'success';
data: SMSLog[];
pagination: Pagination;
}
interface EmailLogsResponse {
status: 'success';
data: EmailLog[];
pagination: Pagination;
}
interface Company {
orgNr: string;
navn: string;
forrAdrPostSted: string;
}
interface CompanySearchResponse {
status: 'success';
companies: Company[];
}
interface HealthResponse {
status: 'healthy' | 'unhealthy';
timestamp: string;
environment: string;
database: 'connected' | 'disconnected';
error?: string;
}
interface ErrorResponse {
status: 'error';
error: {
code: string;
message: string;
retryAfter?: number;
details?: any;
};
}
#Example Implementations
#JavaScript/TypeScript Client
class MessagingAPIClient {
private baseUrl: string;
private accessToken: string; constructor(accessToken: string, baseUrl = 'https://apiaccess.digify.no/api/v1') {
this.baseUrl = baseUrl;
this.accessToken = accessToken;
}
private async request<T>(
method: string,
path: string,
body?: any,
queryParams?: Record<string, string>
): Promise<T> {
const url = new URL(${this.baseUrl}${path});
url.searchParams.set('AccessToken', this.accessToken);
if (queryParams) {
Object.entries(queryParams).forEach(([key, value]) => {
url.searchParams.set(key, value);
});
}
const response = await fetch(url.toString(), {
method,
headers: {
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
});
const data = await response.json();
if (data.status === 'error') {
throw new APIError(data.error, response.status);
}
return data;
}
async sendMessages(messages: Message[]): Promise<SendMessagesResponse> {
return this.request<SendMessagesResponse>('POST', '/messages/send', { messages });
}
async getSMSLogs(query?: SMSLogsQuery): Promise<SMSLogsResponse> {
return this.request<SMSLogsResponse>('GET', '/logs/sms', undefined, query as any);
}
async getEmailLogs(query?: EmailLogsQuery): Promise<EmailLogsResponse> {
return this.request<EmailLogsResponse>('GET', '/logs/email', undefined, query as any);
}
async searchCompanies(search: string): Promise<CompanySearchResponse> {
return this.request<CompanySearchResponse>('GET', '/companies/search', undefined, { search });
}
async getCompanyByOrgNr(orgNr: string): Promise<CompanySearchResponse> {
return this.request<CompanySearchResponse>('GET', /companies/${orgNr});
}
async healthCheck(): Promise<HealthResponse> {
const url = this.baseUrl.replace('/api/v1', '/health');
const response = await fetch(url);
return response.json();
}
}
class APIError extends Error {
constructor(public error: ErrorResponse['error'], public status: number) {
super(error.message);
this.name = 'APIError';
}
}
// Usage example
const client = new MessagingAPIClient('YOUR_ACCESS_TOKEN');
// Send SMS
await client.sendMessages([
{
type: 'sms',
destAddr: '4712345678',
msg: 'Hello from TypeScript client!',
},
]);
// Search companies
const companies = await client.searchCompanies('DNB');
console.log(companies.companies);
// Get SMS logs
const logs = await client.getSMSLogs({ limit: 10, offset: 0 });
console.log(logs.data);
#Python Client
import requests
from typing import List, Dict, Optional, Union
from datetime import datetimeclass MessagingAPIClient:
def __init__(self, access_token: str, base_url: str = 'https://apiaccess.digify.no/api/v1'):
self.base_url = base_url
self.access_token = access_token
def _request(self, method: str, path: str, json_data=None, params=None) -> Dict:
url = f"{self.base_url}{path}"
# Add AccessToken to params
if params is None:
params = {}
params['AccessToken'] = self.access_token
response = requests.request(
method=method,
url=url,
json=json_data,
params=params,
headers={'Content-Type': 'application/json'}
)
data = response.json()
if data.get('status') == 'error':
raise APIError(data['error'], response.status_code)
return data
def send_messages(self, messages: List[Dict]) -> Dict:
return self._request('POST', '/messages/send', json_data={'messages': messages})
def get_sms_logs(self, limit: int = 50, offset: int = 0, **kwargs) -> Dict:
params = {'limit': limit, 'offset': offset, **kwargs}
return self._request('GET', '/logs/sms', params=params)
def get_email_logs(self, limit: int = 50, offset: int = 0, **kwargs) -> Dict:
params = {'limit': limit, 'offset': offset, **kwargs}
return self._request('GET', '/logs/email', params=params)
def search_companies(self, search: str) -> Dict:
return self._request('GET', '/companies/search', params={'search': search})
def get_company_by_orgnr(self, org_nr: str) -> Dict:
return self._request('GET', f'/companies/{org_nr}')
def health_check(self) -> Dict:
url = self.base_url.replace('/api/v1', '/health')
response = requests.get(url)
return response.json()
class APIError(Exception):
def __init__(self, error: Dict, status_code: int):
self.error = error
self.status_code = status_code
super().__init__(error['message'])
Usage example
client = MessagingAPIClient('YOUR_ACCESS_TOKEN')Send SMS
response = client.send_messages([
{
'type': 'sms',
'destAddr': '4712345678',
'msg': 'Hello from Python client!'
}
])Search companies
companies = client.search_companies('DNB')
print(companies['companies'])Get SMS logs
logs = client.get_sms_logs(limit=10)
print(logs['data'])#C# Client
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;public class MessagingAPIClient
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
private readonly string _accessToken;
public MessagingAPIClient(string accessToken, string baseUrl = "https://apiaccess.digify.no/api/v1")
{
_baseUrl = baseUrl;
_accessToken = accessToken;
_httpClient = new HttpClient();
}
private async Task<T> RequestAsync<T>(HttpMethod method, string path, object body = null, Dictionary<string, string> queryParams = null)
{
var uriBuilder = new UriBuilder($"{_baseUrl}{path}");
var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query);
query["AccessToken"] = _accessToken;
if (queryParams != null)
{
foreach (var param in queryParams)
{
query[param.Key] = param.Value;
}
}
uriBuilder.Query = query.ToString();
var request = new HttpRequestMessage(method, uriBuilder.Uri);
if (body != null)
{
var json = JsonSerializer.Serialize(body);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
}
var response = await _httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
var data = JsonSerializer.Deserialize<T>(content);
return data;
}
public Task<SendMessagesResponse> SendMessagesAsync(List<object> messages)
{
return RequestAsync<SendMessagesResponse>(HttpMethod.Post, "/messages/send", new { messages });
}
public Task<SMSLogsResponse> GetSMSLogsAsync(int limit = 50, int offset = 0)
{
var query = new Dictionary<string, string>
{
["limit"] = limit.ToString(),
["offset"] = offset.ToString()
};
return RequestAsync<SMSLogsResponse>(HttpMethod.Get, "/logs/sms", null, query);
}
public Task<CompanySearchResponse> SearchCompaniesAsync(string search)
{
var query = new Dictionary<string, string> { ["search"] = search };
return RequestAsync<CompanySearchResponse>(HttpMethod.Get, "/companies/search", null, query);
}
public Task<CompanySearchResponse> GetCompanyByOrgNrAsync(string orgNr)
{
return RequestAsync<CompanySearchResponse>(HttpMethod.Get, $"/companies/{orgNr}");
}
}
// Usage example
var client = new MessagingAPIClient("YOUR_ACCESS_TOKEN");
var response = await client.SendMessagesAsync(new List<object>
{
new { type = "sms", destAddr = "4712345678", msg = "Hello from C# client!" }
});
var companies = await client.SearchCompaniesAsync("DNB");
#Best Practices
#1. Error Handling
- Always check the
status field in responses- Implement retry logic for 429 (rate limit) and 500 (server error) responses
- Use exponential backoff for retries
- Log errors with full context for debugging
#2. Rate Limiting
- Respect the
Retry-After header/field in 429 responses- Implement client-side rate limiting to stay under limits
- Batch messages when possible to reduce API calls
#3. Message Batching
- Send multiple messages in a single request when possible
- Maximum 100 messages per request
- Batching reduces overhead and improves throughput
#4. Authentication
- Store AccessToken securely (environment variables, secrets manager)
- Never hard-code tokens in source code
- Rotate tokens periodically
#5. Logging
- Log all API requests and responses (excluding sensitive data)
- Include request IDs for tracing
- Monitor error rates and response times
#6. Testing
- Use simulation mode for development/testing
- Test error handling with invalid data
- Verify retry logic works correctly
#7. Monitoring
- Track API usage and credit consumption
- Set up alerts for rate limit approaching
- Monitor error rates and success rates
#Support
For issues, questions, or feature requests:
- Documentation: https://apiaccess.digify.no/
- Health Status: https://apiaccess.digify.no/health
- Technical Support: Contact your system administrator
Document Version: 1.0
Last Updated: 2025-11-01
API Version: v1