{"service":"DC-Emailer","description":"Sends DataCentral report exports as emails: optionally refreshes the export, waits for completion, embeds the exported pages, and delivers to groups and addresses.","authentication":{"transports":["X-API-Key: {key} header (preferred)","Authorization: ApiKey {key} header","?key={key} query parameter"],"note":"The key authenticates you to DC-Emailer. It is also forwarded upstream to DataCentral as 'Authorization: ApiKey {key}' when neither the dcApiKey parameter nor a server-side DataCentral:ApiKey is configured — so a DataCentral API key can be passed once and used for both. With pass-through enabled (DcEmailer:AllowKeyPassThrough, the default in the Azure deployment), the key does not need to be registered on DC-Emailer at all: it is accepted when DataCentral itself authenticates it for the request's instance and tenant."},"setup":{"steps":["Sign in to the DataCentral instance you will target (e.g. dev.datacentral.ai) as a user with role management and the 'Manage API Keys' permission.","Create a role for the integration: Administration -> Roles -> Create new role (e.g. 'EmailerRole'). On the Permissions tab, tick exactly the three permissions shown in the tree below — none of the Managing/Add/Delete/Edit sub-permissions are needed.","Create the key: Administration -> Users -> Manage API Keys (next to 'Create new user') -> Create new API Key. Set a secret and an expiration, and select the role from the previous step. Store the secret when it is shown.","Note the tenancy name of the tenant you target. Pass it as the 'tenant' parameter — DC-Emailer resolves it to the tenant id and sends it upstream as the Abp.TenantId header.","Call DC-Emailer with the key (see authentication): pass it once via key/X-API-Key and it is forwarded upstream, or keep separate keys with the dcApiKey parameter."],"permissions":[{"permission":"Export","path":"Pages > Administration > Item links > Power BI item links > Export","unlocks":"ItemLinks endpoints: listing export links (GetAllGroupedByItem) and triggering refreshes (UpdateExportForLinkKey). The sibling 'Dax query'/'Embed' boxes are not needed.","withoutIt":"Link lookup, refresh=true, and refresh polling fail — no distribution can be sent."},{"permission":"Organisation Units","path":"Pages > Administration > Organisation Units","unlocks":"OrganizationUnit endpoints: the group tree and group members. The parent checkbox only — the 'Managing …' sub-permissions are not needed.","withoutIt":"GET /api/v1/groups and groupIds/groupNames recipient resolution fail with 401 'Required permissions are not granted'."},{"permission":"Power BI Items","path":"Pages > Administration > Power BI Items","unlocks":"PbiReports/GetExportedPagesForReport and the exported page download. The parent checkbox only — Add/Delete/Edit item and Manage workspaces are not needed.","withoutIt":"Emails still send, but with page-name text sections instead of inline page images."}]},"endpoints":[{"method":"GET","path":"/wizard","purpose":"Interactive send wizard: connect, pick links/groups, shape the email, preview, send with live progress (anonymous page; key entered in the UI). Embeddable as a DataCentral iframe Tool — see /embed."},{"method":"GET","path":"/embed","purpose":"How to embed the wizard in DataCentral as an iframe Tool: setup steps, configuration figures, troubleshooting (anonymous page)."},{"method":"GET","path":"/api/v1/distributions/send","purpose":"Send a distribution; parameters in the query string."},{"method":"POST","path":"/api/v1/distributions","purpose":"Same as send; parameters as a JSON body (same names)."},{"method":"GET","path":"/api/v1/distributions/preview","purpose":"Compose the email without sending (current exports; no refresh): JSON { subject, html, plan, warnings } with inlined images, or raw email HTML via format=html. Same parameters as send."},{"method":"GET","path":"/api/v1/distributions/{id}","purpose":"Status/result of a distribution. JSON, or a live HTML progress page for browsers (see 'format')."},{"method":"GET","path":"/api/v1/distributions/history","purpose":"Past runs; optional from/to (ISO dates), defaults to the last 30 days. Scoped to the caller's instance/tenant — a tenant only sees its own runs (operator keys without instance/tenant parameters see all)."},{"method":"GET","path":"/api/v1/groups","purpose":"Available groups (DataCentral organization units), flat + nested tree. Accepts instance, tenant, dcApiKey."},{"method":"GET","path":"/api/v1/links","purpose":"All export links visible to the key/tenant, with page names, export status, and last export time — the source of valid linkIds. Accepts instance, tenant, dcApiKey, format."},{"method":"GET","path":"/api/v1/help","purpose":"This tutorial (anonymous)."},{"method":"GET","path":"/health","purpose":"Liveness (anonymous)."}],"requiredParameters":[{"name":"linkIds","example":"linkIds=423","description":"Export link id(s), comma separated. The owning reportId is resolved automatically; supply reportIds to pair positionally and skip the lookup."},{"name":"recipients (at least one source)","example":"groupIds=177,178 — or groupNames=EmailGroup1 — or to=someone@company.is","description":"Any of groupIds, groupNames (case-insensitive display names), to, cc, bcc. Email lists accept ';' or ',' separators. Group members receive the email via Bcc (they never see each other's addresses); duplicates are merged with to > cc > bcc precedence, so an explicit to=/cc= entry for the same person wins."},{"name":"key","example":"key=... (or the X-API-Key header)","description":"See authentication."}],"optionalParameters":[{"name":"refresh","example":"refresh=true","description":"Re-export first, poll until every page of every link reports finished, then send. Default false."},{"name":"testRun","example":"testRun=true","description":"Dry run: validates and returns the resolved recipient plan; no refresh, no email."},{"name":"force","example":"force=true","description":"Skip the reattach window (identical requests within 10 minutes normally return the running/recent job)."},{"name":"subject","example":"subject=Sales {date}","description":"Email subject. Default: 'DataCentral report update - {datetime}'. Variables: {date}, {time}, {datetime}, {requestId}."},{"name":"header","example":"header=Sales update","description":"Headline inside the email body. Raw text, or an HTML fragment (anything containing a real HTML tag is used verbatim). Supports the same variables as subject."},{"name":"text","example":"text=Numbers as of {datetime}.","description":"Free text under the headline. Raw text (line breaks kept) or an HTML fragment. Supports the same variables as subject."},{"name":"reportIds","example":"reportIds=386","description":"Pairs positionally with linkIds; skips the upstream link lookup."},{"name":"instance","example":"instance=dev","description":"DataCentral instance: api.[instance].datacentral.ai; 'main' = api.datacentral.ai. Default: configured BaseUrl."},{"name":"tenant","example":"tenant=internal","description":"DataCentral tenancy name; resolved to a tenant id (cached) and sent as Abp.TenantId. Default: configured TenantId."},{"name":"dcApiKey","example":"dcApiKey=...","description":"Explicit DataCentral API key for the upstream calls, when it differs from your DC-Emailer key."},{"name":"format","example":"format=json","description":"'json' or 'html' — overrides Accept-header negotiation on send, status, and help responses."}],"scripting":{"model":"Submitting is asynchronous: with refresh=true the send endpoint returns 202 Accepted immediately, carrying requestId and statusUrl, while the refresh -> compose -> send pipeline continues in the background. Poll statusUrl until the state is terminal. Sends without refresh complete synchronously and return 200 with the final state. Identical requests within the reattach window return the existing run (reattached: true) — pass force=true for a deliberate resend.","inFlightStates":["Received","Validating","Resolving","Refreshing","Composing","Sending"],"terminalStates":["Completed","DryRunComplete","PartiallyFailed","Failed"],"powershell":"$key  = '{key}'\n$send = Invoke-RestMethod -Headers @{ 'X-API-Key' = $key } `\n  -Uri 'https://emailer.datacentral.ai/api/v1/distributions/send?linkIds=423&groupIds=177&refresh=true&format=json'\n\"submitted $($send.requestId) - $($send.state)\"\nwhile ($send.state -in 'Received','Validating','Resolving','Refreshing','Composing','Sending') {\n    Start-Sleep -Seconds 5\n    $send = Invoke-RestMethod -Headers @{ 'X-API-Key' = $key } -Uri \"$($send.statusUrl)?format=json\"\n    \"$($send.state) - $($send.message)\"\n}\nif ($send.state -eq 'Completed') { \"sent to $($send.result.emailsSent) recipient(s)\" }\nelse { $send.errors }"},"examples":[{"name":"Dry run (no refresh, no email) — see who would receive what","url":"https://emailer.datacentral.ai/api/v1/distributions/send?linkIds=423&groupIds=177&testRun=true&key={key}"},{"name":"Send without refresh","url":"https://emailer.datacentral.ai/api/v1/distributions/send?linkIds=423&to=someone@company.is&key={key}"},{"name":"Refresh, wait, then send to groups and addresses","url":"https://emailer.datacentral.ai/api/v1/distributions/send?linkIds=423&groupNames=EmailGroup1&to=someone@company.is&refresh=true&key={key}"},{"name":"Custom texts with variables (raw text and HTML both work)","url":"https://emailer.datacentral.ai/api/v1/distributions/send?linkIds=423&to=someone@company.is&subject=Daily report {date}&header=Sales overview&text=<p>Fresh numbers as of {datetime}.</p>&key={key}"},{"name":"Preview the email in the browser without sending","url":"https://emailer.datacentral.ai/api/v1/distributions/preview?linkIds=423&to=someone@company.is&format=html&key={key}"},{"name":"Target a specific instance and tenant","url":"https://emailer.datacentral.ai/api/v1/distributions/send?linkIds=423&to=someone@company.is&instance=dev&tenant=internal&key={key}"},{"name":"List groups","url":"https://emailer.datacentral.ai/api/v1/groups?key={key}"},{"name":"List export links (find your linkIds)","url":"https://emailer.datacentral.ai/api/v1/links?key={key}"},{"name":"Check a distribution (append format=json to force JSON in a browser)","url":"https://emailer.datacentral.ai/api/v1/distributions/{requestId}?key={key}"},{"name":"History for a date range","url":"https://emailer.datacentral.ai/api/v1/distributions/history?from=2026-08-01&to=2026-08-10&key={key}"}]}