Skip to content

Billing integration

Making an integration with an ERP or financial system is the most common scenario for integrations on the Cloudmore platform. It’s a straightforward process where typically only one API call is required to download the data necessary.

Billing data is generated monthly in Cloudmore but can be generated on different days for different services. For instance, Microsoft NCE billing data is generated between the 6th and 8th of every month when Microsoft makes the data available. Legacy Microsoft service would typically have a different date that varies for each Microsoft partner. For most Cloudmore customers, it is sufficient to download billing data once or twice per month.

Several API calls from Cloudmore will allow you to export billing data to integrate with a third-party system like an ERP. This guide will help you pinpoint the available APIs and recommendations for working with the Cloudmore API.

The complete set of available API calls can be found in the Cloudmore Swagger document: https://api.cloudmore.com/swagger/

Available Billing API calls

API EndpointData returnedNotes
…/billing/reports/MonthlyBillingPerOrganizationAsync– All servicesRecommended API call
NOTE! Not for production!

…/billing/reports/MonthlyBillingPerOrganization
and
…/billing/reports/MonthlyBillingPerService
– All servicesThis API should only be used for testing. This API returns the same data as the asynchronous call but has a limit on the amount of data that can be returned.
…/billing/azurereports/AzureOneTimeInvoice/Microsft data:
– 365 NCE
– Azure NCE
– Perpetual Licenses
– Software Subscriptions
– Marketplace Items
Returns detailed Microsoft information for all services under the New Commerce Experience (NCE).

This data is based on Microsoft’s “Recurring and one-time purchases” invoice.
…/billing/cspreports/MonthlyBillingPerOrganization– Microsoft O365 LegacyDetailed report for Microsoft Legacy services.

This data is based on Microsoft’s “Recurring” invoice.
…/billing/azurereports/InvoiceOverview– Microsoft Azure Legacy billing dataDetailed report for Microsoft Legacy services.

This data is based on Microsoft’s “Recurring” invoice.
…/billing/azureonetimeusage/AzureOneTimeInvoice– Microsoft Azure Usage dataMicrosoft Azure usage data report. Please note that the Microsoft usage report may slightly differ from the Microsoft Azure billing report because of rounding and transformation made by Microsoft.

Recommendation

Cloudmore heavily recommends that you use the “…/billing/reports/MonthlyBillingPerOrganizationAsync” API call to retrieve your billing data. This API call returns billing data for all services in Cloudmore and is the most future-proof call to use. Using this API call will make sure that you don’t have to revisit the billing integration when you move from selling Microsoft services to additional services in Cloudmore.

This guide will focus on how to call the “…/billing/reports/MonthlyBillingPerOrganizationAsync” API call and explain the data returned.

How to call the API

Please see the Authentication documentation to see detailed information on how to authenticate with Cloudmore API.

Once you get an authentication token, you have to send a POST call to start generating the data.

curl -v POST https://api.cloudmore.com/api/resellers/{ResellerId}/billing/reports/MonthlyBillingPerOrganizationAsync \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkMzRERCQ...' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{ 
  "startDate": "2023-01-01", 
  "endDate": "2023-02-01", 
}'

In the response header, look for the “location” property.

< HTTP/1.1 201 Created
< content-length: 0
< date: Mon, 20 Mar 2023 16:48:38 GMT
< server: envoy
< location: https://api.cloudmore.com/api/resellers/{ResellerId}/billing/reports/MonthlyBillingPerOrganizationAsync/2cf155ea-3a06-4765-a3e0-9178ec67263
< request-context: appId=cid-v1:1aebea94-e93f-48c4-9742-65cff419d034
< x-powered-by: ASP.NET
< x-envoy-upstream-service-time: 204

Now send a GET call to the location URL.

curl -X GET https://api.cloudmore.com/api/resellers/{ResellerId}/billing/reports/MonthlyBillingPerOrganizationAsync/2cf155ea-3a06-4765-a3e0-9178ec6b67263 \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkMzRERCQ...' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'

