Skip to main content

How to Integrate Quota Management

Purpose

This guide is intended to help other teams integrate their services with the standardized Quota Management System. It covers processes such as quota checking, deduction, refund, and usage logging.


Core Components

ComponentDescription
Check BalanceAPI endpoint to verify the current quota before deduction
DeductAPI endpoint to reduce quota after usage is confirmed

Integration Steps

1. Identify the Billing Component

Before integration, the engineer must obtain a billing code from the product team for the feature that will be connected to quota management. This code must be included in every request to the quota management system.


2. Authentication (SSO Token)

Before calling any Quota Management API, you must obtain an SSO access token.

This integration supports two types of tokens:

User Token Used when the product already has an existing user access token that is used to access the main application pages. This same token can be reused to call the Quota Management API.

Service-to-Service Token Used as an alternative when the product does not have, or does not manage, a user token. In this case, the token is obtained using Service-to-Service authentication.

Endpoint:

POST {{base_sso_url}}/auth/oauth2/token
Environmentbase_sso_url
Stagingapi.mekari.io
Productionapi.mekari.com

Client Credentials

⚠️ {{client_id}} and {{client_secret}} must be requested via DM from the Bifrost team. Do not share credentials in public channels or repositories.

Sample Request:

{
"grant_type": "client_credentials",
"scope": "sso:profile",
"client_id": "{{client_id}}",
"client_secret": "{{client_secret}}"
}

Sample Response:

{
"expires_in": 3600,
"token_type": "bearer",
"access_token": "uveNyBkn36rDEEpBLfnGsZoOs9gx4PI1"
}

3. Check Quota Balance

Before deducting quota, you must call the check quota API to ensure quota availability.

Endpoint:

POST {{base_sso_url}}/internal/qontak/billing/v1/quota-managements/check-quota

Authentication: IAG (Service to Service)

Sample Request

{
"billing_code": "EmailBroadcast",
"company_id": "154982",
"extra_attrs": {
"expectation_deduction": {
"en": 1,
"other": 1
}
},
"is_scheduled": true
}

Request Parameters

FieldTypeRequiredDescription
billing_codestringYesThe billing code provided by the product team
company_idstringYesThe ID of the company whose quota will be checked
expectation_deductionobjectYesThe expected quota usage, by category (e.g., per country or other grouping). At least one field is required.
is_scheduledbooleanNoSet to true if the usage is scheduled (e.g., a broadcast job). Default: false

Sample Response (Success) — Limited Quota

{
"billing_code": "EmailBroadcast",
"company_id": "154982",
"extra_attrs": {
"estimation_quota": {
"total_estimation_balance_quota": 200,
"total_estimation_credit_quota": 2
},
"expectation_deduction": {
"en": 1,
"other": 1
},
"is_sufficient": true,
"is_unlimited": false,
"quota_info": {
"total_remaining_balance_quota": 100,
"total_remaining_credit_quota": 1
},
"used_quota": {
"total_used_balance_quota": 100,
"total_used_credit_quota": 1
}
},
"is_scheduled": true
}

Sample Response (Success) — Unlimited Quota

When the component is configured as unlimited (is_unlimited: true), the system short-circuits immediately and always returns is_sufficient: true without evaluating balances or calling price APIs. estimation_quota and used_quota will be zero.

{
"billing_code": "EmailBroadcast",
"company_id": "154982",
"extra_attrs": {
"estimation_quota": {
"total_estimation_balance_quota": 0,
"total_estimation_credit_quota": 0
},
"expectation_deduction": {
"en": 1,
"other": 1
},
"is_sufficient": true,
"is_unlimited": true,
"quota_info": {
"total_remaining_balance_quota": 0,
"total_remaining_credit_quota": 0
},
"used_quota": {
"total_used_balance_quota": 0,
"total_used_credit_quota": 0
}
},
"is_scheduled": false
}

Response Fields Explanation

is_sufficient

A boolean flag that indicates whether the current available quota is sufficient to fulfill the expected deduction.

  • true: Quota is sufficient, you can proceed with deduction
  • false: Quota is insufficient, deduction should not proceed

When is_unlimited is true, is_sufficient is always true regardless of remaining balances.

is_unlimited

A boolean flag indicating whether the billing component is configured as unlimited for this organization.

  • true: The component has unlimited quota. Deduction will be recorded but no actual quota is decremented.
  • false: The component has a finite quota pool subject to normal balance checking.

