Skip to main content

API Reference

Job Queue Admin exposes REST API endpoints for external integration. These APIs enable Azure Functions middleware, custom automation, and third-party integrations.


API Overview

Available APIs

APIPurposeUse Case
Callback APIHandle interactive actionsTeams/Slack button clicks
Pending Notifications APILayer 3 Azure pollingBackup notification delivery

Authentication

All API endpoints use Business Central's standard authentication:

  • OAuth 2.0: For Azure AD authenticated clients

  • Service-to-Service (S2S): For Azure Functions middleware

  • Basic Auth: For development/testing only


Callback API

Overview

The Callback API handles interactive actions triggered from notification cards (Teams/Slack buttons). When a user clicks "Restart" or "Put On Hold" in a notification, the Azure Functions middleware calls this API.

Base URL

/api/jemel/jobQueueMonitor/v1.0/companies({companyId})/jqaCallbacks

Endpoints

Get Job Queue Entry

GET /jqaCallbacks({systemId})

Returns job queue entry details for displaying in notification cards.

Response Fields:

FieldTypeDescription
idGUIDSystem ID of job queue entry
descriptionTextJob description
statusOptionCurrent status (Ready, In Process, Error, On Hold)
objectTypeToRunOptionCodeunit or Report
objectIdToRunIntegerObject ID to execute
noOfAttemptsToRunIntegerCurrent attempt count
maximumNoOfAttemptsToRunIntegerMaximum allowed attempts
earliestStartDateTimeDateTimeScheduled start time
errorMessageTextLast error message
jobQueueCategoryCodeCodeJob category

Restart Job

POST /jqaCallbacks({systemId})/restart

Restarts a failed or on-hold job queue entry.

Behavior:

  • Sets status to Ready

  • Sets Earliest Start Date/Time to current time

  • Returns error if job is currently running

Response:

FieldValue
actionResult"Success" or "Error"
actionMessageDescriptive message

Put On Hold

POST /jqaCallbacks({systemId})/putOnHold

Puts a job queue entry on hold.

Behavior:

  • Sets status to On Hold

  • Prevents auto-restart rules from restarting

  • Returns error if job is currently running

Response:

FieldValue
actionResult"Success" or "Error"
actionMessageDescriptive message

Set to Ready

POST /jqaCallbacks({systemId})/setToReady

Sets a job queue entry to Ready status without immediate restart.

Response:

FieldValue
actionResult"Success" or "Error"
actionMessageDescriptive message

Restart All Failed

POST /jqaCallbacks/restartAllFailed

Bulk restart all job queue entries in Error status.

Response:

FieldValue
actionResult"Success"
actionMessage"X job(s) restarted"

Resume All On Hold

POST /jqaCallbacks/resumeAllOnHold

Bulk resume all job queue entries in On Hold status.

Response:

FieldValue
actionResult"Success"
actionMessage"X job(s) resumed"

Pending Notifications API

Overview

The Pending Notifications API supports Layer 3 (Azure Polling) notification delivery. An Azure Functions timer trigger polls this API to find and process stale notifications that Layer 1 and Layer 2 failed to deliver.

Base URL

/api/jemel/jobQueueMonitor/v1.0/companies({companyId})/jqaPendingNotifications

Endpoints

Get Pending Notifications

GET /jqaPendingNotifications

Returns notifications with Pending or Failed status.

Response Fields:

FieldTypeDescription
entryNoIntegerQueue entry number
jobQueueEntryIdGUIDSource job queue entry ID
channelCodeCodeTarget notification channel
channelTypeEnumEmail, Teams, or Slack
statusEnumPending or Failed
triggerEventEnumOnError, OnAutoRestart, OnCooldown
createdAtDateTimeWhen notification was queued
attemptCountIntegerNumber of send attempts
jobDescriptionTextJob description snapshot
jobErrorMessageTextError message snapshot
companyNameTextSource company
deepLinkTextURL to job in BC
channelTeamsWorkflowUrlTextTeams webhook URL
channelSlackBotTokenTextSlack bot token
channelSlackChannelIdTextSlack channel ID
channelEmailAddressTextEmail recipient

Get Stale Notifications

POST /jqaPendingNotifications/getStaleNotifications

Marks notifications older than the stale threshold as "picked up by Azure". Prevents duplicate processing.

Behavior:

  • Finds notifications older than configured threshold (default 5 minutes)

  • Sets "Picked Up By Azure" flag to true

  • Records pickup timestamp

Use Case: Call this at the start of Azure polling cycle to claim notifications.

Mark as Sent

POST /jqaPendingNotifications({entryNo})/markAsSent

Marks a notification as successfully sent by Azure polling.

Behavior:

  • Creates log entry with Sent status

  • Sets delivery layer to "Azure Polling"

  • Deletes queue entry

  • Records success in telemetry

Mark as Failed

POST /jqaPendingNotifications({entryNo})/markAsFailed

Marks a notification as failed after Azure polling attempt.

