Asks: Multichannel Intake Guide
Asks lets anyone file a request into an Ithura project from Slack, email, or a public web form, without an Ithura account. Every request becomes a normal intake issue (triage state, same activity feed, same comment thread), and updates flow back to the submitter on the channel they came from.
| Channel | Inbound | Outbound (back to submitter) |
|---|---|---|
| Slack | Emoji reaction or @bot mention files the message as an issue; thread replies become issue comments | Confirmation + replies to the submitter post into the same Slack thread |
Mail to <local-part>@<inbound-domain> creates an issue; replies append as comments | Confirmation + replies to the submitter arrive as email replies in the same thread | |
| Web form | Public form submission creates an issue | Confirmation email + a tokenised, no-login status page |
Only updates the team explicitly shares reach the submitter. See Privacy: what the submitter sees below.
How it works
Every intake request gets an intake_threads row: the channel, a
channel-native thread anchor (external_ref), the submitter's email/display
name, and an opaque reply_token. That row is what routes replies in both
directions. Submitters are never given accounts; issues and comments they
produce carry NULL author fields plus an inline attribution label.
Comments arriving FROM a channel are inserted directly and never re-relayed, so there are no echo loops. Slack bot posts are likewise ignored on the inbound side.
Privacy: what the submitter sees
Comments have an access level: INTERNAL (the default) or EXTERNAL. This is
the boundary between private triage and what reaches the person who filed the
request.
- Internal by default. Every ordinary comment is
INTERNAL. Internal triage discussion stays inside Ithura. It is never emailed, never posted to the Slack thread, and never shown on the public status page. - External updates are opt-in. Only a comment explicitly marked
EXTERNALis relayed back to the submitter (as a Slack thread reply and/or an email reply) and listed on their status page.
Triagers post an external update from the inbox detail using the Reply to submitter box (see Triage: replying to the submitter). The confirmation message the submitter first receives is always sent; it is the only automatic outbound message.
Triage: replying to the submitter
Open a request in the project inbox to triage it. The detail panel gives you everything you need to answer the submitter without leaving Ithura.
- Reply to submitter box: type an update and post it. This creates an
EXTERNALcomment, so it is the one kind of comment that goes back out. The box states where the update will land (emailed to the address on file and/or posted to the Slack thread, plus the status page). Regular comments elsewhere on the issue remain internal. - From: for email and form requests that captured an address, the submitter
email shows here as a
mailto:link. - View in <channel>: Slack-sourced requests show a clickable button that opens the original Slack thread (see Provenance).
- Attachments: files that arrived with the request render with their real name and size, and are downloadable.
The same View in Slack provenance button also appears on the full issue detail panel, not just the inbox.
Provenance: tracing a request to its source
Slack-sourced issues expose a source_url that deep-links to the original Slack
thread. It is built from the captured channel:thread_ts anchor stored on the
intake thread, so triagers can jump straight to the conversation the request
came from. The link renders as a View in Slack button in both the inbox
detail and the issue detail panel. Email and form requests have no such link.
Attachments
Attachments from any channel (email parts, Slack files relayed on capture, and
web-form _attachments) are stored as normal issue attachments with their real
filename, content type, and byte size (each capped at 5 MB). They appear in the
inbox detail's attachment list with a download action, so a triager sees exactly
what the submitter sent.
1. Slack
Asks intake rides on the same Slack integration as channel sync. Once Slack is enabled for your instance and connected to your workspace (see the Slack guide), turn on intake per project below.
Project setup
Project settings → Integrations → Slack: pick the channel (existing outbound
sync), then flip on Asks intake and optionally change the trigger emoji
(default :ticket:). Intake maps that channel to this project.
Using it
- React with the trigger emoji on any message in the synced channel: the message becomes an intake issue (first line = title, full text = description). The bot replies in the thread with an issue link and an "Edit details" button (title + priority modal). If the underlying issue has since been deleted, clicking "Edit details" opens a short "Request unavailable" modal instead of silently doing nothing.
- @mention the bot with a request: same, using the mention text. Mentioning the bot inside an already-captured thread adds a comment instead.
- Thread replies into Ithura: Slack replies in a captured thread become issue comments labelled "Reply from <name> (Slack)".
- Replies back to Slack: only an update the team explicitly shares (an
EXTERNALcomment, posted from the inbox "Reply to submitter" box) is posted into the thread as*<name>*: <text>. Internal comments stay in Ithura.
A second reaction on the same message never opens a duplicate: the thread anchor is unique per message.
2. Email
Once inbound email is enabled for your instance, each project gets its own intake address that turns mail into issues.
Project setup
Project settings → Email-in: create an address. Auto-generated as
intake-<id>@<domain>, or pick a custom local part
(support-payments@<domain>). Each address targets one project (and
optionally a specific intake).
Using it
- Send or forward mail to the address: subject → title, body → description, attachments (up to 5 MB each) are stored on the issue with their real filename and size.
- The submitter gets a confirmation email. Its
Message-IDencodes the issue, so any reply (even months later, even with a truncated References chain) lands as a comment on the right issue. Reply matching runs three passes: our synthesised Message-IDs, the thread's recorded notification ID, then any previously seen inbound Message-ID. - Updates the team shares (
EXTERNALcomments, posted from the inbox "Reply to submitter" box) are emailed to the submitter as replies in the same thread ("<name> replied: …", plus the status link). Internal comments are not emailed. - Duplicate webhook deliveries dedupe on
(address, Message-ID).
3. Web form
Project setup
Project settings → Forms: create a form with a slug, title and fields
(text, textarea, email, number, single_select, multi_select,
checkbox, url, date). The public page is served at:
https://<web-host>/forms/<workspace-slug>/<form-slug>
Embed anywhere with one line:
<iframe src="https://<web-host>/forms/<workspace-slug>/<form-slug>" width="100%" height="720" style="border:0" title="Submit a request" loading="lazy"></iframe>
Using it
- Submissions create an intake issue (title/description mapped from conventional field keys, full payload preserved on the intake record).
- If the form has an
email-typed field and the visitor fills it, they get a confirmation email with a "View your request" link:https://<web-host>/asks/status/<reply-token>(a read-only, no-login status page). The link is also shown on the form's thank-you screen. - Optional attachments: POST a
_attachmentsarray of{filename, content_type, data(base64)}(5 MB per file cap). Each is stored on the issue with its filename and size. - Abuse guards: honeypot field, 10 submissions/IP/hour.
The status page
The no-login status page (/asks/status/<reply-token>) is keyed on the opaque
reply token, so it works for any intake thread that carries one, not just forms.
It shows:
- the request title and current state (with a colour dot for progress),
- What you sent: the submitter's own submitted message,
- Updates from the team: a timeline of
EXTERNALcomments only, each with author and timestamp, - submitted and last-updated timestamps.
Internal triage comments never appear here. The page has no authentication and can be revisited anytime.
Endpoint reference
| Endpoint | Auth | Purpose |
|---|---|---|
POST /webhooks/slack/events/ | Slack signing secret | Events API ingress |
POST /webhooks/slack/interactivity/ | Slack signing secret | Edit-details modal |
POST /webhooks/email-in/ | X-Inbound-Token | Inbound email ingress |
GET /public/forms/{ws}/{form}/ | none | Form schema |
POST /public/forms/{ws}/{form}/submit/ | none (rate-limited) | Form submission |
GET /public/asks/status/{token}/ | reply token | Submitter status page |
GET/POST/DELETE …/slack/sync/ | project admin | Channel + intake toggle |
GET/POST/PATCH/DELETE …/email-in/ | project member | Inbound addresses |
…/forms/ CRUD | workspace admin | Form management |
Smoke verification checklist
- React with the trigger emoji on a Slack message → issue appears in the project within ~5 s; threaded confirmation with issue link visible.
- Post an update from the inbox "Reply to submitter" box (an EXTERNAL comment) → it appears as a Slack thread reply. A plain internal comment does not.
- Email with two attachments to the intake address → issue created, attachments listed on the issue.
- Reply to the confirmation email → reply appears as an issue comment.
- Submit the public form with an email → issue created; confirmation email contains a status URL that opens without login.
- Confirm none of the three submitters exist as workspace members.
Automated coverage: internal/handlers/asks_intake_test.go (email reply
threading, form thread + status page, Slack signature gate, full
reaction→issue→reply flow against a fake Slack API) and
asks_intake_unit_test.go.