Manifst Connectors

Connect Manifst to your software development tools to automatically synchronize tickets with your Git activity and receive real-time notifications in Slack and Discord.

Overview

Connectors are configured per project under Settings → Integrations. Only project owners can create or edit connectors.

The GitHub, GitLab, Azure DevOps, Bitbucket, Slack, Discord, Jira, Linear and Google Meet connectors are included from the Team plan onwards. Power BI and the REST API belong to the Portfolio plan.

Who gets access

A subscription grants capabilities to the account, never to the project. Being invited to a paying subscriber's project therefore grants no rights over their connectors: every member is assessed on their own plan.

Project memberWhat they can do
Team plan or above See Git links on item pages, trigger a Jira sync, analyse a Meet recording. Configuration remains restricted to project owners.
Starter plan Sees neither the Development panel nor the commit and pull request badges, and cannot drive any connector : even on a project that has active ones.

Their work still feeds the connectors, though: their commits are linked to tickets, their edits are pushed to Jira, their actions fire notifications. The subscriber paid for a complete view of their project's delivery : a teammate's contribution is not removed from it, it simply stays invisible to that teammate.

ConnectorDirectionMain Use Case
GitHub GitHub → Manifst Link commits & PRs to tickets, automatically close stories on merge
GitLab GitLab → Manifst Link commits & merge requests to tickets, automatically close stories on merge
Azure DevOps Azure DevOps → Manifst Link commits & pull requests to tickets, auto-close on merge
Bitbucket Bitbucket → Manifst Link commits & pull requests to tickets, auto-close on merge
Slack Manifst → Slack Real-time notifications for status changes, sprint updates, and comments
Discord Manifst → Discord Embed notifications in a Discord channel via Incoming Webhook
Power BI / REST API Manifst → Power BI Track and consolidate project data (EVM, costs, backlog, roadmap) in BI reports via REST API v1

GitHub

Automatically link commits and pull requests to your tickets using issue references in commit messages and PR descriptions.

Configuration

  1. In Manifst, open Settings → Integrations → GitHub → Configure.
  2. Copy the displayed secret key (visible only once).
  3. In GitHub, open your repository → Settings → Webhooks → Add webhook.
  4. Payload URL: the URL displayed in Manifst after saving.
  5. Content type: application/json required
  6. Secret: the secret key copied in step 2.
  7. Events: Pushes + Pull requests.
  8. Click Add webhook. GitHub sends a ping event : Manifst responds {"ok":true}.

Push : Link a commit to a ticket

Include a #<PREFIX>-<id> reference in your commit message to automatically link the commit to the matching item.

Syntax
#<PREFIX>-<numeric id>

The prefix is the one shown in front of the item in Manifst. Every ticket type defined in the project is recognised : not just US : along with both roadmap levels:

PrefixTarget
#US-42, #BG-7, …A ticket, per the abbreviations defined in Settings → Ticket types
#EP-12An Epic
#SE-3A Super Epic
The prefix must match the item's actual type: #US-42 links nothing if item 42 is a bug shown as BG-42. Unresolved references show up in the delivery log rather than being linked to the wrong item.
Examples
git commit -m "fix: login form validation #US-42"

git commit -m "feat: add pagination support #US-17 #US-18"

git commit -m "fix: crash on startup #BG-7"

Multiple references in a single commit are supported.

Branch name

The branch name is parsed too, without the hash sign: no commit-message discipline is needed when your naming convention already carries the reference.

git checkout -b feature/US-42-login-form
git checkout -b hotfix/BG-7

The branch then appears in the Development panel of the item, next to commits and pull requests.

Recorded FieldSource
Commit SHAcommit.id
Messagecommit.message (max 500 chars)
URLcommit.url
Authorcommit.author.name
Branchref
A push does not change the ticket status. It only creates a link, shown in the Development panel of the item. To update status automatically, use a Pull Request with auto-close enabled.

Pull Requests

Mention one or more items in the PR title or description, using the same syntax as for commits. The PR's source branch is parsed as well: a PR coming from feature/US-42-… is linked even without an explicit mention.

GitHub's closing keywords (fix, fixes, close, closes, resolve, resolves…) placed in front of a reference are recognised and recorded as a closing intent.