estimation_quota

Provides an estimation of how much quota will be consumed for the current request, based on the expected deduction provided in the request.

FieldDescription
total_estimation_balance_quotaEstimated balance quota that would be consumed, if charged entirely from balance. Example: if 1 credit = 100 balance units, and you're deducting 2 credits, this might show 200.
total_estimation_credit_quotaEstimated credit quota that would be consumed. Typically matches the number of expected deductions if charged entirely by credit.

Note: The quota system may charge partially from balance and partially from credit depending on availability. This field helps anticipate potential usage. Both values are 0 when is_unlimited=true.

quota_info

Displays the company's current available quota, separated into balance and credit.

FieldDescription
total_remaining_balance_quotaThe total number of balance units currently available. These are generally smaller units used after converting from credit (e.g., 1 credit = 100 balance units).
total_remaining_credit_quotaThe total number of credits remaining. Credits are higher-level quota units and are typically used first unless otherwise specified.

used_quota

Shows how the quota will be deducted based on the current quota availability. If credit is available, it may be used first. Any remaining deduction may be fulfilled using balance.

FieldDescription
total_used_credit_quotaThe amount of credit quota that will be used. 0 when is_unlimited=true.
total_used_balance_quotaThe amount of balance quota that will be used. 0 when is_unlimited=true.

Possible Error Responses

HTTP Statusresp_codeError DescriptionCauseAction Required
404"404"Component not foundbilling_code not found or not registered in the systemVerify billing_code with product/billing team
404"404"Organization package not foundCompany doesn't have any active packageContact billing team to activate package for this company
422"422"Feature is not activeThe billing component exists but is not active for this companyContact billing team to activate the feature
422"422"Package component is not activeThe package component is not active for this companyContact billing team to activate the package component
500"500"Internal errorDatabase error or system malfunctionRetry the request. If persists, contact Bifrost team

Sample Error Response

{
"resp_code": "422",
"resp_desc": {
"id": "feature is not active",
"en": "feature is not active"
},
"meta": {
"version": "",
"api_env": ""
}
}

4. Deduct Quota

Once quota is confirmed to be sufficient, you can proceed to deduct it.

Endpoint:

POST {{base_sso_url}}/internal/qontak/billing/v1/quota-managements/deduction

Authentication: IAG (Service to Service)

Sample Request

{
"billing_code": "EmailBroadcast",
"company_id": "154982",
"deduction_code": "id",
"quantity": 1,
"extra_attrs": {
"account_uniq_id": "string",
"agent_id": "string",
"agent_name": "string",
"broadcast_name": "string",
"channel_id": "string",
"channel_integration_id": "string",
"channel_name": "string",
"completion_at": "string",
"completion_id": "string",
"conversation_category": "string",
"conversation_id": "string",
"country": "string",
"customer_name": "string",
"customer_uniq_id": "string",
"message_broadcast_id": "string",
"origin_type": "string",
"prompt_at": "string",
"prompt_id": "string",
"recipient": "string",
"room_id": "string",
"template_name": "string"
},
"free_reason": "string",
"is_free": true,
"unique_code": "string"
}

Request Parameters

FieldTypeRequiredDescription
billing_codestringYesThe billing code provided by the product team
company_idstringYesThe ID of the company whose quota will be deducted
deduction_codestringYesMust match what was agreed upon with the billing and service teams (e.g., country code)
quantityfloatNoNumber of units to deduct (min: 0.01). Defaults to 1.0 when omitted.
extra_attrsobjectYesRequired metadata for billing/reporting (e.g., broadcast template name, agent name, room_id)
is_freebooleanNoSet to true if this deduction is agreed to be free. Default: false
free_reasonstringNoRequired if is_free is true. Reason why this deduction is free
unique_codestringNoRequired if the deduction must be uniquely tracked within a certain period (idempotency key)

Sample Response (Success)

{
"billing_code": "EmailBroadcast",
"company_id": "154982",
"credited_to": "initial",
"deduction_code": "id",
"extra_attrs": {
"recipient": "user@example.com",
"broadcast_name": "Monthly Newsletter",
"template_name": "newsletter_template"
},
"free_reason": "",
"is_free": false,
"unique_code": "broadcast-123-msg-456",
"value_before": 1000,
"value_after": 999
}