Behavior:

  • Creates log entry with Failed status

  • Updates queue entry with error message

  • Does not delete queue entry (may retry)

  • Records failure in telemetry

Send Notification

POST /jqaPendingNotifications({entryNo})/sendNotification

Sends a notification through BC using the configured channel handler. This is the only way to send Email notifications from Azure polling, as Email requires BC's Email Account (SMTP credentials stored in BC).

Behavior:

  • Retrieves channel configuration

  • Uses appropriate channel handler (Email, Teams, or Slack)

  • For Email: Sends via BC Email Account system

  • For Teams/Slack: Sends via channel's webhook/API

  • On success: Creates log entry, deletes queue entry

  • On failure: Marks queue entry as failed with error

Use Case: Azure middleware should call this for Email channel notifications instead of trying to send directly (middleware has no SMTP access).

Response:

OutcomeResult CodeQueue Entry
SuccessDeletedRemoved
FailureUpdatedMarked as Failed

Azure Functions Integration

Typical Polling Flow (Layer 3 - Safety Net)

1. Timer triggers Azure Function (every 30 minutes) 2. Function calls GET /jqaPendingNotifications with filter:

  • Created At < (now - 15 minutes) // Stale threshold
  • Picked Up By Azure = false
  1. For each notification: a. Call POST /getStaleNotifications to claim b. Based on channel type:
    • Email: Call POST /sendNotification (BC sends via Email Account)
    • Teams: Middleware sends to webhook, then POST /markAsSent
    • Slack: Middleware sends via Bot API, then POST /markAsSent c. On error: Call POST /markAsFailed

Important: Email notifications must use /sendNotification because the middleware cannot send email directly (BC's SMTP credentials are not accessible externally).

Note: Layer 3 is a safety net. Most notifications (90%+) are delivered by Layer 1 (immediate) or Layer 2 (BC job queue) before Layer 3 polling runs.

Sample Azure Function (TypeScript)

import { AzureFunction, Context } from '@azure/functions'; import { BCApiClient } from '../shared/bcApiClient';

const pollPendingNotifications: AzureFunction = async function ( context: Context ): Promise { const bcClient = new BCApiClient(process.env.BC_TENANT_ID);

// Get stale notifications
const notifications = await bcClient.get('/jqaPendingNotifications', {
$filter: "pickedUpByAzure eq false"
});

for (const ntf of notifications.value) {
try {
// Mark as picked up
await bcClient.post(
\`/jqaPendingNotifications(${ntf.entryNo})/getStaleNotifications\`
);

// Send notification based on channel type
if (ntf.channelType === 'Email') {
// Email: Delegate to BC (BC has SMTP credentials)
await bcClient.post(
\`/jqaPendingNotifications(${ntf.entryNo})/sendNotification\`
);
// sendNotification handles marking as sent/failed internally
} else if (ntf.channelType === 'Teams') {
await sendTeamsNotification(ntf);
await bcClient.post(
\`/jqaPendingNotifications(${ntf.entryNo})/markAsSent\`
);
} else if (ntf.channelType === 'Slack') {
await sendSlackNotification(ntf);
await bcClient.post(
\`/jqaPendingNotifications(${ntf.entryNo})/markAsSent\`
);
}
} catch (error) {
// Mark as failed (only for Teams/Slack - Email handled by sendNotification)
if (ntf.channelType !== 'Email') {
await bcClient.post(
\`/jqaPendingNotifications(${ntf.entryNo})/markAsFailed\`
);
}
}
}

};

export default pollPendingNotifications;


Configuration

Stale Notification Threshold

Configure when notifications are considered stale:

  1. Open Job Queue Admin Setup

  2. Set Stale After (Minutes) field (default: 5)

  3. Notifications older than this are eligible for Azure polling

Layer 3 Toggle

Enable or disable Azure polling:

  1. Open Job Queue Admin Setup

  2. Toggle Azure Polling Enabled (Layer 3)

  3. When disabled, Layer 3 API calls are still accepted but not used


Error Handling

HTTP Status Codes

CodeMeaningAction
200SuccessContinue
400Bad RequestCheck request format
401UnauthorizedVerify authentication
403ForbiddenCheck permissions
404Not FoundEntry doesn't exist
500Server ErrorRetry with backoff

Common Errors

ErrorCauseSolution
"Cannot restart job that is currently running"Job in processWait for completion
"Entry not found"Already processedSkip to next
"Unauthorized"Invalid tokenRefresh authentication

Security Considerations

API Access Control

  • APIs require BC authentication

  • Use service-to-service (S2S) for Azure Functions

  • Limit API access to specific IP ranges if possible

Data Exposure

  • Channel credentials (tokens, URLs) are included in API responses

  • Use HTTPS only

  • Consider additional encryption for sensitive channels

Audit Trail

  • All API actions are logged in Notification Log

  • Telemetry tracks API usage

  • Action source marked as "API"


Next Steps: Review Notification Queue & Log for monitoring API activity, or see Azure Enterprise App Setup for S2S authentication configuration.