PR Description Example
## Changes
User profile form implementation.

Closes #US-15 #US-23

The link status in Manifst is updated automatically whenever the PR state changes:

GitHub ActionManifst Link Status
opened, reopened, synchronize, editedopen (or draft if the PR is a draft)
converted_to_draftdraft
ready_for_reviewopen
closed (unmerged)closed
closed (merged)merged
The PR's current text is authoritative: removing #US-42 from the description removes the matching link. A link that already triggered an auto-close is kept, so the trace of what changed the status is never erased.

Auto-close stories on merge

When a PR is merged, Manifst can automatically move linked tickets to a designated "Done" column.

To enable this feature:

  1. In Settings → Integrations → GitHub, toggle on Automatically close stories on merge.
  2. Select the destination column from the dropdown list (columns of type "done" in your Kanban board).
  3. Save. Any merged PR referencing a ticket will move the referenced tickets to that column, whatever their prefix.
If a ticket already has the target status, no change is made. The target column must belong to the same project.
Auto-close applies to tickets only. An Epic or Super Epic referenced with #EP- or #SE- is linked to the PR, but its status is never changed: those two levels are not driven by Kanban columns.

Once, at merge time

Closing fires on the event that performs the merge, not on the "merged" state. In practice, if you reopen a ticket by hand afterwards, later editing the PR title or description will not close it again. A human decision is never overwritten by a text change on GitHub.

An automatically closed ticket carries an "auto-closed" note under the matching link, in the Development panel of its page.

GitLab

The same linking as GitHub, applied to merge requests. Both connectors can run side by side on the same project.

Configuration

  1. In Manifst, open Settings → Integrations → GitLab → Configure.
  2. Copy the displayed token (visible only once).
  3. In GitLab, open your project → Settings → Webhooks → Add new webhook.
  4. URL: the one displayed in Manifst after saving.
  5. Secret token: the token copied in step 2.
  6. Trigger: Push events + Merge request events.
  7. Leave Enable SSL verification ticked, then Add webhook.
GitLab's authentication is weaker than GitHub's, and that comes from GitLab, not Manifst. GitHub signs each delivery body (HMAC-SHA256), proving both possession of the secret and the integrity of the content. GitLab merely echoes the token in plain text in a header. Consequences: the received content cannot be verified, and the token is replayable if intercepted. The webhook URL must therefore use HTTPS : over HTTP, the token travels in the clear on every push.

References

The syntax is identical to GitHub: #<PREFIX>-<id> in a commit message, the branch name parsed without a hash sign, and closing keywords recognised. See the GitHub section for details : it is literally the same parsing code.

GitHubGitLab
Pull requestMerge request : shown as !42 in the UI
pull_request eventMerge request events
X-Hub-Signature-256 headerX-Gitlab-Token header
Content type: application/json must be selectedAlways JSON, nothing to set
Manifst stores the merge request's visible number (iid : the one in the URL and in !42), not its instance-wide internal id. Links therefore point at the MR you actually see.

Private repositories and self-managed instances

The connector is inbound: GitLab calls Manifst. A private repository therefore works exactly like a public one : no repository access token is needed, and Manifst never reads your code.

A self-managed instance works too, on one condition: it must be able to reach Manifst's public URL. An instance behind a corporate firewall can often call out even when inbound access is impossible : what matters is the direction of travel.

One nuance about links: the ones recorded point at your repository. On a private repository, a Manifst member without GitLab access will land on a login page. That is the expected behaviour : repository permissions belong to GitLab.

Azure DevOps

Azure DevOps

The same linking as GitHub and GitLab, applied to Azure Repos Service Hooks. The parsing engine is the same code: reference syntax does not change from one platform to the next.

Setup

Azure DevOps does not create one webhook covering several events: it creates one subscription per event. You therefore repeat the operation, pointing at the same URL, for each event type you want to track.

  1. In Manifst, open Settings → Integrations → Git connector → Configure, pick Azure DevOps, fill in the repository URL and save.
  2. Copy the token and the webhook URL shown (the token is displayed only once).
  3. In Azure DevOps, open Project settings → Service hooks, then Create subscription.
  4. Service: Web Hooks. Trigger: Code pushed.
  5. On the Action screen: paste the Manifst URL into URL, and under HTTP headers enter the line:
    X-Manifst-Token: <your token>
  6. Leave Resource details to send on All.
  7. Test, then Finish.
  8. Repeat steps 3 to 7 for Pull request created, Pull request updated and Pull request merge attempted.
