Billing data - M8com webhook

Automate invoicing with M8com's webhook

7 min read

With the M8com Billing Data webhook it is possible for a wholesale partner to automatically receive billing records for all their customer organizations. M8com posts individual billing records (one per product per organization) to the partner's configured webhook endpoint at the end of each billing period.

There are two main features:

  1. Billing data posting (monthly or quarterly)
  1. Refund posting ( optional )

When the webhook is configured and activated for a wholesale partner, M8com will automatically post billing data when:

  • a billing period ends and the billing sync is triggered
  • a manual billing data post is initiated by an administrator
  • a retry is triggered for previously failed records

If the refund feature is enabled, M8com will post refund requests to a separate refund URL when:

  • a refund is initiated for a previously billed record

How it works

Overview - Billing Data Posting

When a billing period ends (monthly or quarterly), the billing sync process triggers the billing data webhook for each configured wholesale partner. M8com collects billing report data for all customer organizations under the partner, calculates amounts from the partner's pricebook (including 25% Swedish VAT), and posts one HTTP request per product per organization to the partner's webhook endpoint.

Flow of events:

  1. Billing sync triggers postPartnerBillingData for the partner
  1. M8com retrieves the partner's active billing webhook configuration
  1. M8com calculates the billing period dates (the previous month or quarter)
  1. For each customer organization, M8com fetches the billing report
  1. For each product with quantity > 0, a billing record is constructed
  1. Records are validated (customer billing ID must exist) and sent in batches of 50
  1. Each request/response is logged in billingWebhookLog for auditing and retry support

Overview - Retry Logic

If any records fail during a billing run (eg due to missing billing IDs or network errors), M8com stores them and will automatically retry failed records on the next run for the same period. Records that succeeded are not re-sent. New organizations added since the previous run are also included.

Webhook Setup for partners

The billing data webhook is configured per wholesale partner in the partner administration settings. The setup requires the manageBilling permission and is handled by Partner administration.

Connecting a new webhook

When connecting a new billing data webhook, the following must be configured:


Setting Required Description
Webhook URL Yes The HTTPS endpoint where billing data will be posted
Authentication Method Yes Either OAuth2 or Custom Auth
Partner Customer Billing ID Field Yes The JSON field name used for the customer billing ID in each request (eg customerId)
Billing Data Period Yes Monthly or Quarterly
Include Partner Organization No Whether to include the partner's own organization in billing
Partner Organization Billing ID Conditional Required if "Include Partner Organization" is enabled

Authentication Methods

OAuth2 supports two grant types:

Setting Required Description
Grant Type Yes Client Credentials or Authorization Code
Client ID Yes OAuth2 client identifier
Client Secret Yes OAuth2 client secret
Token URL Yes OAuth2 token endpoint URL
Authorization Base URL Conditional Required for Authorization Code grant type
Scope No OAuth2 scope (Authorization Code only)
Refresh Token Timeout No How long before the refresh token expires (Authorization Code only)
Authorization Parameters No Additional parameters for the authorization request

Custom Auth allows authentication via custom parameters in:

Location Description
Headers Custom key-value pairs sent as HTTP headers
Bodysuit Custom key-value pairs merged into the request body
Query String Custom key-value pairs appended to the URL

At least one custom auth parameter (in any location) must be configured.

Additional Configuration

Setting Required Description
Additional Static Parameters No Extra key-value pairs included in every request body (eg partner-specific identifiers)
Refund Enabled No Enable refund posting to a separate endpoint
Refund URL Conditional Required if refunds are enabled
Refund Reference ID Field Conditional Field name in the billing response to use as reference ID for refunds
Refund Static Parameters No Extra key-value pairs included in every refund request

Implementation Notes

This specification is for the implementer of a billing data receiver, and describes the request format that must be supported by the receiving endpoint.

Notes

  • It's the responsibility of the webhook receiver to be available and handle requests correctly.
  • M8com sends billing data as individual HTTP POST requests - one request per product per organization.
  • Requests are sent in batches of up to 50 concurrent requests .
  • Each request has a 20 second timeout .
  • All requests use Content-Type: application/json and Accept: application/json.
  • Authentication (OAuth2 token or custom auth) is handled automatically by M8com before each batch.

Adding is not breaking

  • New fields may be introduced to the JSON request body over time and is not considered a breaking change. Integrators must ensure that their integrations ignore unknown fields by default.

Billing Data Posting

REQUEST

M8com will send an HTTP POST request to the configured webhook URL for each billable product per organization. The request body is in Content-Type: application/json format.

Request body

