Organization and tenant management API for resellers
The Admin API is only available to resellers with an organization API key. This is a privileged API for managing multiple customer tenants. If you’re looking for standard invoice operations, see the regular API documentation.
The Admin API enables resellers to programmatically manage customer organizations (tenants), provision API credentials, and handle Peppol network registration on behalf of customers.This is separate from the standard e-invoice.be API and uses different authentication.
You are responsible for determining and setting the correct Peppol ID for each tenant. Although peppol_ids is an array, the platform currently only supports a single Peppol ID per tenant. Setting the Peppol ID at tenant creation is important for proper Peppol registration later.
The tenant schema now separates company registration identifiers:
company_number - The official company registration number from the national business register:
Belgium: CBE/KBO number (e.g., 0123456789)
Netherlands: KvK number (Chamber of Commerce)
Luxembourg: RCS number (Registre de Commerce et des Sociétés)
Other countries: Equivalent business registration number
company_tax_id - The actual VAT or tax identification number:
Format includes country prefix (e.g., BE0123456789, NL123456789B01)
Used for tax purposes and invoicing
May be different from the company number in some jurisdictions
For Belgian companies, the CBE number (without “BE” prefix) is typically used in the Peppol ID as 0208:<CBE number>, while the full VAT number (with “BE” prefix) goes in company_tax_id.
Registering on the SMP (Service Metadata Publisher) - Maps the Peppol ID to e-invoice.be’s access point
Writing to the Peppol Directory - Creates a searchable entry for participant lookup
Routing documents - Ensures incoming invoices reach the correct tenant
Every tenant must have exactly one associated Peppol ID. Setting it during tenant creation ensures the tenant is properly configured before Peppol registration.
Use a consistent naming convention for tenant names, such as customer-slug or company-id. This makes it easier to manage multiple customers.
curl -X POST "https://api.e-invoice.be/api/admin/tenants/ten-9k2m4p7q3w5x8r/api-keys" \ -H "Authorization: Bearer YOUR_ORGANIZATION_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "production-key", "description": "Production API key for Customer Company" }'
Request Body:
name (required) - Identifier for the API key
description (optional) - Purpose or environment
Response:
{ "id": "api-3h8f5j2k9l4m7n6p1q5r8s2t4v6w9x3y", "tenant_id": "ten-9k2m4p7q3w5x8r", "name": "production-key", "description": "Production API key for Customer Company", "created_at": "2026-04-16T10:00:00.000000Z", "updated_at": "2026-04-16T10:00:00.000000Z", "is_deleted": false}
The id field IS the API key. There is no separate key field — the value returned in id (e.g. api-3h8f5j2k9l4m7n6p1q5r8s2t4v6w9x3y) is the bearer token your customer must include in the Authorization header of every API request:
Treat the id as a secret. Capture it on creation, store it securely (e.g. encrypted at rest), and deliver it to your customer over a secure channel. Never log it, commit it to version control, or expose it in client-side code.
Because the id is itself the bearer token, listing API keys returns the live credentials for this tenant. Treat the response as sensitive credential material: restrict who can call this endpoint, avoid logging the response body, and never return it to end users.
async function rotateApiKey(tenantId, oldKeyId) { try { // 1. Create new API key console.log('Creating new API key...'); const newKey = await adminApi.post( `/api/admin/tenants/${tenantId}/api-keys`, { name: 'production-key-v2', description: 'Rotated production key' } ); // The id IS the bearer token — capture it now; it cannot be retrieved later. const newApiKey = newKey.data.id; console.log('✓ New key created:', newApiKey); // 2. Provide new key to customer // (Send via secure channel, update their configuration) // 3. Wait for customer to switch over console.log('Waiting for customer to update configuration...'); await new Promise(resolve => setTimeout(resolve, 3600000)); // 1 hour // 4. Revoke old key console.log('Revoking old API key...'); await adminApi.delete( `/api/admin/tenants/${tenantId}/api-keys/${oldKeyId}` ); console.log('✓ Old key revoked'); return newKey.data; } catch (error) { console.error('Key rotation failed:', error.response?.data || error.message); throw error; }}
async function auditedTenantCreate(tenantData) { const result = await adminApi.post('/api/admin/tenants', tenantData); await auditLog.create({ action: 'TENANT_CREATE', tenant_id: result.data.id, admin_user: currentUser.id, timestamp: new Date(), details: { name: tenantData.name } }); return result.data;}
Peppol Registration Checks
Always verify registration prerequisites:
async function registerOnPeppol(tenantId, peppolId) { // 1. Check tenant is not in test mode const tenant = await adminApi.get(`/api/admin/tenants/${tenantId}`); if (tenant.data.test_mode) { throw new Error('Cannot register test-mode tenant on Peppol'); } // 2. Verify Peppol ID format if (!/^\d{4}:\d+$/.test(peppolId)) { throw new Error('Invalid Peppol ID format'); } // 3. Register return await adminApi.post( `/api/admin/tenants/${tenantId}/peppol/register`, { peppol_id: peppolId } );}