Skip to main content

Change Document: v2.3.2.0 — API Page Breadcrumb Telemetry for Permission Failure Diagnosis

FieldValue
Version2.3.2.0
Date2026-03-09
ExtensionBC Dialing Application (Cambay Solutions)
SeverityLow — diagnostic telemetry only, no behavioral changes
StatusImplemented — ready for deployment

Background

Production telemetry from March 9 reveals that 65% of ReceivePhoneNumber (Pag-80000) and 74% of ReceiveEmail (Pag-80003) API calls are returning HTTP 403 with NavPermissionException. The 403s come in pairs (Nextiva auto-retry), while successful 201s are singles. Both success and failure happen on the same pages within the same hour, indicating the failure is path-dependent — likely the new customer creation path hits tables not covered by the pages' inline Permissions property.

The existing telemetry (BCDIALER-4000, BCDIALER-4001, BCDIALER-0010) only fires after the customer lookup/creation succeeds. When a 403 aborts execution mid-trigger, no extension-level telemetry is emitted — the only evidence is the RT0008 event in environment-level telemetry, which shows the HTTP status but not which specific operation triggered the permission check.

This version adds breadcrumb-style telemetry at each stage of the OnInsertRecord trigger. When a permission failure aborts execution, the last breadcrumb logged identifies the exact operation that caused it.


Summary of Changes

#SeverityCategoryDescription
1LowAddedStep-through breadcrumb telemetry (BCDIALER-0020) on Pages 80000 and 80003

Detailed Changes

1. Breadcrumb Telemetry — BCDIALER-0020

Files modified:

  • src/Page/Pag-80000.ReceivePhoneNumber.al — 7 breadcrumb points
  • src/Page/Pag-80003.ReceiveEmail.al — 7 breadcrumb points

Breadcrumb steps:

StepIDFires BeforeWhat It Tells You
11-TriggerEntryConfig readRequest reached BC and the trigger fired. If this is missing, the 403 occurred before the trigger (page-level permission check)
22-CustomerLookupFindFirst() on CustomerConfig loaded successfully. If execution stops here, the Customer table read failed
33-NewCustStartTemplateHeader.Get()Customer lookup found no match — entering creation path. If this is the last step, the template Get failed
44-PreInitCustomerNoInitCustomerNo()Template loaded. If execution stops here, InitCustomerNo hit a permission wall (No. Series tables)
55-PreInsertInsert(true)Number assigned. If execution stops here, the Insert(true) trigger chain hit a missing permission
66-PreApplyTemplateApplyCustomerTemplate()Record inserted. If execution stops here, ApplyCustomerTemplate accessed an unpermitted table
77-PreModifyModify(true)Template applied. If execution stops here, the final Modify(true) failed

Custom dimensions per breadcrumb:

DimensionPresent InDescription
SourceAll stepsPage identifier (Pag-80000 or Pag-80003)
StepAll stepsStep identifier (e.g., 4-PreInitCustomerNo)
PhoneNumberSteps 1-2 (Pag-80000)Inbound phone number
EmailSteps 1-2 (Pag-80003)Inbound email address
TemplateNameStep 3Customer template being used
CustomerNoSteps 5-7Assigned customer number

Diagnosis workflow:

  1. Query BCDIALER-0020 events and correlate with RT0008 403 events by timestamp
  2. For each 403, find the last BCDIALER-0020 breadcrumb within the same second
  3. The step ID tells you exactly which operation triggered the permission failure
  4. Add the missing table to the page's Permissions property

Querying the Telemetry

Find the last breadcrumb before each 403:

let failures = traces
| where timestamp > ago(1d)
| where customDimensions.eventId == "RT0008"
| where customDimensions.alObjectName in ("ReceivePhoneNumber", "ReceiveEmail")
| where customDimensions.httpStatusCode == "403"
| project failureTime = timestamp, objectName = tostring(customDimensions.alObjectName);
traces
| where timestamp > ago(1d)
| where customDimensions.eventId == "ALBCDIALER-0020"
| project breadcrumbTime = timestamp,
Source = tostring(customDimensions.alSource),
Step = tostring(customDimensions.alStep),
Phone = tostring(customDimensions.alPhoneNumber),
Email = tostring(customDimensions.alEmail),
CustomerNo = tostring(customDimensions.alCustomerNo)
| order by breadcrumbTime desc

Summarize which step is the last one reached (failure point):

traces
| where timestamp > ago(1d)
| where customDimensions.eventId == "ALBCDIALER-0020"
| extend Source = tostring(customDimensions.alSource),
Step = tostring(customDimensions.alStep)
| summarize count() by Source, Step
| order by Source, Step

If Step 4 has significantly fewer events than Step 3, the failure is in InitCustomerNo. If Step 5 has fewer than Step 4, the failure is in Insert(true). And so on.


Object Inventory

No new objects. Modified objects only:

ObjectIDTypeStatusDescription
ReceivePhoneNumber80000PageModifiedAdded 7 BCDIALER-0020 breadcrumbs
ReceiveEmail80003PageModifiedAdded 7 BCDIALER-0020 breadcrumbs

Deployment Notes

  • No data migration required — telemetry-only change
  • No configuration changes required
  • Backwards compatible — additive telemetry, no behavioral changes
  • Verification: After deployment, trigger an inbound call to a number not in BC (to exercise the new customer creation path) and confirm BCDIALER-0020 events appear in Application Insights with sequential step IDs

Known Limitations

  • Breadcrumbs may not fire on page-level 403s. If the permission check fails before the OnInsertRecord trigger executes (e.g., the API user lacks read permission on the source table itself), no BCDIALER-0020 events will be emitted. The absence of Step 1 combined with a 403 on RT0008 indicates a page-level permission failure rather than a trigger-level one.
  • Telemetry ingestion delay — 2-10 minutes before events are queryable in Application Insights.