Response Fields

FieldTypeDescription
credited_tostringIndicates the result of the deduction. See values below.
value_beforefloatRemaining quota value before deduction
value_afterfloatRemaining quota value after deduction
extra_attrsobjectEcho of the extra_attrs sent in the request

credited_to possible values:

ValueMeaning
"initial" / "additional" / "postpaid"Normal deduction from the respective quota type (actual component code is returned)
"free"Deduction was marked as free (is_free=true), no quota was consumed
"already-deducted"unique_code was already used — idempotency hit, no duplicate deduction performed
"retry-deduction"Optimistic locking conflict detected. The deduction job has been automatically requeued as a background job. No action needed from the caller — it will be processed shortly.

Unlimited Quota Behavior: When the component has is_unlimited=true, the deduction is still recorded in the billing log (for audit purposes), but no actual quota is decrementedvalue_before and value_after will be equal, and all price/margin values are set to 0. credited_to is still populated based on the first available quota type.

Post-deduction Alert: After a successful deduction, the system automatically checks if the remaining quota has dropped below the component's threshold_running_out percentage (e.g., 40%). If so, a quota alert notification is triggered asynchronously. Your service does not need to handle this — it is managed by the billing system.

Possible Error Responses

HTTP Statusresp_codeError DescriptionCauseAction Required
404"404"Component not foundbilling_code not found or not registered in the systemVerify billing_code with product/billing team
404"404"Organization package component not foundCompany doesn't have the component in their packageContact billing team to add component to company's package
404"404"Component quota not foundNo quota record exists for this company-component combinationContact billing team to initialize quota
422"422"Feature is not activeThe billing component exists but is not active for this companyContact billing team to activate the feature
422"422"Package component is not activeThe package component is not active for this companyContact billing team to activate the package component
422"422"Billing log already existsThe unique_code has already been used for a previous deductionThis is expected behavior for idempotency. The deduction was already processed.
500"500"Internal errorDatabase error, billing service error, or system malfunctionRetry the request. If persists, contact Bifrost team

Sample Error Response

{
"resp_code": "404",
"resp_desc": {
"id": "Tidak ditemukan",
"en": "Not found"
},
"meta": {
"version": "",
"api_env": ""
}
}

5. Quota Info

Endpoint:

GET {{base_sso_url}}/internal/qontak/billing/v1/quota-managements/info/:billing_code

Authentication: IAG (Service to Service)

Query Parameters

FieldTypeRequiredDescription
billing_codestringYesBilling component code (path parameter)
company_idstringNoCompany ID. Required for internal/S2S requests; auto-filled from SSO token for user requests.

Sample Response (Success)

{
"billing_code": "VOICE-RECORDING-2026-01",
"company_id": "269783",
"is_active": true,
"initial_quota": {
"initial_quota": 1,
"remaining_quota": 1,
"usage_quota": 0,
"unit_type": "credit",
"is_unlimited": false
},
"additional_quota": {
"initial_quota": 0,
"remaining_quota": 59,
"usage_quota": 1,
"unit_type": "credit",
"is_unlimited": false
},
"postpaid_quota": {
"initial_quota": 100000,
"remaining_quota": 99998,
"usage_quota": 2,
"unit_type": "credit",
"is_unlimited": false
}
}

Response Fields

Each quota type (initial_quota, additional_quota, postpaid_quota) contains:

FieldTypeDescription
initial_quotafloatTotal quota allocated at the start of the contract/month
remaining_quotafloatCurrent remaining quota available for deduction
usage_quotafloatTotal quota consumed so far
unit_typestringUnit of measurement: credit or balance
is_unlimitedbooleanWhether this quota type is configured as unlimited for this organization. When true, deductions are recorded but quota is never decremented.

6. Refund Quota

Refund quota back to the organization's package component. This endpoint restores previously deducted quota by distributing it back to initial quota first (up to its limit), then to additional quota for any overflow.

Endpoint:

POST {{base_sso_url}}/internal/qontak/billing/v1/quota-managements/refund

Authentication: IAG (Service to Service)

Sample Request

{
"company_id": "154982",
"billing_code": "EmailBroadcast",
"refund_code": "id",
"unique_code": "refund-broadcast-123-msg-456",
"quantity": 1
}

Request Parameters

