Deployment & Infrastructure
BulkForge
Customer Install Guide
This guide is for customers who subscribed to BulkForge SaaS on Azure Marketplace and need to deploy the
Customer Agent into their own Azure subscription. The dashboard and job orchestration API (control plane)
are hosted by AgeeBgee Solutions; you only install the execution agent. Delivery credentials for
Postmark and/or Azure Communication Services (ACS) stay in your tenant Key Vault.
What is the Agent?
A .NET 10 Azure Functions app (Linux Flex Consumption) that runs in your subscription. It polls the BulkForge control
plane for outbound jobs, reads delivery credentials from your Key Vault via managed identity, sends through Postmark
and/or ACS, and reports per-recipient audit results back over HTTPS. Your tokens never leave your tenant.
Prerequisites
- Active BulkForge SaaS subscription — subscribe via Azure Marketplace, complete onboarding at the control plane, and create an agent in Manage Agents to obtain your Agent ID and Agent Key.
- Azure subscription with permission to create resource groups, Function Apps (Flex Consumption), Storage accounts, and Key Vaults.
- A delivery channel (optional at install time): the agent deploys and connects to the control plane with zero channels configured — add credentials any time before your first send, either during install or later from the dashboard.
- Postmark — Server API token (
Servers → API Tokens). Optional: Account token (sender/template sync in dashboard).
- Azure Communication Services — connection string and verified sender address from your ACS resource.
- SendGrid — API key scoped to Mail Send.
- Agent Package URL — public HTTPS zip (default in the Marketplace wizard; override only when pinned to a specific release).
Channel priority, fallback routing, and default From addresses are configured in the BulkForge dashboard under Manage Agents after install — not in the Marketplace wizard.
Install via Azure Marketplace
- Subscribe to BulkForge SaaS on Azure Marketplace. You'll land on the control plane's onboarding page, which creates your Agent ID and Agent Key automatically — no separate "Manage Agents" step needed first.
- On the "Welcome aboard" screen, click Deploy agent to Azure. This opens the Azure Portal deployment blade directly, with the correct template already loaded — you don't need to find a separate "BulkForge Install" listing on the Marketplace storefront.
- Basics — subscription, region, resource group (e.g.
rg-bulkforge-agent).
- Agent Configuration — Postmark, ACS, and SendGrid credentials are all optional here. Leave any or all blank if you'd rather see the agent come up Active first; add them later from Manage Agents when you're ready to send:
Postmark Server Token (optional)
Postmark Account Token (optional)
ACS Connection String (optional)
ACS sender email address (optional)
SendGrid API Key (optional)
- Control Plane Connection — the Agent ID and Agent Key from the welcome screen are pasted in here manually (the key is shown once on that screen and is not emailed; the welcome email has your Agent ID and the deploy link); Azure doesn't support prefilling this field from a link. Control plane URL is set automatically to
https://bulkforge-engine-prod.azurewebsites.net.
- Review + create → wait approximately 3–5 minutes.
Prefer to install from the Marketplace storefront directly instead of the dashboard button? Search for the BulkForge Install offer (Azure Application) in Azure Portal and click Create — it's the same template and behaves the same way, just requires finding the listing yourself.
The template provisions:
- Function App (
agent-<random>) on Linux Flex Consumption (.NET 10 isolated via functionAppConfig).
- Storage account — runtime and deployment package container.
- Key Vault — Postmark tokens, ACS connection string, and sender address (RBAC; Function App reads via managed identity).
- A deployment script that copies the agent zip from the package URL into
deployments/released-package.zip (no WEBSITE_RUN_FROM_PACKAGE on Flex).
Metadata sync: After start, MetadataSyncFunction runs immediately, then every 15 minutes, pushing Postmark templates and sender signatures to the dashboard when tokens are configured. If the From Address dropdown is empty right after install, wait one sync cycle or restart the Function App.
After Deployment
- Open the Function App (name starts with
agent-) and confirm AgentPollingFunction is running — first poll within ~1 minute.
- In the BulkForge dashboard, open Manage Agents. Your agent should show Status: Active with a recent
lastHeartbeat.
- Configure delivery channel priority and defaults under Manage Agents if using both Postmark and ACS.
- Compose a message, upload recipients, or trigger a workflow through the Workflow Trigger API (Power Automate can call it as a custom connector).
Verifying the Install
- Dashboard → Manage Agents → agent status Active with current heartbeat
- Submit a small test send and confirm the per-recipient send results in the job audit view and the job summary (see below for what they do and do not tell you)
- Function App → Log stream — no repeated HTTP 401/403 against the control plane URL
- Key Vault → Function App managed identity has Key Vault Secrets User
What the Job Summary and Audit Trail Tell You
For every recipient, BulkForge records whether the email provider accepted or rejected the message, and the provider's reason when it rejected it. Each job also has a summary, for example 90 recipients: 89 accepted, 1 failed, with the failures grouped by reason (job page, or /api/jobs/<job id>/report). Accepted means the provider took the message for sending. It is not a delivery confirmation: BulkForge does not receive bounce, complaint or delivery events from the provider afterwards.
What you learn at send time depends on the provider:
- Postmark: the agent sends a batch and Postmark answers for each message. A message Postmark refuses at that moment (for example an inactive recipient) is recorded as failed with Postmark's reason. Anything that happens after Postmark accepts the message is not reported to BulkForge.
- SendGrid: SendGrid answers that it accepted the request. With agent 3.5.0 or later, identical messages to several recipients go in one request (up to 10 by default, each recipient as its own email so they cannot see each other), and the recipients of that request share one accepted answer; the same Batch sending switch as for Azure turns this off, and older agents send one request per recipient. If SendGrid refuses a group, for example because of one invalid address, its recipients are sent one at a time so only the bad address fails. A recipient SendGrid later suppresses or bounces is not reported to BulkForge.
- Azure Communication Services: for a single recipient per request, the agent checks the message's final status shortly after sending and records a failure with Azure's reason, for example a recipient on your suppression list. When the agent groups several recipients into one request (batch sending), Azure reports one status for the whole request. An address Azure drops inside a group, for example one on your suppression list, is then recorded as accepted and nothing in the audit shows it. Bounces after acceptance are not reported. Batch sending is on by default for agents on version 3.4.0 or later (up to 10 recipients per request, which keeps you well under Azure's per-minute and per-hour limits). If you need every suppressed address to show as failed, turn it off under Manage Agents → Sending Limits → Batch sending; each recipient is then sent on its own.
If a provider does not confirm whether a message went through (for example a server error, or no answer), agent 3.5.0 or later records the affected recipients as Outcome unknown, with a note that the email may have been sent. The agent does not send that job again on another channel, so check the recipient or your provider's activity before re-sending.
Agent 3.5.0 or later also reports a Sending performance panel under Manage Agents (last hour and last 24 hours): recipients accepted by Azure, Azure requests, average recipients per request, busiest minute, and how often Azure rate-limited the agent. It counts what Azure accepted, not inbox delivery, and it covers Azure Communication Services sends only, so it stays empty for an agent that sends only through Postmark or SendGrid.
To see bounces, complaints and suppressions for individual addresses, use your provider's own reporting: Postmark's message activity, SendGrid's activity and suppression lists, or Azure Communication Services' delivery reports and suppression list.
Troubleshooting
Agent does not go Active (within ~5 minutes)
Check Function App Log stream for HTTP errors against the control plane. Common causes: wrong Agent ID, mismatched Agent Key, or inactive SaaS subscription. Confirm Function App → Identity → system-assigned is On and Key Vault RBAC grants Key Vault Secrets User.
Functions blade empty
Wait 2–3 minutes. Confirm released-package.zip exists in the deployments container. Restart the Function App once. For manual recovery, run OneDeploy with a public package URL (use a JSON file; inline --body breaks):
@'
{"properties":{"packageUri":"https://YOUR-RELEASES-BLOB/customer-agent-code.zip","remoteBuild":false}}
'@ | Set-Content -Encoding utf8 onedeploy-body.json
$sub = az account show --query id -o tsv
$uri = "https://management.azure.com/subscriptions/$sub/resourceGroups/YOUR_RG/providers/Microsoft.Web/sites/YOUR_FUNCTION_APP/extensions/onedeploy?api-version=2022-09-01"
az rest --method PUT --uri $uri --headers "Content-Type=application/json" --body "@onedeploy-body.json"
Runtime / stack errors
Use Flex Consumption (FC1). Runtime is functionAppConfig → dotnet-isolated 10.0 — do not change legacy Stack settings in the portal. If deployment fails with an FC1 SKU error, pick a supported region.
ACS sends fail
Verify ACS connection string and sender address in Key Vault match a verified ACS domain. Confirm channel routing in Manage Agents selects ACS for the job.
Updating the Agent
We publish builds to public Azure Blob Storage (customer-agent-code.zip tracks latest; versioned names are also available). Upload the new zip to deployments/released-package.zip or run OneDeploy, then restart the Function App. You do not need to redeploy the full ARM template for routine code updates.
Rotating Secrets
Postmark or ACS credentials
- Key Vault → relevant secret → New version → paste updated value.
- Restart the Function App.
Agent Key
You can get a new Agent Key at any time, whether or not your agent has connected, without contacting us:
- Sign in to the BulkForge dashboard, open Manage Agents, find the agent, open Install and credentials, and click Rotate key.
- Read the warning and confirm. The old key stops working immediately, so your running agent stops connecting to BulkForge until you finish the next step.
- Copy the new key right away: it is shown once and cannot be shown again. In the Azure portal open your Function App, then Settings → Environment variables (older portal layout: Configuration → Application settings), edit
AGENT_KEY, paste the new value and apply. The Function App restarts automatically and the agent shows as running again within a few minutes.
If you lose the new key before pasting it, rotate again (rotation is limited to once a minute and 5 times an hour per agent). Only the signed-in owner of the agent can rotate its key, and not while the subscription is suspended or cancelled. If you have not deployed the agent yet, you do not need to rotate: use the key from the install form, or rotate to get a fresh one.
Data and Privacy
Delivery credentials are stored in your Key Vault; we never see them. For each job you submit, the control plane stores the full job payload (recipient email addresses, any per-recipient template data, and the subject and message content you submit), a per-recipient delivery audit record (recipient address, status, error code, channel used), and job counters. Templates you save in the dashboard are also stored. Your agent retrieves each job from the control plane and sends the messages directly through Postmark or ACS. All agent ↔ control plane traffic is HTTPS.
Retention: a daily sweep clears the stored payload (recipient addresses, template data, subject and message content) of completed jobs 7 days after the job last changed, and of failed and abandoned jobs after 90 days. It deletes per-recipient audit records after 90 days. Once a job's payload is cleared it can no longer be re-sent from the dashboard. Saved templates are not covered by the sweep: email support and we will purge them, or any of your other data, on request.
Your Subscription and Renewal
BulkForge is billed through Microsoft Marketplace. A Marketplace subscription renews automatically only if auto-renew is turned on. It is off by default, and we cannot turn it on for you: only you can, or the reseller who sold you the subscription if you bought through one.
To keep BulkForge running past the end of your current term, open your BulkForge subscription on the SaaS page in the Azure portal (or in the Microsoft 365 admin center) and turn on auto-renew. Your current term end date is shown there.
Starter and Pro can be bought as a 1-month term or as a 1-year term billed monthly. A 1-year term is a 12-month commitment: after the first 72 hours it cannot be cancelled until the term and its payments are complete. Microsoft emails a renewal reminder 30 days before a 1-year term ends; auto-renew is still off by default. The main BulkForge dashboard page shows your term end date and whether auto-renew is on.
If the subscription ends, BulkForge stops accepting your agent, and a cancelled Marketplace subscription cannot be reactivated: subscribe again to continue.
Uninstall
Delete the agent resource group:
az group delete --name rg-bulkforge-agent --yes
Your control-plane subscription and historical job audit data remain unless you request engine record removal from support.
Support
Email support@ageebgeesolutions.com. Include Agent ID, resource group, region, delivery channel (Postmark/ACS), and Function App Log stream lines (UTC).
Product overview: What is BulkForge? · Architecture: Reference architecture · Updates: What's new in BulkForge
Guide Reference: BF-INSTALL-V3 · Revised September 2026