Elliptic is built to sit in the middle of your work, not off to the side. Integrations let the tools your team already lives in feed work straight into your projects, and webhooks let Elliptic push its own events back out the moment they happen. The same surfaces are open to your agents over the company-brain MCP, so an agent can file, link, and react to work through these channels exactly the way a person does.
This page covers everything in the integrations layer: connecting Slack, GitHub, Sentry, and email so they create and close work for you, configuring per-project webhooks to Slack and Discord, and the event outbox that delivers signed HTTP webhooks to any endpoint you run. Most of these surfaces are admin-gated. Inbound endpoints are authenticated by a per-project token or a signed request, never by your login.
Slack
The Slack integration connects one Slack workspace to your organization. Once connected, you can post a meeting's summary into a channel with a single click, and your team can file work into Elliptic from Slack with a slash command. The connection is org-wide: one workspace per organization.
Connecting a workspace
Connecting Slack is an admin action. You complete a standard Slack OAuth handshake, and Elliptic exchanges the authorization code for a bot token. That bot token is never stored in plaintext. It is encrypted at rest with your organization's key-encryption key (the same AES-256-GCM custody that protects your BYOK provider keys), bound to your org id, so a leaked database row is useless on its own. Elliptic keeps the workspace's team id and team name alongside the encrypted token, and records who installed it.
After connecting, the integration status shows connected along with the workspace name. Re-running the connect flow updates the stored token in place rather than creating a second connection.
Listing channels
With a workspace connected, Elliptic can list that workspace's channels using the stored bot token. This is what populates the channel picker when you go to post a meeting, so you choose a destination by name instead of pasting a channel id.
Posting a meeting summary
From any meeting you can send its AI summary into a Slack channel. Elliptic assembles a single formatted message:
- The meeting title, in bold.
- The latest AI summary for the meeting, trimmed to keep the message compact.
- An Action items section listing up to ten items extracted from that summary.
- An embedded Ask about this meeting link, when the meeting has an active share.
The Ask link points at the meeting's public share page (/share/meetings/{token}), so anyone in the channel can open the meeting and ask questions of it without needing a Elliptic seat. The link only appears when a share exists and has not been revoked. Posting is recorded in the activity feed as a slack_posted event on the meeting, so the share is part of the meeting's history.
The /elliptic slash command
The slash command turns Slack into an intake surface. Anyone in the connected workspace can type /elliptic <work item title> and Elliptic files a triage item into a default project, then replies in the channel with the new item's identifier. It is the fastest way to capture a request without leaving the conversation it came up in.
/elliptic Investigate slow dashboard load for enterprise tenantsThe item lands as a triage task in the configured project's backlog, tagged with an intake channel of slack so you can tell at a glance where it came from. Elliptic replies in-channel with a confirmation like :white_check_mark: Created COS-214 — Investigate slow dashboard load.... If you send the command with no text, it replies privately with the usage hint.
Every slash-command request is verified before it does anything. Elliptic reads the raw request body exactly as Slack sent it and checks Slack's signature: it recomputes the v0 HMAC-SHA256 over v0:{timestamp}:{body} with your Slack signing secret and compares it in constant time. Requests whose timestamp is more than five minutes off from now are rejected outright, which blocks replayed requests. An invalid or stale signature is refused before any work item is created.
Setting the default project
The slash command needs to know where to file items. An admin sets the default project for Slack in the integration settings. Until a default project is set, the command replies privately telling the user that an admin needs to pick one, and if the configured project is later deleted the command reports that too. Setting the default project is restricted to owners and admins.
GitHub
The GitHub integration connects a repository to a project so that two things happen automatically: new issues become triage tasks, and pull requests or commits that reference a Elliptic task by its identifier link back to it and close it when they merge. It is set up per project, GitHub-first, and runs entirely through an inbound webhook, so Elliptic never needs write access to your repo.
Connecting a repository
On a project you add a connection by entering the repository's owner and repo. Elliptic mints a unique secret token for that connection and gives you a webhook URL to paste into your repository's settings under Settings, Webhooks:
https://elliptic.sh/api/v1/integrations/git/<token>Point GitHub at that URL and choose to send issue, pull request, and push events. Elliptic reads the X-GitHub-Event header to tell them apart. The token in the URL is what authenticates the repo to the right project, so treat it as a secret. A connection can be disabled or removed at any time, which immediately stops it from accepting events.
Issues become triage tasks
When an issue is opened or reopened, Elliptic creates a triage task in the connected project's backlog, carrying the issue title and body. The task is tagged with an intake channel of github and remembers the GitHub issue id, so the same issue never produces a duplicate. If GitHub sends a later event for an issue Elliptic already imported, it updates the existing task's title and description in place instead of creating another.
Pull requests and commits auto-link and auto-close
Elliptic scans the title, body, and branch name of a pull request, and the messages of pushed commits, for a closing keyword followed by a task identifier. The recognized keywords are close, closes, closed, fix, fixes, fixed, resolve, resolves, and resolved, and the identifier is your project key plus the task number, like COS-123.
Fixes COS-123: debounce the search input
Resolves COS-130 as well.For every identifier it finds, Elliptic adds a link from that task to the pull request or commit comparison, so the task page shows where the work happened. What differs between events is closing behavior:
| Event | Links the task | Closes the task |
|---|---|---|
| Pull request merged | Yes | Yes, moves the task to Done unless it is already in a completed status |
| Pull request opened or updated, not merged | Yes | No |
| Push (commits) | Yes | No |
So a PR that says closes COS-123 links the task as soon as Elliptic sees the PR, and only flips it to Done once the PR actually merges. Pushes and unmerged PRs link but never close, which keeps the board honest while work is still in flight.
Branch name suggestions
To keep the link working in the other direction, Elliptic can suggest a git branch name for any task. The suggestion is built from the task's identifier and a slugified title, for example cos-123-debounce-the-search-input. Branch off with that name, mention the identifier in your PR, and the auto-link and auto-close behavior above picks it up with no extra setup.
Sentry and email intake
Sentry and email intake share the same shape: a per-project, tokenized endpoint that turns an inbound payload into a triage item. You create the endpoint on a project, paste the URL into Sentry or your email provider, and matching events open work items in that project. The token in the URL is the only credential, so anyone holding it can post to that project. Rotate it by deleting the intake and creating a new one.
Sentry alerts
Create a Sentry intake on a project and add its URL to a Sentry alert rule's webhook action:
https://elliptic.sh/api/v1/integrations/sentry/<token>When Sentry fires the alert, Elliptic opens a triage bug in the project. It pulls the issue title, the culprit or transaction, and a link back to Sentry into the task, and it maps Sentry's level to a bug severity so triage is already prioritized:
| Sentry level | Bug severity |
|---|---|
| fatal | Critical |
| error | High |
| warning | Medium |
| info | Low |
| anything else | Medium |
Forwarded email
Email intake works the same way, for turning a forwarded email into a task. There is no mail server inside Elliptic. Instead you point your email provider's inbound-parse webhook at the intake URL, and the provider posts each email to it:
https://elliptic.sh/api/v1/integrations/email/<token>Elliptic opens a triage task using the email's subject as the title, and folds the sender and the message body into the description. Both intakes return the new item's identifier (like COS-318) in their response, and both tag the task with an intake channel of sentry or email so you always know how it arrived.
Project webhooks (Slack and Discord)
Project webhooks are the outbound half of Slack and Discord: instead of pulling work in, they post a formatted message to a channel whenever something happens in Elliptic. You subscribe a webhook to the exact events you care about, and Elliptic pushes a tidy Slack Block Kit message or Discord embed for each one. Managing webhooks is admin-gated, and they are scoped to a project (with a few organization-level events available too).
Subscribing to events
When you create a webhook you pick events from a catalog, grouped by domain. The catalog covers task, comment, note, meeting, project, organization, and team events:
| Domain | Events you can subscribe to |
|---|---|
| Tasks | created, updated, assigned, status changed, completed, deleted |
| Comments | added, updated, deleted |
| Notes | created, updated, deleted |
| Meetings | created, updated, deleted, summarized |
| Projects | updated, member added, member removed |
| Organization | member joined, member removed, role changed, invite created, org updated |
| Teams | created, member added, member removed |
Task, comment, note, meeting, and project events are scoped to the project the webhook belongs to. The organization and team events are org-wide, so a webhook on any project can subscribe to them. The task completed event is special: it fires whenever a status change lands the task in a completed status, so you can be notified about finished work without watching every status change.
The signing secret and the test message
Creating a webhook returns a signing secret exactly once. Elliptic uses it to sign the deliveries it sends, so the receiving side can verify the message genuinely came from Elliptic. Copy it when it is shown, because it is never displayed again. Every webhook also has a Test action that sends a sample message to the destination right away, so you can confirm the channel and formatting before you depend on it.
Each webhook tracks its last delivery: when it last fired, whether it succeeded or failed, and the error detail when it failed. The webhook list surfaces this as a delivery status badge, so a broken destination is obvious at a glance. You can enable, disable, edit, or delete a webhook at any time, and deleting one stops all deliveries to it.
The destination allowlist
Project-webhook destinations are deliberately limited. Only Slack incoming webhooks (on hooks.slack.com) and Discord incoming webhooks (on Discord's webhook hosts, under /api/webhooks/) are accepted, and only over HTTPS. Any other host, including localhost and cloud metadata addresses, is rejected when you save the webhook. This allowlist is what closes the server-side request forgery surface that an arbitrary outbound URL would open.
url_hint, the host plus the last four characters, enough to recognize which webhook is which without exposing the secret.The event outbox and generic HTTP webhooks
Underneath the Slack and Discord webhooks sits a general-purpose delivery backbone. As your organization works, Elliptic captures domain events into a durable event outbox. Generic HTTP webhook endpoints then subscribe to those events by type, and a scheduler drains the outbox and delivers each event to every matching endpoint with a signed request. This is the surface to use when you want to drive your own automation off Elliptic events rather than post into a chat tool.
Registering an endpoint
An admin registers a webhook endpoint on the organization with a destination URL, an optional list of event types to filter on, and a secret. If you do not supply a secret, Elliptic generates one. An endpoint with an empty event-type list receives every event; otherwise it receives only the event types you list. Endpoints are enabled by default and can be deleted when you no longer need them.
Signed deliveries
Each delivery is a JSON POST carrying the event id, event type, entity type, entity id, the initiator type, and any event data. Elliptic signs the body with HMAC-SHA256 using the endpoint's secret and sends the result in an X-Elliptic-Signature header, so your receiver can verify authenticity. Recompute the same HMAC over the raw request body and compare:
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody: string, signature: string, secret: string): boolean {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}X-Elliptic-Signature. The Slack and Discord project webhooks above use a different scheme: they sign {timestamp}.{body} and send sha256=<hex> alongside X-Elliptic-Timestamp and X-Elliptic-Event headers. Verify against whichever surface you are receiving from, they are not interchangeable.Retries and the dead-letter
Delivery is resilient. An endpoint that returns an HTTP error or is unreachable is retried, up to five attempts, with exponential backoff between tries (the wait doubles each attempt, in minutes). If an event still has not been delivered after five attempts, it is marked failed and moved out of the active queue, so a permanently broken endpoint never blocks the rest. A delivery counts as successful only when every matching endpoint accepts it.
An event whose endpoints all happen to filter it out is marked delivered immediately, with no HTTP call. That keeps the outbox from accumulating events that have nowhere to go.
The delivery log, manual dispatch, and retry
Elliptic keeps a delivery log of recent outbox events that you can filter by status: pending, delivered, or failed. Each entry shows the event type, attempt count, the last delivery error, and when it was created. From the log you can:
- Retry a dead-lettered event. This resets its failed flag and attempt count and queues it for another delivery, useful once you have fixed the receiving endpoint.
- Dispatch pending events on demand, instead of waiting for the next scheduled drain, when you want to flush the queue immediately.
Left alone, the outbox drains itself. A background scheduler runs about once a minute, finds every organization with due, undelivered events, and dispatches them. Delivery is locked per row so two drains never send the same event twice, and the whole loop is idempotent: re-running it only touches events that have not been delivered yet.
Related
Integrations bring outside tools to Elliptic through fixed connectors. For installable apps, agents, importers, and outbound MCP connectors that let your agents reach other systems, see the Marketplace & connectors page. For how inbound items land as triage and how you accept or decline them, see Projects & Tasks.