If the data is not ready yet, the response will have a status of 0 (zero) in the Status property

{"status":0,"data":null}%  

Once the report generation is done, the status will be 5 (five), and the data property will be filled with the billing report data.

Response details

The Respons data is structured like this:

  • Billing Date
    • Organization
      • Service
        • Subscription
          • Billing Line
[
  {
    "status": "5",
    "data": [
      {
        "billingDate": "2023-01-19T23:59:59",
        "billingContentStartDate": "2023-01-19T23:59:59",
        "billingContentEndDate": "2023-01-19T00:00:00",
        "organizations": [
          {
            "name": "Contoso Organization",
            "customerNumber": "12345",
            "orgId": "720b40a2-f8d1-4dbb-a115-0193215c3721",
            "organizationGroups": [
              "Large Customers"
            ],
            "services": [
              {
                "serviceName": "Microsoft 365 CSP Direct Legacy",
                "serviceId": "881ae94a-b03a-4da5-8e67-21ea8d77dfb5",
                "totalResellerPrice": "45.00",
                "totalCustomerPrice": "54.00",
                "currencyCode": "EUR",
                "customerCurrencyCode": "EUR",
                "items": [
                  {
                    "itemName": "Microsoft Defender for Office 365 (Plan 2)",
                    "subscriptionId": "9ff279d7-00f3-4277-9d03-ef10b52b8273",
                    "poNumber": "12345689",
                    "quantity": 10.0,
                    "totalResellerPrice": "45.00",
                    "totalCustomerPrice": "54.00",
                    "resellerPricePerUnit": "4.50",
                    "customerPricePerUnit": "5.40",
                    "products": [
                      {
                        "productName": "Microsoft Defender for Office 365 (Plan 2)",
                        "productId": "efe1183a-8fa0-4138-bf0a-5ae271ab6e3c",
                        "itemCode": "EFE1183A-8FA0-4138-BF0A-5AE271AB6E3C",
                        "quantity": 10.0,
                        "resellerPricePerUnit": "4.50",
                        "customerPricePerUnit": "5.40",
                        "totalResellerPrice": "45.00",
                        "totalCustomerPrice": "54.00",
                        "itemDetails": "Cycle fee. Charge for upcoming period from 1/1/2023 to 1/31/2023"
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]

Information about fields

For more details, please see the data model in Swagger.

Field NameDescription
statusA post-call will initiate a report generation, which can take several minutes before the report is ready. The status property will return the status of that report generation. When the report generation is running, a status of 0 (zero) will be returned. Please wait at least 30 seconds and then call the API again. Once completed, the status property will return a status of 5 (five), and the data property will be filled with the data. For additional statuses that could be returned, please see the Swagger document.
billingDateThe date when the billing data was generated. Different services in Cloudmore could have different billing dates. For instance, Microsoft NCE will have a billing date of 6 – 8 of every month (depending on when Microsoft releases its billing). Microsoft Legacy has different dates for different partners. Other services in Cloudmore can be configured to generate billing on a certain date.
billingContentStartDate&
billingContentEndDate
When you generate a billing report, you will specify a start and end date. Cloudmore will return any billing data with a “billingDate” between the start and end date. In the “billingContentStartDate” & “billingContentEndDate” we will return the start and end date that was specified in the POST call.
organizationsThe billing report has an array of all the organizations (end customers).
nameDisplay name of the organization
customerNumberThe customer number of the organization. This field will be filled by the Broker (reseller) and can be used to identify or match a customer with an external system.
orgIdThe unique identifier of the service in Cloudmore. This field cannot be edited and will always remain the same throughout the lifetime of the organization.
organizationGroupsAn array of all the organization groups that the organization belongs to. The organization groups are a powerful tool that can be used to identify cost centers, key account managers, etc.
servicesAn array of all the services in the billing report with a summary of cost and sales for that specific service.
serviceNameThe name of the Service, for instance, “Microsoft 365 CSP Direct Legacy”, “Microsoft 365 CSP Direct”, “Microsoft Azure CSP Direct”.
serviceIdThe unique identifier of the service in Cloudmore. This field cannot be edited and will always remain the same throughout the lifetime of the service.
totalResellerPriceA summary of the total cost of the service for that organization.
totalCustomerPriceA summary of the total sales of the service for that organization. Subtracting the “totalResellerPrice” from the “totalCustomerPrice” will provide the margin of the service.
currencyCode & customerCurrencyCodeThese fields will present the currency code that prices are based on. The currencies are presented as 3-letter ISO 4217 (https://www.xe.com/iso4217.php ). The “currencyCode” is the currency the Broker buys the service in. The “customerCurrencyCode” is the currency of the Organization that buys the service in. Typically the “currencyCode” and the “customerCurrencyCode” is the same. Currently (Mar 2023), only custom service supports different currencies between Broker and Organization. For all Microsoft services, the currencies will be the same.
itemsThe array that contains the list of subscriptions.
itemNameThe name of the subscription.
subscriptionIdThe unique identifier of the subscription in Cloudmore. This field cannot be edited and will always remain the same throughout the lifetime of the service.
poNumberThe Purchase Order Number associated with the subscription. This field is an optional field that the Broker can fill.
quantityThe number of licenses/seats for the billing line.
productsThe array that contains the actual billing lines. If no changes have been made to a subscription, this typically will only return one line. If changes have been made or there are several products under the subscription, multiple items (billing lines) will be returned.
productNameThe name of the product in the subscription.
productIdThe unique identifier of the product in Cloudmore. This field cannot be edited and will always remain the same throughout the lifetime of the product.
itemCodeThe item code that is provided by the vendor.
itemDetailsDetails about the billing line.

Python example

This is a short example of a python code to retrieve an access token and get the billing report data. This example code without error handling should not be used for production.

import requests
import json
import base64
import time
# Define constants
RESELLER_ID = '11111111-2222-3333-4444-555555555555' # Replace with you Broker ID
CLIENT_ID = 'ro.customer.client'
API_SECRET = 'MySecret' # Replace with your API Secret
USERNAME = 'username@broker.com' # Replace with your username
PASSWORD = 'MyPassword' # Replace with your password
START_DATE = '2023-01-01' # Replace with the start date you want
END_DATE = '2023-01-31' # Replace with the end date you want
SAVE_TO_FILE = f'BillingReport_{START_DATE}_{END_DATE}_json.txt'
# Base64 encode the API secret
client_decoded = f'{CLIENT_ID}:{API_SECRET}'
b64_secret = base64.b64encode(client_decoded.encode()).decode()
# Get Access Token
token_url = 'https://api.cloudmore.com/connect/token'
token_headers = {'Authorization': f'Basic {b64_secret}'}
token_data = {
    'client_id': 'ro.customer.client',
    'grant_type': 'password',
    'username': USERNAME,
    'password': PASSWORD,
    'scope': 'api'
}
token_response = requests.post(token_url, headers=token_headers, data=token_data)
token = token_response.json().get("access_token")
print('Access Token received:', token)
# Start generating Billing Report
billing_url = f'https://api.cloudmore.com/api/resellers/{RESELLER_ID}/billing/reports/MonthlyBillingPerOrganizationAsync'
billing_headers = {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Authorization': f'Bearer {token}'
}
billing_data = json.dumps({'startDate': START_DATE, 'endDate': END_DATE})
billing_response = requests.post(billing_url, headers=billing_headers, data=billing_data)
location = billing_response.headers['location']
print('Billing Report generation started')
print('Endpoint to retrieve billing report:', location)
# Get billing report (try until status 5)
def get_billing_report():
    while True:
        response = requests.get(location, headers=billing_headers)
        report_status = response.json()['status']
        if report_status != 5:
            print('Billing Report not ready... waiting 5 seconds.')
            time.sleep(5)
        else:
            print('Billing Report Done!')
            return response
billing_response = get_billing_report()
# Write JSON to file
with open(SAVE_TO_FILE, 'w') as file:
    json.dump(billing_response.json(), file)