Field Type Optional Description
[partnerCustomerBillingIdField]thong No Dynamic field name • the field name is configured per webhook (eg customerId). The value is the organization's partner customer billing ID.
productthong No The product type identifier (eg pbx-unlimited, sms-package, phone-number).
amountnumber No Price in the smallest currency unit (ore) including 25% VAT . For example, 15000 means SEK 150.00.
vatnumber No VAT percentage expressed as an integer. Always 2500 (representing 25.00%).
currencythong No Currency code. Always SEK.
clientTransactionIdthong No A unique UUID v4 identifier for this specific billing record. Can be used for idempotency checks.
invoiceTextthong No Human-readable name for the product, suitable for display on an invoice. Falls back to the product type if no display name is configured.
quantitynumber No The number of licenses. For package-type products, this is the end-of-period quantity. For all other products, this is the peak quantity during the period.
articleIdthong No Article identifier, same as the product field.
additional static params varies Yes Any additional key-value pairs configured in the webhook's "Additional Static Parameters" are spread into the request body at the top level.

Amount Calculation

The amount field is calculated as:

Where:

amount = floor(recurringPrice * 100 * 1.25)
  • recurringPrice is the price from the partner's pricebook (in SEK)
  • * 100 converts to ore (smallest currency unit)
  • * 1.25 adds 25% Swedish VAT
  • floor() rounds down to the nearest integer

Quantity Selection

Product Type Quantity Used
Package products (eg pbx-package, package-unlimited) periodEndQuantity • the quantity at the end of the billing period
All other products periodPeakQuantity • the highest quantity observed during the billing period

Example Request

In this example:

POST https://partner-billing-system.example.com/api/billing
Content-Type: application/json
Authorization: Bearer <oauth2-access-token>

{
  "customerId": "ORG-12345",
  "product": "pbx-unlimited",
  "amount": 49625,
  "vat": 2500,
  "currency": "SEK",
  "clientTransactionId": "550e8400-e29b-41d4-a716-446655440000",
  "invoiceText": "PBX Unlimited",
  "quantity": 5,
  "articleId": "pbx-unlimited"
}
  • customerId is the dynamic field name configured as the partnerCustomerBillingIdField
  • amount of 49625 ore = 496.25 SEK (including 25% VAT)
  • quantity of 5 means 5 licenses of this product

Example with Additional Static Parameters

If the webhook is configured with additional static parameters like { "partnerId": "ACME-001", "region": "SE" }:

RESPONSE

M8com expects an HTTP 200 response. The response body is logged for auditing purposes. M8com analyzes the response body for error indicators even on HTTP 200 responses:

{
  "partnerId": "ACME-001",
  "region": "SE",
  "customerId": "ORG-12345",
  "product": "pbx-unlimited",
  "amount": 49625,
  "vat": 2500,
  "currency": "SEK",
  "clientTransactionId": "550e8400-e29b-41d4-a716-446655440000",
  "invoiceText": "PBX Unlimited",
  "quantity": 5,
  "articleId": "pbx-unlimited"
}
Error Indicator Description
statusIndicator not equal to "0" Error indicator
error: trueGeneric error flag
success: falseGeneric success flag
status: "error" or status: "failed" Status field error values
errorCode or error_code present Error code fields

If any of these indicators are found, the record is marked as failed and can be retried.

Billing Period Calculation

The billingMonth parameter uses the format YYYY-MM and represents the month after the billing period has ended.

Monthly


billingMonth Period Start Period End
2025-022025-01-01 2025-01-31
2025-032025-02-01 2025-02-28
2025-072025-06-01 2025-06-30

Quarterly

For quarterly billing, billingMonth must be the month after a quarter ends (01, 04, 07, or 10).

billingMonth Period Start Period End Quarter
2025-012024-10-01 2024-12-31 Q4 2024
2025-042025-01-01 2025-03-31 Q1 2025
2025-072025-04-01 2025-06-30 Q2 2025
2025-102025-07-01 2025-09-30 Q3 2025

Refund Posting

Overview

When refund posting is enabled, M8com can send refund requests to a separate configured URL. Refunds reference a previous billing record using a field from the original billing response.

Refund Configuration

Setting Description
Refund URL The HTTPS endpoint where refund requests are posted
Refund Reference ID Field The field name in the original billing response's responseData to use as the reference ID
Refund Static Parameters Additional key-value pairs included in every refund request body

Details & limitations

  • All amounts are in SEK (Swedish Krona) and expressed in ore (1/100 SEK).
  • VAT is always 25% (Swedish VAT) and is included in the amount field.
  • A webhook request times out after 20 seconds .
  • Records are sent in batches of 50 .
  • Each billing record gets a unique clientTransactionId (UUID v4) for idempotency.
  • Products with zero quantity are not billed.
  • Products included via another product (eg included in a package) are excluded to avoid double-billing.
  • The partnerCustomerBillingIdField is a dynamic field name - the actual JSON key depends on the webhook configuration.
  • If a customer organization is missing a partnerCustomerBillingId, the record is logged as failed and can be retrieved after the ID is configured.
  • For the partner's own organization, the billing ID comes from the webhook configuration (partnerOrganizationBillingId) rather than the organization record.
  • All requests and responses are logged in billingWebhookLog for auditing, debugging, and retry support.
  • If all records for a run succeed, the run is marked as completed and will not be re-sent.
  • If a run has already been completed for a given period, subsequent triggers return immediately without re-sending.
Did this answer your question?