FieldTypeRequiredDescription
company_idstringYesThe ID of the company whose quota will be refunded
billing_codestringYesThe billing code associated with the deducted quota
refund_codestringYesIdentifier that represents the refund context (e.g., country, category, or agreed code)
quantityfloatYesAmount of quota to be refunded. Minimum value is 1
unique_codestringNoUnique identifier to ensure idempotent refund processing

Sample Response (Success)

{
"company_id": "154982",
"billing_code": "EmailBroadcast",
"refund_code": "id",
"unique_code": "refund-broadcast-123-msg-456",
"value_before": 999,
"value_after": 1000,
"refunded_to": "initial"
}

Response Fields

FieldTypeDescription
refunded_tostringIndicates the result of the refund. See values below.
value_beforefloatRemaining quota value before refund
value_afterfloatRemaining quota value after refund

refunded_to possible values:

ValueMeaning
"initial"Refund was applied to the initial quota pool
"additional"Refund was applied to the additional quota pool (overflow from initial)
"already-refunded"unique_code was already used — idempotency hit, no duplicate refund performed
"retry-refund"Optimistic locking conflict detected. The refund job has been automatically requeued as a background job. No action needed from the caller.

Possible Error Responses

HTTP StatusResponse CodeError DescriptionCauseAction Required
404"404"Component not foundBilling code not found or not registered in the systemVerify billing_code with product/billing team
400"400"Feature is not activeMain component is inactive (main_component_is_active = false)Contact billing team to activate the feature
400"400"Package component is not activePackage component is inactive (package_component_is_active = false)Ensure company has active package subscription
404"404"Component quota not foundQuota record not found for company_id and billing_code combinationVerify company has the billing component provisioned
500"500"Internal errorDatabase error, billing service error, or system malfunctionRetry the request. If persists, contact Bifrost team

7. Negative Balance Handling (Downgrade Event)

In certain scenarios, such as package downgrade, an organization's quota balance may become negative.

This negative balance is not processed through an API call, but is instead published asynchronously via a Kafka event. Services that depend on quota information must listen to this event to properly handle post-downgrade quota states.

Kafka Topic

billing.quota_management.negative_balance

Event Payload

{
"company_id": "154982",
"billing_code": "EmailBroadcast",
"negative_amount": 50
}

Payload Fields

FieldTypeDescription
company_idstringThe ID of the company affected by the downgrade
billing_codestringThe billing component whose quota becomes negative
negative_amountfloatThe amount of quota that exceeds the downgraded package limit

Event Semantics

  • This event indicates that the current quota usage exceeds the new package limit
  • negative_amount represents the excess quota that must be reconciled
  • No automatic quota deduction or refund is performed by this event
  • Consumer services are responsible for:
    • Blocking further usage
    • Triggering internal reconciliation

8. Inactive Package Event (Kafka)

When a billing component transitions from active to inactive (e.g., subscription cancelled or plan downgraded to exclude the feature), the system publishes a Kafka event.

Kafka Topic

billing.quota_management.inactive_package

Event Payload

{
"company_id": "154982",
"organization_id": "org-uuid-12345",
"billing_code": "EmailBroadcast",
"is_package_inactive": true,
"quota_usage": 150.5
}

Payload Fields

FieldTypeDescription
company_idstringThe ID of the company affected
organization_idstringThe organization UUID
billing_codestringThe billing component that became inactive
is_package_inactivebooleanAlways true for this event
quota_usagefloatTotal quota consumed across all quota types (initial + additional + postpaid)

Event Semantics

  • Fired when a component goes from is_active=true to is_active=false
  • Initial and postpaid quota are reset to 0 on deactivation; additional quota is preserved (can be carried over when the component becomes active again)
  • Consumer services should use this to block further usage of the feature until reactivation

How is_unlimited is Determined

is_unlimited is automatically computed by the billing system — integration teams do not set it manually.

When the quota cache is refreshed (via the invalidate-cache endpoint or subscription change), the system checks:

  1. The billing component has an unlimited_value threshold configured (e.g., 99999999)
  2. The company's new initial_quota OR postpaid_quota equals or exceeds that threshold

If both conditions are met, is_unlimited=true is stored on the organization_package_components record and returned in all API responses going forward.

Practical implication: A company on an "unlimited" plan will have their feature's quota set to a very large number (≥ unlimited_value). The billing system detects this automatically and short-circuits all quota checks and deductions for that company's component.


