GHG Audit Export¶
Table of contents
Overview
Setup
Authentication Token
Trigger Audit Export
Output Description
Get Audit Export Status
Output Description
Download Audit Export
Related Links
Overview¶
This notebook demonstrates how to use the GHG Audit Export APIs to retrieve a full audit record of API activity for your organization for a given date range.
The export is generated asynchronously. The workflow is:
Trigger — submit an export request with a date range using
POST /audit.Poll — check the status of the request using
GET /audit/statusuntil the status isCOMPLETEDorFAILED.Download — retrieve the ZIP archive using
GET /audit/downloadonce the status isCOMPLETED.
All endpoints require an admin bearer token. If the authenticated user is not an admin, the service returns 403 Forbidden.
Setup¶
Ensure that Python 3+ is installed on your system.
Note: To run this notebook, add your credentials to ../../../auth/secrets.ini and ../../../auth/config.ini.
Example secrets.ini format:
[EAPI]
api.pat_token = <Your Emissions API PAT token>
api.tenant_id = <Your Emissions API Tenant Id>
[ ]:
# Install the prerequisite Python packages
%pip install pandas configparser IPython requests
[ ]:
import pandas as pd
import configparser
import requests
import json
import time
from IPython.display import display as display_summary
Authentication Token¶
Run the following code snippet to generate a Bearer Token using your PAT token. These admin endpoints require an authenticated admin user.
[ ]:
config = configparser.RawConfigParser()
config.read(['../../../auth/secrets.ini','../../../auth/config.ini'])
EAPI_PAT_TOKEN = config.get('EAPI', 'api.pat_token')
EAPI_TENANT_ID = config.get('EAPI', 'api.tenant_id')
EAPI_AUTH_ENDPOINT = config.get('EAPI', 'api.auth_endpoint')
EAPI_BASE_URL = config.get('EAPI', 'api.base_url')
EAPI_AUTH_CLIENT_ID = 'saascore-' + EAPI_TENANT_ID
EAPI_CLIENT_ID = 'ghgemissions-' + EAPI_TENANT_ID
auth_request_headers: dict = {}
auth_request_headers["X-IBM-Client-Id"] = EAPI_AUTH_CLIENT_ID
auth_request_headers["X-IBM-Envizi-Pat"] = EAPI_PAT_TOKEN
verify = True
auth_url = f"{EAPI_AUTH_ENDPOINT}"
response = requests.post(url = auth_url,
headers = auth_request_headers,
verify = verify
)
if response.status_code == 200:
jwt_token = response.text
print("Authentication Success")
else:
print("Authentication Failed")
print(response.text)
Authentication Success
Trigger Audit Export¶
Use POST /audit to queue an audit export job for a given date range.
The request body accepts the following parameters:
fromDate(required): Start date of the audit period (inclusive), inYYYY-MM-DDformat.toDate(required): End date of the audit period (inclusive), inYYYY-MM-DDformat.apiName(optional): Filter the export to a specific API endpoint — partial match on endpoint path.
The endpoint returns:
202 Acceptedfor a fresh request, or a retry after a previous failure.409 Conflictif an identical request is already in progress or has already completed.
[ ]:
AUDIT_ENDPOINT = f"{EAPI_BASE_URL}/audit"
payload = {
'fromDate': '2025-01-01',
'toDate': '2025-03-31'
# Optional: uncomment to filter by a specific API endpoint
# 'apiName': '/location'
}
request_headers: dict = {}
request_headers['Content-Type'] = 'application/json'
request_headers['x-ibm-client-id'] = EAPI_CLIENT_ID
request_headers['Authorization'] = 'Bearer ' + jwt_token
response = requests.post(
AUDIT_ENDPOINT,
headers=request_headers,
data=json.dumps(payload)
)
print(f'Status Code: {response.status_code}')
if response.status_code == 202:
response_json = response.json()
request_id = response_json.get('requestId')
print(f'requestId: {request_id}')
display_summary(pd.json_normalize(response_json))
elif response.status_code == 409:
print('Conflict: an identical request is already in progress or completed.')
print(response.text)
else:
print(response.text)
Status Code: 202
requestId: tenant-uuid/20250101/20250331
Output Description¶
requestId - Unique identifier for the export request. Pass this to the status and download endpoints.
status - Current processing status. On acceptance this will be QUEUED.
message - Human-readable result message.
submittedAt - ISO-8601 timestamp indicating when the request was submitted.
links - Hypermedia links for the status check and download endpoints.
Possible HTTP status codes:
202- Request accepted — fresh request or retry after a previous failure400- Bad request — missing or invalid parameters403- Forbidden — admin token required409- Conflict — request is already in progress or already completed500- Internal server error
Get Audit Export Status¶
Use GET /audit/status to check the current processing status of an audit export request.
Pass the requestId returned by POST /audit as a query parameter. The endpoint always returns HTTP 200 for a valid requestId; the status field in the response body indicates the actual job outcome.
The tenant extracted from the token must match the tenant embedded in the requestId.
Possible status values:
Status |
Description |
|---|---|
|
Request accepted and waiting to be processed |
|
Report is currently being generated |
|
Report is ready for download |
|
Report generation failed — see |
[ ]:
AUDIT_STATUS_ENDPOINT = f"{EAPI_BASE_URL}/audit/status"
request_headers: dict = {}
request_headers['Content-Type'] = 'application/json'
request_headers['x-ibm-client-id'] = EAPI_CLIENT_ID
request_headers['Authorization'] = 'Bearer ' + jwt_token
print('Polling status...')
while True:
response = requests.get(
AUDIT_STATUS_ENDPOINT,
headers=request_headers,
params={'requestId': request_id}
)
if response.status_code != 200:
print(f'Unexpected status code: {response.status_code}')
print(response.text)
break
status_json = response.json()
current_status = status_json.get('status')
print(f'Status: {current_status}')
if current_status == 'COMPLETED':
print('Export is ready for download.')
display_summary(pd.json_normalize(status_json))
break
elif current_status == 'FAILED':
print(f'Export failed: {status_json.get("message")}')
break
else:
# QUEUED or IN_PROGRESS — wait before polling again
time.sleep(10)
Polling status...
Status: QUEUED
Status: IN_PROGRESS
Status: COMPLETED
Export is ready for download.
Output Description¶
requestId - The unique identifier of the export request.
status - Current job status: QUEUED, IN_PROGRESS, COMPLETED, or FAILED.
submittedAt - ISO-8601 timestamp when the request was submitted. Present for QUEUED, IN_PROGRESS, and FAILED states.
completedAt - ISO-8601 timestamp when the export completed. Present only when status is COMPLETED.
message - Human-readable detail. Present when status is FAILED — describes why the export could not be generated.
links.download - Download URL. Present only when status is COMPLETED.
Possible HTTP status codes:
200- Status returned — always HTTP 200 for a validrequestId400- Invalid or missingrequestId, orrequestIdbelongs to a different tenant403- Forbidden — admin token required
Download Audit Export¶
Use GET /audit/download to download the completed audit export. This endpoint is only available when the export status is COMPLETED.
The response is a ZIP archive (application/octet-stream) containing a single CSV file named audit-export.csv.
The tenant extracted from the token must match the tenant embedded in the requestId.
[ ]:
AUDIT_DOWNLOAD_ENDPOINT = f"{EAPI_BASE_URL}/audit/download"
request_headers: dict = {}
request_headers['x-ibm-client-id'] = EAPI_CLIENT_ID
request_headers['Authorization'] = 'Bearer ' + jwt_token
response = requests.get(
AUDIT_DOWNLOAD_ENDPOINT,
headers=request_headers,
params={'requestId': request_id}
)
print(f'Status Code: {response.status_code}')
if response.status_code == 200:
output_file = 'audit-export.zip'
with open(output_file, 'wb') as f:
f.write(response.content)
print(f'Export saved to: {output_file}')
elif response.status_code == 409:
print('Report is still processing or generation failed. Check status before downloading.')
else:
print(response.text)
Status Code: 200
Export saved to: audit-export.zip
The downloaded ZIP archive contains a single file: audit-export.csv.
Possible HTTP status codes:
200- ZIP file returned successfully400- InvalidrequestIdfor this tenant403- Forbidden — admin token required409- Report still processing, or generation failed