"Resource details to send" must stay on "All". Set to Minimal or None, Azure no longer sends commits, title or branch: there is then nothing left to parse, and no link can be created. The delivery log flags this case with the reason resource_too_thin : it is a settings problem, not a reference syntax one.
Authentication is as strong as GitLab's, not as strong as GitHub's, and that comes down to Azure DevOps. GitHub signs the body of every delivery (HMAC-SHA256), proving both possession of the secret and the integrity of the content. Azure DevOps signs nothing: it simply forwards the headers you configured. Consequences: the received content cannot be verified, and the token is replayable if intercepted. The webhook URL must therefore use HTTPS : Azure requires it anyway as soon as authentication is involved.

References

The syntax is identical to GitHub: #<PREFIX>-<id> in a commit message or a pull request description, the branch name parsed without the hash, and the same closing verbs. See the GitHub section for details.

GitHubAzure DevOps
One webhook, several eventsOne subscription per event
X-Hub-Signature-256 header (signature)Free-form X-Manifst-Token header (shared token)
pull_request eventPull request created / updated / merge attempted
Pull request written #42Pull request written !42
The event is called "Pull request merge attempted", and the word matters: Azure also fires it when the merge fails, on a conflict for instance. Manifst treats the pull request as merged : and therefore triggers auto-close : only when Azure additionally reports a completed state and a succeeded merge. A conflicted pull request closes no ticket.

Private repositories and Azure DevOps Server

The connector is inbound: Azure calls Manifst. A private repository therefore works exactly like a public one : no repository access token is needed, and Manifst never reads your code.

An Azure DevOps Server (self-hosted) instance works too, provided it can reach Manifst's public URL. What matters is the direction of travel: an instance behind a corporate firewall can often call out even when inbound access is impossible.

Azure DevOps refuses to target localhost and reserved address ranges. A development environment is therefore not reachable directly: you need to expose Manifst behind a public HTTPS tunnel to exercise the connector outside production.

Linear

Linear

Sync a Linear team with Manifst bidirectionally: Initiatives, Projects, Issues, sub-issues, Cycles, comments, labels/tags, estimates, and dependencies.

One backlog tool per project. Jira, Azure Boards and Linear all write the parent, status and sprint of the same items. Connecting multiple backlog tools simultaneously would lead to write conflicts. Manifst therefore allows only one active backlog connector per project : disconnect the current one if you are switching tools.

Setup

  1. In Linear, open Settings → Account → API (or Workspace → API) and generate a Personal API Key.
  2. In Manifst, go to Settings → Integrations → Backlog connector → Configure, then pick Linear.
  3. Connection : paste your Linear API Key and click Test. Manifst validates the key and lists all accessible teams in your workspace.
  4. Team & Prefix : select the Linear Team to sync. You can set a prefix for ticket types (default Type /, e.g. labels named Type / Bug become the Bug ticket type in Manifst).
  5. Frequency : adjust the automatic background sync interval (from 5 to 1440 minutes, 10 min by default).
  6. Sync : Manifst reads the Linear backlog, calculates the diff (creations, updates), and only writes to Manifst once you click Save to Manifst.
Saving configuration vs writing data. Completing the setup steps saves the connector configuration to your project, but no items are imported until step 6 is confirmed. Once active, automatic background sync runs incrementally.
Real-time updates to Linear. As soon as the connector is active, any action performed in Manifst (creating or updating Tickets, Subtasks, Epics, Super Epics, Sprints, Comments or Dependencies) is pushed immediately to Linear using its GraphQL API.

Mapping