Error Handling Best Practices

1. Check Quota First

Always call the Check Quota endpoint before attempting deduction. This helps you:

  • Verify component availability
  • Check if quota is sufficient
  • Get accurate quota information

2. Handle Specific Error Codes

Implement specific handling for each error code:

// Example error handling (pseudo-code)
try {
const response = await checkQuota(params);

if (!response.extra_attrs.is_sufficient) {
// Handle insufficient quota
// This is NOT an error, it's expected behavior
showInsufficientQuotaMessage();
return;
}

// is_unlimited=true means quota is never depleted — deduction still proceeds
// but value_before == value_after in the deduction response
if (response.extra_attrs.is_unlimited) {
logInfo("Component is unlimited — deduction will be recorded without decrementing quota");
}

// Proceed with deduction
const deductionResponse = await deductQuota(params);

// Handle special credited_to values
switch (deductionResponse.credited_to) {
case "already-deducted":
// unique_code was already processed — idempotency, safe to ignore
logInfo("Deduction already processed for unique_code:", params.unique_code);
break;
case "retry-deduction":
// Optimistic lock conflict — system has auto-requeued the job
// No action needed; the deduction will be processed in the background
logInfo("Deduction requeued due to lock conflict, will be processed shortly");
break;
default:
// Normal: "initial", "additional", "postpaid", or "free"
logInfo("Deduction applied to:", deductionResponse.credited_to);
}

} catch (error) {
switch (error.resp_code) {
case "404":
// Component/package not found
logError("Setup issue - contact billing team");
break;

case "422":
if (error.resp_desc.en === "feature is not active") {
showMessage("Feature not activated for your account");
} else if (error.resp_desc.en === "billing log already exists") {
// Idempotent request - already processed
logInfo("Deduction already processed");
}
break;

case "500":
// Retry logic
retryWithBackoff();
break;
}
}

3. Use Unique Codes for Idempotency

When processing critical operations (e.g., broadcasts, transactions), always provide a unique_code:

{
"unique_code": "broadcast-{broadcast_id}-message-{message_id}"
}

This ensures that if your request is retried due to network issues, the quota won't be deducted multiple times.

4. Retry Strategy

For 500 errors, implement exponential backoff:

  1. First retry: Wait 1 second
  2. Second retry: Wait 2 seconds
  3. Third retry: Wait 4 seconds
  4. Max retries: 3 attempts

If all retries fail, log the error and alert your monitoring system.


Additional Notes

Quota Migration (Legacy Systems)

Existing systems (e.g., MUV, WA Balance, Chatbot AI) being migrated to this quota management platform must continue using the old flow in parallel (dual-write) during the transition period.

  • Transition period: TBD (will be communicated by the Bifrost team)

Handling Insufficient Quota

When is_sufficient: false is returned from the Check Quota endpoint:

  • This is not an error — it's expected behavior
  • Any action required (e.g., showing upgrade prompt, disabling feature) must be handled by your service
  • Do NOT attempt to call the Deduction endpoint when quota is insufficient

Testing

Before going to production:

  1. Test with your staging credentials
  2. Verify error handling for all error codes
  3. Test idempotency with duplicate unique_code
  4. Verify quota deduction accuracy
  5. Test with insufficient quota scenarios

Support

For questions or issues:

  • Setup/Configuration: Contact Bifrost team via #bifrost-support
  • Bug Reports: Create ticket in Bifrost JIRA
  • Feature Requests: Discuss with product and billing teams

API Response Format

All responses follow the standard Mekari API format:

{
"resp_code": "200",
"resp_desc": {
"id": "Indonesian description",
"en": "English description"
},
"meta": {
"version": "1.0",
"api_env": "production"
},
"data": {
// Response data here
}
}

Error Response Format

{
"resp_code": "{HTTP_STATUS_CODE}",
"resp_desc": {
"id": "Deskripsi error dalam Bahasa Indonesia",
"en": "Error description in English"
},
"meta": {
"version": "",
"api_env": ""
}
}

Quick Reference: Error Codes Summary

Error CodeCommon ScenariosFirst Action
404Component/package not foundVerify setup with billing team
422Feature not active, already processed, validationCheck component status or idempotency
500System errorRetry with backoff, then escalate