| name | trigger-registration |
| description | Register Connector Namespace trigger configs for Azure Functions with the ConnectorTrigger extension. USE WHEN: setting up polling triggers (e.g., OnNewEmail, OnNewFile) that call back to an Azure Function, scaffolding a new Function App project with ConnectorTrigger, wiring callback URLs, or troubleshooting trigger configs. NOT FOR: connection setup (use connection-setup skill), extension internals development. |
Connector Trigger Registration for Azure Functions
Registers polling trigger configs on a Connector Namespace so that connector events (new email, new file, etc.) call back to your Azure Function via the ConnectorTrigger extension.
When to Use
- Developer needs a connector trigger (e.g., "when a new email arrives in Office365")
- Developer has an existing Connector Namespace connection (use the
connection-setup skill first if not)
- Developer needs to scaffold a new Function App project with
[ConnectorTrigger]
- Developer needs to wire the callback URL from a deployed or local Function App
Prerequisites
- Azure CLI ≥ 2.75.0 installed and authenticated (
az login)
connector-namespace CLI extension installed (see connection-setup skill)
- Connector Namespace with a connected connector (see
connection-setup skill)
- The Connector Namespace must have a system-assigned managed identity enabled
- Supported regions for Connector Namespace:
westcentralus
Key Concepts
Extension Webhook Endpoint
The connector extension (Microsoft.Azure.Functions.Worker.Extensions.Connector for isolated worker — recommended; Microsoft.Azure.Functions.Extensions.Connector for in-process) registers a webhook route on the Function App:
POST /runtime/webhooks/connector?functionName={FunctionName}&code={connector_extension_key}
functionName must exactly match the [Function("...")] attribute name
connector_extension is a system key auto-generated when the extension loads
- Locally (
func start), the system key is not enforced
Trigger Config vs Connection
Connector Namespace
├── connections/
│ └── office365-conn ← auth + runtime URL (connection-setup skill)
└── triggerConfigs/
└── onnewemail-trigger ← poll + callback config (THIS skill)
Scaffolding a New Function App Project
1. Initialize with azd
.NET
azd init -t functions-quickstart-dotnet-azd
Python
azd init -t functions-quickstart-python-azd
TypeScript
azd init -t functions-quickstart-typescript-azd
JavaScript
azd init -t functions-quickstart-javascript-azd
Note: The azd init templates create a host.json file. For non-.NET languages (Node.js, Python, etc.), update host.json to use the preview extension bundle version 4.42.0 or greater:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Preview",
"version": "[4.42.0, 5.0.0)"
}
}
2. Install connector packages
Add the connector extension and SDK packages:
.NET
Install the latest pre-release NuGet packages:
dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Connector --prerelease
dotnet add package Azure.Connectors.Sdk --prerelease
In-proc customers only: if you are still on the .NET in-process model, replace Microsoft.Azure.Functions.Worker.Extensions.Connector with Microsoft.Azure.Functions.Extensions.Connector. The isolated worker (above) is the recommended path for new projects.
Python
Add to requirements.txt (include packages based on your approach):
# >=2.2.0b4 required Python 3.13+, >=1.26.0b3 for Python < 3.13
azure-functions>=2.2.0b4
# Currently only supports Office 365 OnNewEmail operation
azurefunctions-extensions-connectors
# Required for str payloads, don't include if using azurefunctions-extensions-connectors
azure-connectors
Add to local.settings.json for Python < 3.12:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "python",
"PYTHON_ISOLATE_WORKER_DEPENDENCIES": "1"
}
}
TypeScript / JavaScript
npm install @azure/functions@4.15.1-preview
npm install @azure/functions-extensions-connectors@0.0.1-preview
npm install @azure/connectors
3. Replace the HTTP trigger with a ConnectorTrigger function
Delete any sample HTTP trigger functions and replace with:
.NET
using Microsoft.Azure.Functions.Worker.Extensions.Connector;
using Azure.Connectors.Sdk.Office365.Models;
[Function("OnNewEmail")]
public void OnNewEmail(
[ConnectorTrigger]
Office365OnNewEmailTriggerPayload payload)
{
_logger.LogInformation("From: {From}, Subject: {Subject}",
payload.From, payload.Subject);
}
Python
import azure.functions as func
import azurefunctions.extensions.connectors.office365 as office365
import logging
from typing import List
app = func.FunctionApp()
@app.function_name(name="OnNewEmail")
@app.connector_trigger(arg_name="emails")
def on_new_email(emails: List[office365.ClientReceiveMessage]) -> None:
logging.info("OnNewEmail trigger received")
for email in emails:
logging.info(f"Subject: {email.subject}")
logging.info(f"From: {email.from_}")
TypeScript
import { app, InvocationContext } from "@azure/functions";
app.generic("OnNewEmail", {
trigger: { type: "connectorTrigger", name: "payload" },
handler: async (payload: unknown, context: InvocationContext) => {
const data = typeof payload === "string" ? JSON.parse(payload) : payload;
for (const email of data?.body?.value ?? []) {
context.log(`From: ${email.from}, Subject: ${email.subject}`);
}
},
});
OneDrive for Business example (OnNewFile trigger):
import { app, InvocationContext } from "@azure/functions";
app.generic("OnNewFile", {
trigger: { type: "connectorTrigger", name: "payload" },
handler: async (payload: unknown, context: InvocationContext) => {
const data = typeof payload === "string" ? JSON.parse(payload) : payload;
for (const file of data?.body?.value ?? []) {
context.log(`File: ${file.name}, Path: ${file.path}`);
}
},
});
4. Run locally
Before starting the Function App, ensure Azurite (local Azure Storage emulator) is running. The Functions runtime requires AzureWebJobsStorage for local development.
Start Azurite (required for local development)
Choose one option:
Option 1: VS Code Extension (simplest)
- Install the Azurite extension from VS Code Marketplace
- Open Command Palette (
Ctrl+Shift+P) and run "Azurite: Start"
Option 2: npm global install
npm install -g azurite
azurite
Option 3: npx (no installation)
npx azurite
Verify local.settings.json includes:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "<language>"
}
}
Start the Function App
func start
The extension logs the webhook endpoint at startup:
Connector endpoint: http://localhost:7071/runtime/webhooks/connector
⚠️ Troubleshooting: If func start fails with AzureWebJobsStorage or storage connection error:
- Verify Azurite is running in a separate terminal
- Confirm
local.settings.json has "AzureWebJobsStorage": "UseDevelopmentStorage=true"
- Check that no other process is using port 10000 (Azurite's default port)
- Clear identity cache if switching between cloud/local storage:
%LOCALAPPDATA%\.IdentityService\cache (Windows) or ~/.IdentityService/cache (Mac/Linux)
Local Development with Port Forwarding
To test triggers locally, you need to expose your local Function App so the Connector Namespace can reach it.
Start the Function App with --enableAuth to secure the endpoint behind a system key:
func start --enableAuth
⚠️ Important: Always use --enableAuth when exposing your app via a dev tunnel. Without it, your function endpoint is completely unauthenticated on the public internet.
Confirm the app is running on http://localhost:7071.
🔔 Confirm: Is the Function App running with --enableAuth? (Yes / No)
⚠️ Security Warning: The following steps expose your local Function App to the public internet. Only proceed for local testing and if you understand the implications.
VS Code port forwarding
- Navigate to the Ports view in the Panel region (
Ports: Focus on Ports View) and select Forward a Port
- If you haven't logged in with GitHub before, you'll be prompted to sign in
- Enter port
7071 — port forwarding starts and the Ports view updates to show the forwarded port and its Forwarded Address (e.g., https://<id>-7071.uks1.devtunnels.ms)
- Change the visibility by right-clicking on the port and selecting Port Visibility → Public. Public ports don't require sign in
🔔 Confirm: Is the port forwarded with Public visibility and do you see the tunnel URL in the Ports panel? (Yes / No)
Create a dev tunnel with CLI (alternative to VS Code)
💡 Prefer the CLI over VS Code UI? Use the devtunnel CLI instead — see Dev Tunnels CLI quickstart.
devtunnel user login
devtunnel create <your-tunnel-id> -a
devtunnel port create <your-tunnel-id> -p 7071
devtunnel host <your-tunnel-id> --allow-anonymous
🍎 Mac note: If devtunnel host returns Tunnel service error: Request not permitted. Unauthorized tunnel creation access: Anonymous does not have 'create' access scope, your login didn't actually stick. On macOS, devtunnel user login without a provider flag silently leaves you as Anonymous. Fix:
devtunnel user login -g
devtunnel user show
devtunnel host -p 7071 --allow-anonymous
If your corp tenant blocks --allow-anonymous on the default tunnel domain, drop the flag and grant per-port anon access instead:
devtunnel host -p 7071
devtunnel access create -p 7071 --anonymous
Registering a Trigger Config
Step 1: Get the Callback URL
Deployed Function App
$resourceGroup = "<resource-group>"
$functionAppName = "<function-app-name>"
$functionName = "<function-name>" # must match [Function("...")] attribute
$connectorExtensionKey = az functionapp keys list -g $resourceGroup -n $functionAppName --query "systemKeys.connector_extension" -o tsv
$callbackUrl = "https://$functionAppName.azurewebsites.net/runtime/webhooks/connector?functionName=$functionName&code=$connectorExtensionKey"
Local development (with dev tunnel)
Use the tunnel URL from the Local Development with Port Forwarding section above.
Important: Always start your Function App with --enableAuth when using a dev tunnel.
Without it, your function endpoint is completely unauthenticated on the public internet.
func start --enableAuth
Retrieve the connector_extension system key from local Azurite storage:
# Use the well-known Azurite connection string
# See: https://learn.microsoft.com/azure/storage/common/storage-use-emulator#authorize-with-shared-key-credentials
$connStr = "<azurite-connection-string>"
# Find the most recent host.json blob
$blobs = az storage blob list --container-name azure-webjobs-secrets --connection-string $connStr -o json | ConvertFrom-Json
$blobName = ($blobs | Sort-Object { $_.properties.lastModified } | Select-Object -Last 1).name
# Download and parse
az storage blob download --container-name azure-webjobs-secrets --name $blobName --connection-string $connStr --file host-keys.json --no-progress
$keys = Get-Content host-keys.json | ConvertFrom-Json
$connectorKey = ($keys.systemKeys | Where-Object { $_.name -eq "connector_extension" }).value
Build the callback URL:
$tunnelUrl = "<your-tunnel-url>" # from VS Code Ports panel, e.g., https://<id>-7071.uks1.devtunnels.ms
$functionName = "<function-name>"
$callbackUrl = "$tunnelUrl/runtime/webhooks/connector?functionName=$functionName&code=$connectorKey"
Note: The tunnel must have Public visibility (anonymous access). The Connector Namespace cannot authenticate to private tunnels. We use connector extension keys for auth instead of the tunnel's built-in auth.
Step 2: Create Trigger Config
The trigger create command needs --notification-details passed via a temp file because the callback URL contains query-string characters:
$resourceGroup = "<resource-group>"
$namespaceName = "<namespace-name>"
$subscriptionId = "<subscription-id>"
$functionAppName = "<function-app-name>"
$triggerName = "<trigger-config-name>" # e.g., "onnewemail-trigger"
$connectionName = "<connection-name>" # e.g., "office365-conn"
$connectorName = "<connector-name>" # e.g., "office365"
$operationName = "<operation-name>" # e.g., "OnNewEmailV3" or "OnNewFilesV2"
$functionName = "<function-name>" # must match [Function("...")] attribute
# Write notification details to a temp file (callbackUrl contains ? and & which break shell parsing)
$notifFile = Join-Path $env:TEMP "notification-details.json"
@{ callbackUrl = $callbackUrl } | ConvertTo-Json -Compress | Set-Content -Path $notifFile -NoNewline
# Delete any existing trigger with the same name
az connector-namespace trigger delete `
-g $resourceGroup --namespace $namespaceName `
-n $triggerName --yes 2>$null | Out-Null
az connector-namespace trigger create `
-g $resourceGroup --namespace $namespaceName `
-n $triggerName `
--connection-details "{connectionName:$connectionName,connectorName:$connectorName}" `
--operation-name $operationName `
--notification-details "@$notifFile" `
--description "$connectorName $operationName -> $functionName" `
--metadata "{destinationType:functionApp,functionAppName:$functionAppName,functionAppResourceGroup:$resourceGroup,functionAppSubscriptionId:$subscriptionId,functionName:$functionName,recurrenceFrequency:Minute,recurrenceInterval:'5'}" `
-o none
Remove-Item $notifFile -ErrorAction SilentlyContinue
Trigger parameters — add --parameters for connector-specific inputs:
# Office 365 OnNewEmailV3 — specify folder
--parameters "[{name:folderPath,value:'Inbox'}]"
# OneDrive for Business OnNewFilesV2 — specify folder
--parameters "[{name:folderId,value:'root'}]"
# SharePoint OnNewItems — specify site and list
--parameters "[{name:dataset,value:'https://contoso.sharepoint.com/sites/MySite'},{name:table,value:'MyList'}]"
Note: Trigger parameters are connector-specific. Use az connector-namespace connection invoke with the connector's operations endpoint to discover available triggers and their required parameters.
Step 3: Verify Trigger Config
az connector-namespace trigger show `
-g $resourceGroup --namespace $namespaceName `
-n $triggerName `
--query "properties.{operation:operationName, state:state, callback:notificationDetails.callbackUrl}" `
-o table
Expected: state = Enabled.
Step 4: Test the Trigger
Trigger the connector event (e.g., send an email to the Office 365 inbox, upload a file to OneDrive root folder, etc.). Watch the Function App logs for execution:
Expected success output:
Executing 'Functions.OnNewEmail' (Reason='', Id=9c4e2415-bc91-430c-bda4-d8953725a432)
Received Microsoft 365 OnNewEmail trigger
Email received from: user@contoso.com
Email subject: Test Email
Executed 'Functions.OnNewEmail' (Succeeded, Id=9c4e2415-bc91-430c-bda4-d8953725a432, Duration=123ms)
If no logs appear:
- Verify the trigger config
state = Enabled (Step 3)
- Check that the callback URL is correct and publicly accessible (for local dev tunnels, verify
--allow-anonymous or per-port anonymous access)
- Review Troubleshooting section below
- Query trigger run history:
az connector-namespace trigger run list `
-g $resourceGroup --namespace $namespaceName `
--trigger-name $triggerName -o table
Step 5: Update Callback URL
To point an existing trigger config to a different callback (e.g., after redeploying or switching tunnels):
az connector-namespace trigger update `
-g $resourceGroup --namespace $namespaceName `
-n $triggerName `
--notification-details "callback-url=$newCallbackUrl"
Step 6: List All Trigger Configs
az connector-namespace trigger list `
-g $resourceGroup --namespace $namespaceName `
-o table
Step 7: Clean up after testing
When done testing, always revoke public access:
- In the Ports panel, right-click the forwarded port
- Select Stop Forwarding Port (or set visibility back to Private)
⚠️ Do not leave your local Function App publicly exposed longer than necessary.
Troubleshooting
Common Errors
| Error | Cause | Fix |
|---|
Could not find member 'connectionName' | Used connectionName at top level | Use --connection-details shorthand: connectionName=… connectorName=… |
Could not find member 'callbackUrl' | Put callbackUrl at properties level | Use --notification-details shorthand: callback-url=… |
| Trigger provisions but never fires | Missing callbackUrl or incorrect URL | Verify --notification-details callback-url=… is set and publicly accessible |
unrecognized arguments: --namespace | Azure CLI < 2.75.0 | Run az upgrade or use --connector-namespace-name |
Polling Interval
The Connector Namespace polls the connector every 1-5 minutes. After polling detects new content, it POSTs the payload to your callback URL.
Reference
For a complete mapping of trigger operations to function signatures across .NET, Python, and TypeScript (including which typed payload to use and how to specify the function), see Operations to Functions Signature Match.