Manifst ConceptLinear Object / FieldDetails & Resolution
Super EpicInitiativeTitle, description (Markdown), status, start and target dates.
EpicProjectLinked to parent Initiative (Super Epic) and selected team.
TicketIssueTitle, description (Markdown), priorities, estimate points, and status.
Ticket TypeLabel Type / ...Extracted from labels with configured prefix (e.g. Type / FeatureFeature).
SubtaskSub-issueChild of a Linear Issue, synced with state (To Do / In Progress / Done).
SprintCycleStart/end dates, cycle name, description, and status (Active / Past / Future).
Story pointsEstimateNumeric story point estimate on Linear Issue.
PriorityPriorityUrgent (1) ↔ Critical, High (2) ↔ High, Normal (3) ↔ Normal, Low (4) ↔ Low.
StatusState CategoryTriage/Backlog/Unstarted → To Do, Started → In Progress, Completed/Canceled → Done.
AssigneeAssigneeMatched automatically by user email address.
CommentsCommentsTwo-way sync of text comments attached to items.
TagsLabelsAutomatic creation and linking of corresponding Manifst tags.
DependenciesRelations (blocks)Linear ordering links are synchronized with Manifst dependency graph.

Linear supports multiple relation types between issues (blocks, duplicate, related). Manifst filters and synchronizes only blocks relations ("blocks" / "is blocked by"), as they are the only ones defining strict execution order.

When a local ticket B is set to depend on ticket A in Manifst, the connector creates a GraphQL relation where A blocks B in Linear. Conversely, any blocks relation created in Linear is immediately reflected on Manifst dependency charts and item details.

Limits & Best Practices

Bitbucket

The same linking as the other three platforms, applied to Bitbucket Cloud Webhooks. The parsing engine is the same code: reference syntax does not change from one platform to the next.

Setup

  1. In Manifst, open Settings → Integrations → Git connector → Configure, pick Bitbucket, fill in the repository URL and save.
  2. Copy the secret and the webhook URL shown (the secret is displayed only once).
  3. In Bitbucket, open your repository → Repository settings → Webhooks → Add webhook.
  4. Title: "Manifst". URL: paste the Manifst URL.
  5. Secret: paste the Manifst secret. This field is optional for Bitbucket, but mandatory for Manifst : see the warning below.
  6. Triggers: tick Repository → Push, then, under Pull request, tick Created, Updated, Merged and Declined.
  7. Leave Status on Active, then Save.
The "Secret" field is not optional on the Manifst side. Bitbucket will happily create a webhook without a secret: it then sends no signature at all, and Manifst rejects the delivery (HTTP 401). This is deliberate : without a signature, anyone who knows the URL could forge commit links on your tickets. A webhook that reports nothing at all, with 401s in Bitbucket's View requests tab, is almost always this field left empty.
Authentication is as strong as GitHub's, unlike GitLab and Azure DevOps. Bitbucket signs the body of every delivery with HMAC-SHA256 and sends the result in the X-Hub-Signature header: both possession of the secret and the integrity of the payload are proven. The secret alone is never replayable.

References

The syntax is identical to GitHub: #<PREFIX>-<id> in a commit message or in a pull request description, the branch name parsed without the hash sign, and the closing verbs recognised. See the GitHub section for details.

GitHubBitbucket
X-Hub-Signature-256 headerX-Hub-Signature header (same HMAC-SHA256)
pull_request eventpullrequest:created / updated / fulfilled / rejected
Merged = mergedMerged = fulfilled, declined = rejected
One branch per deliverySeveral branches possible in a single delivery
A git push --all reaches Bitbucket as a single event describing several branches. Manifst parses them all: tickets referenced on each one are linked in one go. Tags, on the other hand, are ignored (tag_only reason in the log) : a tag is not a working branch.
Bitbucket truncates the commits of large pushes. Past a certain volume the payload carries only a subset of the commits and Bitbucket raises a truncated flag, which the delivery log echoes. A commit that was never sent cannot be linked: this is neither a syntax error nor a Manifst defect. Push more often, or put the reference on the pull request, which is never truncated.

Private repositories

The connector is inbound: Bitbucket calls Manifst. A private repository therefore works exactly like a public one : no repository access token is needed, and Manifst never reads your code.

The only nuance concerns links: the ones recorded point at your repository. On a private repo, a Manifst member without Bitbucket access will land on a login page. That is the expected behaviour : repository permissions belong to Bitbucket.

Bitbucket refuses to target localhost and reserved address ranges. A development environment is therefore not directly reachable: you need to expose Manifst behind a public HTTPS tunnel to exercise the connector outside production.

Jira

Jira

Automatically sync your Jira Cloud backlog with Manifst (Tickets, Epics, Super Epics, and Story Points).

Setup

  1. In Manifst, go to Settings → Integrations → Backlog connector → Configure, then pick Jira.
  2. Connection : enter your Jira site URL (e.g. https://your-site.atlassian.net), your Atlassian email and API token, then click Test.
  3. Project : specify the project key (e.g. PROJ).
  4. Mapping : check the Jira types used for Epics and Super Epics, and the Story Points field.
  5. Filter : adjust the JQL and the automatic sync frequency.
  6. Sync : Manifst reads your backlog, shows what will change, and only writes once you click Save into Manifst.
Moving to the next step saves the connector, but writes nothing into your backlog. The two are separate: the configuration is saved at step 5, the content only at step 6.

Manifst only imports Jira links of type Blocks ("blocks" / "is blocked by"): they are the only ones that say which ticket comes first. A Relates, Duplicate or Cloners link carries no ordering and is ignored.

Matching is done on the link type name. On a Jira instance whose link types were created in another language ("Bloque", "Blockiert"…), dependencies will not come through. The Sync step names the link types actually encountered : that is where you see it.

Azure Boards

Azure Boards

Sync an Azure Boards project with Manifst, both ways: work items, hierarchy, iterations, comments, attachments and dependencies.

One backlog tool per project. Jira and Azure Boards both write the parent, state and sprint of the same tickets. Connecting both would not sync twice: they would overwrite each other on every pass. Manifst therefore rejects the second one : disconnect the first if you are switching tools.

Setup

  1. In Azure DevOps, create a personal access token: User settings → Personal access tokens → New Token, with the Work Items: Read, write & manage scope.
  2. In Manifst, go to Settings → Integrations → Backlog connector → Configure, then pick Azure Boards.
  3. Connection : enter your organization URL (https://dev.azure.com/my-organization) and the token, then click Test.
  4. Project : pick the project and the team.
  5. Mapping : Manifst detects the process template (Agile, Scrum, CMMI or Basic) and derives the four work item types. You can override them if your process is customized.
  6. Filter : adjust the WIQL query and the automatic sync frequency.
  7. Sync : Manifst reads the backlog, shows what will change, and only writes once you click Save into Manifst. Automatic sync then runs every 10 minutes by default.
Moving to the next step saves the connector, but writes nothing into your backlog. The two are separate: the configuration is saved at step 5, the content only at step 6. The read stays valid for 30 minutes; past that, run it again.
Team and iterations. Iterations are listed per team. An iteration defined at project level but not assigned to the selected team will not appear in your sprints: that is the number one cause of "my sprints are missing". Leave the Team field empty to use the project's default team.

Mapping

ManifstAzure Boards (Agile)Azure Boards (Scrum)
Super EpicEpicEpic
EpicFeatureFeature
TicketUser StoryProduct Backlog Item
SubtaskTaskTask
SprintIteration (System.IterationPath)
Story pointsMicrosoft.VSTS.Scheduling.StoryPointsMicrosoft.VSTS.Scheduling.Effort
StatusState category (Proposed / InProgress / Resolved / Completed)
PriorityMicrosoft.VSTS.Common.Priority (1 to 4)
DependencyPredecessor / Successor relation

These types can be changed under advanced options in the modal if your process is customized.

Limits

Slack

Receive notifications in a Slack channel for important project events.

Configuration

  1. In Slack, create an Incoming Webhook at api.slack.com/apps → your app → Incoming Webhooks → Add New Webhook to Workspace. Select the target channel.
  2. Copy the generated Webhook URL (format: https://hooks.slack.com/services/T…/B…/…).
  3. In Manifst, open Settings → Integrations → Slack → Configure.
  4. Paste the URL into the Webhook URL field.
  5. Check the desired events.
  6. Click Test Connection to verify message delivery, then click Save.
Only https://hooks.slack.com/services/… URLs are accepted. All other URLs are rejected for security reasons.

Available Events

story_status_changed
Triggered when a ticket status changes. Indicates former status, new status, and ticket title.
story_assigned
Triggered when a ticket is assigned (or reassigned) to a team member.
new_comment
Triggered on every new comment. Includes a preview (max 120 chars, HTML stripped).
sprint_started
Triggered when a sprint moves from Planning to Active.
sprint_ended
Triggered when a sprint is marked as Completed.
pr_merged
Triggered when a GitHub pull request referencing one or more items is merged. The message shows the target branch, who merged it and the linked items. Requires the GitHub connector on the same project.

Message Format

Messages use Slack mrkdwn formatting. Here are examples for each event:

story_status_changed
🔄 *Status updated* : *Update user profile*
Backlog → In progress
story_assigned
👤 *Story assigned* : *Add pagination*
Assigned to *Marie Dupont*
new_comment
💬 *New comment* on *Update user profile*
By *Jean Martin*: Form must handle profile avatar upload…
sprint_started
🚀 *Sprint started* : *Sprint 3 : Auth & Profile*
sprint_ended
✅ *Sprint ended* : *Sprint 3 : Auth & Profile*

Discord

Receive embed notifications in a Discord channel for key project events.

Configuration

  1. In Discord, open your channel → Channel Settings → Integrations → Webhooks → New Webhook.
  2. Name the webhook (e.g. Manifst), select the channel, then click Copy Webhook URL.
    The URL format is https://discord.com/api/webhooks/<id>/<token>.
  3. In Manifst, open Settings → Integrations → Discord → Configure.
  4. Paste the URL into the Webhook URL field.
  5. Check the desired events.
  6. Click Test to send a test message to Discord, then click Save.
Only https://discord.com/api/webhooks/… URLs are accepted. All other URLs are rejected to prevent SSRF vulnerabilities.

Available Events

Events are identical to the Slack connector : a single event can trigger both Slack and Discord webhooks simultaneously if both are enabled.

story_status_changed
Triggered when a ticket status changes. Embed displays old/new status and ticket title.
story_assigned
Triggered when a ticket is assigned or reassigned to a team member.
new_comment
Triggered on new comments. Embed includes a snippet of the comment.
sprint_started
Triggered when a sprint becomes active.
sprint_ended
Triggered when a sprint is completed.
pr_merged
Triggered when a GitHub pull request referencing one or more items is merged. Requires the GitHub connector on the same project.

Embed Format

Discord notifications utilize embeds with distinct sidebar colors per event type. Text supports standard Discord Markdown (**bold**, *italic*, `code`).

EventColor
sprint_started#57F287 : Green
sprint_ended#FEE75C : Yellow
new_comment#EB459E : Pink
story_status_changed, story_assigned#5865F2 : Blurple
pr_merged#8B5CF6 : Violet

Sample Discord webhook JSON payload:

POST https://discord.com/api/webhooks/<id>/<token>
{
  "embeds": [{
    "description": "🔄 **Story updated** : **Update user profile**\nStatus: `Backlog` → `In progress`",
    "color": 5793266
  }]
}
Discord returns HTTP 204 No Content on success (unlike Slack which returns 200 ok). Manifst treats any response other than 204 as a delivery error.

Google Meet

Google Meet

Turn your Google Meet meeting recordings and transcripts into Epics, User Stories, and subtasks powered by Manifst AI.

Setup

  1. In Manifst, go to Settings → Integrations → Google Meet → Connect.
  2. Grant Manifst access to your Google Workspace meeting notes and transcripts via OAuth.
  3. Once connected, click Meetings to view meetings with available transcripts.
  4. Select a meeting and click Generate backlog: Manifst AI automatically structures your backlog items.

Power BI / REST API

Connect Power BI Desktop (or any BI tool) to the v1 REST API to build custom dashboards and consolidate multi-project tracking: Earned Value Management (EVM), costs, backlog, and roadmap.

This connector requires no configuration in Manifst : it consists of consuming the v1 API directly from Power BI. Refer to the API Documentation for the full list of endpoints.

Key & Prerequisites

The key is shown only once upon creation. Keep it secure and never expose it in client-side code.

Before configuring Power BI, test your key with a quick PowerShell command : if it returns JSON {"data":[…]}, you are ready:

curl.exe -H "Authorization: Bearer mfst_live_YOUR_KEY" https://manifst.net/api/v1/projects

Connecting Power BI Desktop

The API authenticates via an Authorization: Bearer header. The most reliable pattern is declaring it inside a Power Query M script using Web.Contents (compatible with scheduled cloud refresh). Open Home → Transform data → New Source → Blank Query → Advanced Editor and paste:

The examples hard-code the key to stay readable. In practice, create an ApiKey parameter (Home → Manage Parameters) and replace ApiKey = "mfst_live_…" with it: one value to change, and you can redefine it in the Power BI service without reopening the file.
Project EVM Indicators Query
let
    BaseUrl = "https://manifst.net/api/v1",
    ApiKey  = "mfst_live_YOUR_KEY",
    Source  = Json.Document(
        Web.Contents(BaseUrl, [
            RelativePath = "finance",
            Query   = [ project = "PROJECT_UUID" ],
            Headers = [ Authorization = "Bearer " & ApiKey ]
        ])
    )
in
    Source[data]

When Power BI prompts for authentication credentials, select Anonymous: credentials are passed directly via the HTTP header. Obtain your project UUID from the /projects endpoint.

Always declare headers using Web.Contents(BaseUrl, [RelativePath=…, Headers=…]) instead of the GUI wizard to avoid "Dynamic Data Source" errors during scheduled service refreshes.

Portfolio Roll-up (Multi-Project)

The /portfolio/finance endpoint aggregates EVM data for all accessible projects in a single request : perfect for program management. It returns a consolidated totals object plus project-level details.

"One project per row" Table Query
let
    BaseUrl = "https://manifst.net/api/v1",
    ApiKey  = "mfst_live_YOUR_KEY",
    Source  = Json.Document(
        Web.Contents(BaseUrl, [
            RelativePath = "portfolio/finance",
            Headers = [ Authorization = "Bearer " & ApiKey ]
        ])
    ),
    Projects = Table.FromRecords(Source[data][projects])
in
    Projects

The consolidated roll-up block is accessible via Source[data][totals] (Σ BAC, portfolio CPI/SPI, total VAC…). To filter for specific projects, add Query = [ projects = "uuid1,uuid2" ].

EVM portfolio calculations include only projects with a declared BAC (Finance → Budget tab). Overall actual costs remain available via ac_all and engaged_all.

Incremental Refresh & Pagination

Large endpoints /tickets and /time-entries support pagination and date filtering passed in the query parameters:

ParameterRole
updated_sinceReturns items updated since a specific date (YYYY-MM-DD) : useful for incremental refresh.
page / per_pagePagination (default 100, max 500 per page). The meta.total property gives the true total count.
            RelativePath = "tickets",
            Query = [ project = "PROJECT_UUID", updated_since = "2026-07-01", per_page = "500", page = "1" ],
            Headers = [ Authorization = "Bearer " & ApiKey ]
Asking for page = "1" returns the first page only. Past per_page tickets the rest is missing from the report without a single error message : always compare the row count you loaded against meta.total. Either omit page and per_page (the API then returns the whole project at once, which is fine well below a few thousand tickets), or loop as shown below.
Every page : the loop stops at the first empty one
let
    BaseUrl = "https://manifst.net/api/v1",
    ApiKey  = "mfst_live_YOUR_KEY",
    PerPage = 500,
    Page = (n as number) as record =>
        Json.Document(
            Web.Contents(BaseUrl, [
                RelativePath = "tickets",
                Query = [
                    project  = "PROJECT_UUID",
                    page     = Text.From(n),
                    per_page = Text.From(PerPage)
                ],
                Headers = [ Authorization = "Bearer " & ApiKey ]
            ])
        ),
    Pages = List.Generate(
        () => [ n = 1, r = Page(1) ],
        each List.Count([r][data]) > 0,
        each [ n = [n] + 1, r = Page([n] + 1) ],
        each [r][data]
    ),
    Tickets = Table.FromRecords(List.Combine(Pages)),
    // tags is a list of records : expand it or drop it
    Clean   = Table.RemoveColumns(Tickets, {"tags"}, MissingField.Ignore)
in
    Clean
API Limits: 1,000 requests/hour and 10,000 requests/month per key. Rate limit status headers are included in every API response. A full refresh spends one request per table per project: for twelve projects (tickets, sprints, epics, finance, plus the project list and the roll-up) expect around fifty calls, leaving roughly six full refreshes per day. If that is too tight, refresh /portfolio/finance often : a single call : and keep ticket-level detail on a slower schedule.

Data Model

Every ticket carries the UUIDs of its project, epic and sprint. Those are the foreign keys of the model:

TableKeyRelationship
Projects (/projects)uuid1 : ∗ Tickets[project_uuid]
Epics (/epics)uuid1 : ∗ Tickets[epic_uuid]
Sprints (/sprints)uuid1 : ∗ Tickets[sprint_uuid]
Finance (/finance)project_uuid1 : 1 Projects[uuid]
The project_id, epic_id and sprint_id columns are internal identifiers kept for compatibility. Do not use them as keys: /projects and /sprints expose no id, so the relationships would close on nothing.

Remember to type the timestamps as well: they arrive in UTC as YYYY-MM-DD HH:MM:SS, and Power BI treats them as text until you convert them with Table.TransformColumnTypes.

Troubleshooting

SymptomCause & Solution
401 MISSING_API_KEYAuthorization header not sent. Use Web.Contents(BaseUrl, [Headers=…]) in Power Query M script.
401 INVALID_API_KEYKey invalid, revoked, or expired. Regenerate under Settings → API Keys.
403 INSUFFICIENT_SCOPEMissing required scopes (finance:read, roadmap:read…). Recreate key with appropriate permissions.
403 PLAN_REQUIREDREST API requires the Portfolio plan. Verify account subscription.
429 RATE_LIMIT_EXCEEDEDHourly or monthly quota exceeded. Respect Retry-After response header.
Works in Desktop but fails on scheduled service refreshDynamic data source error from GUI wizard. Use RelativePath and Headers parameters in M script.

Security

GitHub : HMAC Verification

GitHub requests are signed with HMAC-SHA256 via the X-Hub-Signature-256 header. Invalid signatures are rejected with HTTP 401.

If your secret key is compromised, regenerate it immediately under Settings → Integrations → GitHub → Regenerate secret, and update GitHub Webhook settings.

Slack : SSRF Protection

Only https://hooks.slack.com/services/… endpoints are accepted. Webhooks pointing to local or private IP spaces are rejected.

Discord : SSRF Protection

Only https://discord.com/api/webhooks/<id>/<token> URLs are accepted. Tokens must match [A-Za-z0-9_-] patterns.

Rate Limiting

The Test Connection button is rate-limited to 5 calls per minute per user per project.

Slack & Discord Circuit Breaker

If delivery fails 5 consecutive times, the connector is automatically disabled to prevent silent errors. Re-enable it once the webhook URL is fixed.

FAQ

GitHub webhook returns ignored: invalid_json

Set GitHub webhook Content type to application/json in GitHub repository Settings → Webhooks.

Webhook returns ignored: integration_not_configured

The GitHub integration has not been created or was deleted for this project. Set it up in Settings → Integrations.

Commits are linked (refs_linked: 1) but story status does not change

A push links commits for reference without altering story status. To close stories automatically, use a merged Pull Request with auto-close enabled.

I am not receiving Slack notifications

Can I connect multiple GitHub repositories?

A single GitHub connector per project is supported. For multi-repo setups, configure multiple GitHub webhooks targeting the same Manifst URL.

A teammate cannot see the "Development" panel

Connectors are included from the Team plan onwards, and the capability is assessed on their subscription : not the project owner's, and not the project itself. A member on the Starter plan therefore sees neither the panel nor the commit badges, even on a project whose connector works fine. Their own commits are still linked, and remain visible to members who are entitled to them.

Lost GitHub secret key

Secrets are shown only once upon creation. If lost, click Regenerate Secret in Manifst and update the value in GitHub.

Not receiving Discord notifications

Can Slack and Discord connectors be active simultaneously?

Yes. Both connectors function independently and send notifications simultaneously if configured for the same event.

Is the Discord token visible to non-owner project members?

No. Webhook URLs are masked in the API for non-owners (only the first 4 token characters are visible). Only project owners see the full URL.