SnagBane — Standalone forms for Microsoft 365 & Google

Standalone: SnagBane runs on your own server and replaces Microsoft Forms and Google Forms without depending on them — forms, answers and files stay in your database. The free Community edition is the complete app (builder with branching and calculations, portals, approvals, workspaces, email, webhooks, Power Automate / Zapier / Make / n8n and most integrations) and needs no Microsoft or Google account. Enterprise connects Microsoft 365, Google Workspace and Okta: sign-in, live directory person pickers and prefill, groups, Teams apps, SharePoint, Excel, Google Sheets and Drive, and Outlook / Gmail mail. Sections about Microsoft 365, Google Workspace and Okta apply to Enterprise.

Requirements

ComponentMinimum
PHP8.3 or newer with extensions: pdo, mbstring, openssl, tokenizer, xml, ctype, json, fileinfo, curl
DatabaseMySQL 8 / MariaDB 10.6+ (recommended), PostgreSQL 14+, or SQLite 3.35+
Web serverApache (with mod_rewrite) or Nginx, with HTTPS (required by Microsoft for sign-in redirect URIs other than localhost)
Outbound HTTPSTo login.microsoftonline.com and graph.microsoft.com for Microsoft 365 features

The download includes vendor/ and pre-built front-end assets. You don't need Composer or Node.js.

Install on cPanel / shared hosting

  1. Create a MySQL database and user in cPanel → Databases → Manage My Databases (cPanel 118 and earlier: MySQL® Databases): under Create New Database create the database; under Add New User create a user (use Password Generator and copy the password); under Add User To Database add the user with ALL PRIVILEGES. cPanel puts your account name in front of both names (e.g. youraccount_snagbane). The installer shows these steps too. To change the password later, change it in cPanel (Current Users → Change Password) and put the same password in DB_PASSWORD in .env, or run php artisan snagbane:db-password, which changes both.
  2. Upload snagbane.zip with File Manager and extract it, for example into /home/you/snagbane.
  3. Point your (sub)domain's document root to /home/you/snagbane/public. If your host doesn't allow that, move the contents of public/ into public_html/ and fix the two paths in public_html/index.php to point at ../snagbane/.
  4. Open https://your-domain/install and follow the three steps: requirements, database, admin account.
  5. Add one cron job in cPanel → Cron Jobs (every minute): cd /home/you/snagbane && php artisan schedule:run >/dev/null 2>&1 — approval reminders, the nightly directory copy for fast person search and clean-up run from it.
  6. Sign in and go to Administration → Microsoft 365.

Install on a VPS (Ubuntu + Nginx)

sudo apt install nginx php8.3-fpm php8.3-{mysql,mbstring,xml,curl,zip,intl,sqlite3} mariadb-server unzip
sudo unzip snagbane.zip -d /var/www/snagbane && sudo chown -R www-data: /var/www/snagbane
# nginx: root /var/www/snagbane/public; try_files $uri $uri/ /index.php?$query_string;
sudo certbot --nginx -d forms.example.com
echo '* * * * * www-data cd /var/www/snagbane && php artisan schedule:run >/dev/null 2>&1' | sudo tee /etc/cron.d/snagbane

Then browse to /install.

Install with Docker (recommended)

The stack runs SnagBane (Apache + PHP 8.3), a background worker for emails / webhooks / integrations, a scheduler (approval reminders, clean-up), MariaDB and Mailpit for testing email.

docker compose up -d --build
# open http://localhost:8080/install
#   Database: MariaDB · host db · database snagbane · user snagbane · leave the password empty
# test inbox for all outgoing email: http://localhost:8025 (in Admin → Email use SMTP host "mailpit", port 1025, no encryption)

No default database passwords. During installation SnagBane replaces the database's starting password with a strong random password for this installation and keeps it in storage/.env (in the storage volume). MariaDB's root password is random too (SnagBane doesn't need it; it is printed once: docker compose logs db | grep "GENERATED ROOT PASSWORD"). See the password with docker compose exec app php artisan snagbane:db-password --show; change it with docker compose exec app php artisan snagbane:db-password (random, or --password=…), then docker compose restart.

Set SNAGBANE_PORT (default 8080) and SNAGBANE_MAIL_PORT (Mailpit, default 8025) in a .env file next to docker-compose.yml if those ports are taken, e.g. by a second copy of SnagBane. Everything that must survive upgrades lives in the storage and dbdata volumes; the exports/ folder receives Local folder exports. Upgrading: replace the files, then docker compose up -d --build — migrations run automatically.

Run locally on Windows (for testing, demos and recording videos)

The easiest way is Laragon (free):

  1. Install Laragon Full, which includes PHP 8.3, MySQL and Apache.
  2. Copy the SnagBane folder into C:\laragon\www\snagbane.
  3. Start Laragon. It serves the site at http://snagbane.test.
  4. Open http://snagbane.test/install, pick SQLite and tick Import demo forms and responses.

You can also double-click start-local.bat if PHP is on your PATH. It serves at http://127.0.0.1:8000.

Microsoft allows http://localhost redirect URIs for sign-in, so SSO works locally if you open SnagBane at http://localhost:8000.

Updating

One click: Administration → System → Updates. SnagBane checks for a new version once a day (or click Check for updates); the Owner clicks Update now. SnagBane then:

  1. downloads the new version and checks its digital signature (a package that isn't signed by SnagBane is refused);
  2. saves a backup of the current version (Administration → System → Restore previous version undoes the update);
  3. shows a short maintenance page, copies the new files and updates the database. Files that the new version no longer has are removed from SnagBane's own code folders (app, config, enterprise, lang, resources, routes, vendor, public/build, public/docs and database/migrations, seeders, factories), so leftovers can't break it; your own files elsewhere (for example in public/) stay. Your .env, storage/, uploads and data are never touched.

Connect your Microsoft 365 tenant

Go to Administration → Microsoft 365 and choose the features (sign-in is always on; tick Person picker & profile prefill, and optionally groups and mail). SnagBane needs an app registration in your own tenant. The recommended way creates it with Microsoft's own tooling under your admin account — nothing passes through a third party.

PowerShell (recommended): the PowerShell tab shows New-SnagBaneApp.ps1 for the features you ticked (copy or download it so your security team can review it). Run it in Windows PowerShell or PowerShell 7 as a Global Administrator (or Application Administrator + Privileged Role Administrator). It needs the Microsoft.Graph module (Install-Module Microsoft.Graph -Scope CurrentUser), signs you in with your normal browser (MFA, Security defaults and Conditional Access work), creates the SnagBane Forms app registration, a 24-month client secret and admin consent, and prints three values. Paste them into the Manual tab and click Save & test.

Manual (Entra admin center): create an app registration yourself with the redirect URI shown in the wizard, add a client secret, add the Microsoft Graph permissions listed there and click Grant admin consent. Then paste the tenant ID, client ID and secret into SnagBane and click Save & test.

SnagBane only stores the app's tenant ID, client ID and client secret, and the secret is encrypted with your APP_KEY.

Other ways: Sign in with Microsoft

Under Other ways, SnagBane can create the app itself after a one-time admin sign-in through Microsoft's public Microsoft Graph Command Line Tools client:

  1. Sign in with Microsoft. A new tab opens Microsoft's normal sign-in page. Sign in as a Global Administrator (or Application Administrator + Privileged Role Administrator) and accept the permissions.
  2. Paste the address. The tab ends on an error page such as “This site can’t be reached” (address http://localhost:1/?code=…) — that is expected: the browser never connects, so nothing on your computer receives the code. Click its address bar, copy the whole address (Ctrl+L, Ctrl+C; on a Mac ⌘L, ⌘C), paste it into SnagBane and click Continue. The address contains a one-time sign-in code; the full address or just the part after ? both work.
  3. Create the app. SnagBane then creates the SnagBane Forms app registration and enterprise app, adds a 24-month client secret, and grants admin consent for exactly the permissions you selected. You see each step as it completes.

The admin's sign-in token is used once and then thrown away. SnagBane only stores the new app's tenant ID, client ID and client secret, and the secret is encrypted with your APP_KEY.

How it works: the sign-in uses Microsoft's first-party Microsoft Graph Command Line Tools client with the authorization-code flow and PKCE (no client secret). That client only accepts Microsoft's registered return addresses (such as http://localhost), not your server's, which is why you copy the address instead of being sent back automatically. The code is useless without a secret that never leaves your SnagBane server, works once, expires after a few minutes and only for the administrator who started the sign-in.

Why not device code? The Device code tab (under Other ways) still exists, but Microsoft now blocks device-code sign-in in most tenants: Security defaults (on by default in new tenants) block it with error AADSTS530035, and the Microsoft-managed Conditional Access policy Block device code flow does the same (AADSTS53003). SnagBane recognises these errors and sends you to Sign in with Microsoft.

Permissions explained

FeaturePermissionTypeWhy
Sign in with Microsoftopenid, profile, email, offline_access, User.ReadDelegatedSign users in and read their own profile
Person picker & prefillUser.Read.AllApplicationSearch the directory and read job title, department and manager
GroupsGroupMember.Read.AllApplication"Specific groups" form access and mapping the admin group
MailMail.SendApplicationSend notifications from a mailbox. Restrict this with an application access policy.
SharePoint / ExcelSites.ReadWrite.All, Sites.Manage.AllApplicationSave responses to SharePoint lists (Manage: create lists and columns) and files
SharePoint / ExcelFiles.ReadWrite.AllDelegatedExcel rows, written as the connected Excel account (see below)

Excel: the connected account

Microsoft's Excel (workbook) API only works for a signed-in account — it has no application permissions — so, like a Power Automate connection, an administrator connects one account under Administration → Microsoft 365 → Excel account → Connect Excel account (a normal Microsoft sign-in through your own SnagBane app registration). Use a service account (for example forms-bot@company.com) and give it Can edit on the workbooks you use. SnagBane keeps its refresh token encrypted; if the sign-in expires or is revoked (password reset, the account is disabled), the Excel integration says so and you connect again.

In a form's Integrations tab, add Excel, paste the workbook's sharing link (Share → Copy link) and a table name. If the table doesn't exist SnagBane creates it on a new sheet with a column per question, adds missing columns later, and writes one row per response. Answers that start like a formula (=, +, -, @) are written as text.

Sign in with Microsoft

Once connected, the login page shows Sign in with Microsoft. With Create accounts automatically on, anyone in your tenant gets an account with the default role when they first sign in. Pick an Admin group and its members become SnagBane admins each time they sign in.

Connect Google Workspace

Go to Administration → Google Workspace and tick the features you need. Google does not allow apps to create OAuth clients for you, so the page gives you a short checklist with every value ready to copy:

  1. Sign in with Google: create an OAuth client (Web application) with the origin and redirect URI shown, paste its ID and secret. Set “Restrict to domain” to your Workspace domain.
  2. Directory person picker & prefill: create a service account, upload its JSON key, then in the Workspace Admin console add domain-wide delegation for its client ID with the read-only directory scopes shown. Enter an admin email to impersonate.
  3. Gmail (optional): add the gmail.send scope and choose “Google Workspace (Gmail API)” in Admin → Email.
  4. Google Sheets: just share each spreadsheet with the service account's email.

Click Save & test — each part is checked and any problem is explained. If Microsoft 365 is also connected, choose which directory person pickers use under Admin → Security.

Sign in with Okta

Administration → Okta. In the Okta Admin Console create an app integration (OIDC – OpenID Connect, Web Application, grant type Authorization Code) with the sign-in redirect URI shown on the page, assign it to your people, then paste the Okta domain (e.g. company.okta.com), client ID and secret and click Save & test. Leave Authorization server empty to use the org server, or enter e.g. default for a custom one.

Job title, department & manager

Every account has a job title, department and manager. They are shown in Administration → Users & roles (search finds them too), used for Manager approval steps and to prefill forms.

Sign-in methods & passwords

The sign-in page always offers email + password, plus a button for each single sign-on that is set up and switched on: Microsoft, Google, Okta. A person is linked to a provider the first time they sign in with it (matched by email).

Paste your questions (fastest start)

Click New form → Paste your questions and paste a list — from Word, an email or an old paper form. One question per line; SnagBane picks the type from the wording (email, date, number, rating, person, file, long answer…) and you can change any type in the preview before creating the form.

Form builder

Click a question type on the left (or drag it onto the canvas), then edit it in the right-hand panel. Drag the handle to reorder, or hover a question and click the round + below it to insert a new one right there. Everything saves automatically. Turn on Live preview (wide screens) to try the form on desktop or mobile while you build — logic, branching and validation work, nothing is saved. Less-used options (show-if conditions, placeholders, length limits, prefill, variable names) are under Show advanced options in the question panel. If two people (or two browser tabs) edit the same form, nothing is overwritten silently: the later save stops and asks whether to load the newest version or keep yours. The available types are short and long answer, email, number, phone, website, single choice, multiple choice, dropdown, yes/no, star rating, scale/NPS, date, time, file upload, hidden field, person picker, heading, text block and page break.

Person picker & Microsoft 365 prefill

A Person picker question searches your tenant as the respondent types. It matches display name, email and UPN, and shows job title and department. Turn on Allow multiple people to collect several.

Prefill from Microsoft 365 fills a question from the signed-in respondent's Entra ID profile. You can use the respondent, their manager, display name, email, job title, department, office, mobile or employee ID. Respondents can still change the value.

Before you connect a tenant, SnagBane uses a built-in demo directory (Contoso users), so you can try everything first.

Fast person search. SnagBane keeps a small local copy of the directory (enabled users only: name, email, job title, department, office, manager), so the picker answers in milliseconds even in tenants with tens of thousands of people and never hits Microsoft's rate limits. It refreshes every night (02:30) and catches up hourly if a run was missed; Administration → Microsoft 365 → Fast person search shows the status and has Sync now. From the command line: php artisan snagbane:directory-sync. Until the first sync finishes, searches go live to Microsoft Graph / Google as before. Needs the scheduler (cron * * * * * php artisan schedule:run) — already running in the Docker stack.

Conditional logic

Enable Conditional logic on any question to show it only when earlier answers match. For example, show "Business justification" when "Estimated cost" is greater than 500. Hidden questions are never validated or stored, and the rule is enforced again on the server.

Branching (side-by-side columns)

Give each answer its own questions. Select a single-choice, dropdown or yes/no question and click Add branching. Lines go out from the question into columns side by side, one per answer, like a switch in Power Automate, and join again below: the questions under the columns are the shared part everyone answers. You see the whole logic at once, without scrolling up and down.

Example: “Leave type” — Annual leave (no extra questions), Sick leave → “From” and “To”, Unpaid, Other → End the form; then everyone else answers “Who covers for you?” and “Manager”.

An unanswered branching question (optional, or hidden by a condition) skips all columns. Answers from columns the respondent didn't take are never saved. Forms made before columns existed keep their “go to” menus and keep working; when the branching means the same thing as columns (at most one later target), the question card offers Show as columns. Columns made with 2.10 stay sections of their own. Branching inside a column is not available yet.

For more complex forms:

Answers to skipped questions and sections the respondent never visited are discarded, and the server recalculates the path, so hidden questions can't be forced in.

Calculations & answer piping

Add a Calculation question and write a formula such as {qty} * {price} or SUM({items.amount}). Supported: + - * / %, comparisons, AND/OR, SUM AVG MIN MAX ROUND FLOOR CEIL ABS COUNT IF CONCAT DAYS TODAY. Give choice options a numeric value to score them. Totals update live for respondents and are always recomputed on the server.

Piping: put {{variable_name}} in any question, description, confirmation or receipt message to insert an earlier answer.

Repeating tables, grids & signatures

Quiz mode

In Settings → Quiz mode turn on grading, then mark correct answers (✓ next to options, or accepted text answers) and points per question. Respondents can see their score immediately; results show the average score, a distribution and the % correct per question. Answer keys are never sent to the browser.

Approval workflows

Open a form's Approvals tab and add stages. Each stage's approver can be the person chosen in a question, the respondent's manager from Microsoft 365 / Google Workspace, specific people, or email addresses. Choose whether any one or everyone must approve, add reminders and optional conditions (e.g. only send to Finance when the total is over 500).

Approvers get a branded email with Review & approve and Reject buttons (single-use, secure links) and also see requests under Approvals in SnagBane. Uploaded files and signatures of the request open straight from the approval page or the inbox, even for approvers who have no access to the form itself. The requester is emailed the outcome, and every step is recorded. If a form lets respondents edit after submitting, a rejected request can be corrected with the edit link and then goes through approval again from the first stage. Events approval.requested, approval.approved and approval.rejected can trigger integrations and webhooks.

How approvals work, step by step

  1. Someone submits the form → the response gets status pending and the first stage starts (stages whose condition doesn't match are skipped).
  2. The stage's approver(s) get an email with single-use Approve/Reject links and see it in Approvals. With “any” one decision completes the stage; with “all” everyone must approve. Rejecting requires a comment.
  3. Approved → next stage; after the last stage the response is approved. Any rejection ends it as rejected. The requester is emailed the outcome and sees it live under My requests on a portal.
  4. Change approver: when an approver is away or doesn't answer, an administrator (or the owner of the form's workspace) hands the waiting approval to someone else — Dashboard → Waiting longest / Overdue approvals, or the request under Results → Approvals. The new approver is notified right away, the old link stops working, and the audit log keeps who changed it, from whom to whom and the optional note. The requester and deactivated accounts can't be picked.
  5. Overdue approvers get reminders (due days per stage). If an approver can't be resolved (no manager in the directory, empty person question) the task goes to the form owner, so nothing is auto-approved by mistake.
  6. The requester can't approve their own request (a switch per stage, on for new stages): if the requester picks themselves, or is one of the approvers, the task goes to the other approvers; if nobody is left, to the requester's manager, else the form owner, or another administrator when the owner is the requester.
  7. Each step fires events for Power Automate, Teams, Slack and webhooks — e.g. create the laptop order only after approval.approved.

Phone app (install SnagBane) — Enterprise

SnagBane can be installed as an app on phones, tablets and computers — straight from your own SnagBane site, no app store and nothing sent to us. The app opens without the browser bars, shows your organisation's name and colour, and can notify approvers when a request is waiting for them.

  1. Open SnagBane in the browser on the phone and sign in. Under Approvals you see Approvals on your phone.
  2. Install: tap Install app where the browser offers it (Chrome, Edge on Android and desktop). On iPhone and iPad, add SnagBane to the Home Screen from Safari and open it from there.
  3. Notifications: tap Turn on notifications and allow them. Each device is turned on separately; turn it off with the same button.

Workspaces, roles & groups

Workspaces are spaces for teams or departments. Every form, portal and workspace-wide integration belongs to one workspace; members see only their workspaces. Use the switcher at the top of the sidebar to work in one workspace or see all. Create one under Workspaces → New workspace (name, colour, icon, then members).

Upgrading from 2.3 or earlier: existing forms, portals and integrations move into a workspace called “General”, and every active administrator becomes an Owner, so nobody loses access. Editors keep seeing their own forms and those shared with them; add people to workspaces when you're ready.

Portals

A portal is one page that lists several forms — like an HR or IT service desk. Open Portals → New portal, pick a look, tick the forms to include and click Create portal. There are two looks (switch any time under Design → Look):

In the editor:

Company portal (Enterprise, administrators): at the top of Portals, click Set up. You get one page for the whole company, in the Modern look: every workspace is a department (IT, HR, Finance…) and its published forms appear by themselves — no picking forms by hand. Everyone sees only the forms they may open, and My requests covers every department. Under Content → Departments hide a workspace or change the order; notices, FAQ and contacts you add appear under the departments. Visitors can pin their own department (the pin next to it) so it opens first for them. Publish it and add it to Microsoft Teams as the company tab (Microsoft Teams in the editor). In the Community edition the card shows what the Enterprise edition adds.

Press Publish and share the link or put it on your intranet/Teams tab. Editors see and manage their own portals; admins see all. Draft portals and draft forms are only visible to their editors.

Page builder: in the portal editor's Content tab, add blocks with + or Add block — form categories, text (supports **bold**, *italic*, [links](https://…) and “- ” lists), images, buttons, info cards, FAQ, notices, contacts and dividers. Reorder, duplicate, and give each block a background.

Own domains for portals

Administration → Domains → add e.g. hr.company.com and choose the portal. Then connect it:

Links SnagBane sends out (approval emails, notifications, webhooks) always use APP_URL, or the custom domain the person is on — never an address taken from an unverified request, so they can't be spoofed.

Editions & licence

Community is free and complete on its own — for organisations that don't use Microsoft 365 or Google Workspace, or simply don't need them. Enterprise connects SnagBane to Microsoft 365, Google Workspace and Okta, and adds the phone app, the REST API, Power Automate / Zapier and the premium connectors. In Community these show with an Enterprise badge, so you can see what you would get.

Community (free)Enterprise (licence)
Forms, builder, logic, calculations, quizzes, files, signatures, translations✓✓
Workspaces, roles, manual groups, approvals (all stages), portals, own domains✓✓
Sign-inSnagBane accounts, two-step verification+ Microsoft, Google and Okta sign-in
Person picker & prefillfrom SnagBane usersfrom Entra ID / Google Workspace, with fast local directory copy
Groups from Entra ID / Google / Okta—✓
Portals as Microsoft Teams apps (Teams sign-in)website tab only✓
EmailSMTP, SendGrid, Mailgun, Postmark, Brevo, Resend, Amazon SES+ Microsoft 365 mailbox, Gmail
IntegrationsMake, n8n, signed webhooks, HTTP request, Discord, Telegram, Mattermost, Dropbox, Notion, Airtable, CRM & newsletter tools, email, local export+ Microsoft Teams, Slack, Amazon S3 / R2 / Wasabi / MinIO, SharePoint, Excel, Google Sheets, Drive, Chat
Power Automate & Zapier automations, Power Automate custom connector—✓
REST API & personal API tokens—✓
Phone app with notifications for approvers—✓
Company portal (every workspace as a department, for a Teams tab)—✓
White-label (hide “Powered by SnagBane”)—✓

Community download → Enterprise: the Community download does not contain the Enterprise module. Activate your licence (below), then Administration → System → Updates → Get SnagBane Enterprise installs it over your copy; your data and settings stay.

Activate: Administration → License, paste the key from your purchase email or your Freemius account, click Activate. The server needs outgoing HTTPS to api.freemius.com and to the SnagBane licence and update service snagbane-updates.markostepanovic.workers.dev; SnagBane re-checks the licence once a day. Deactivate on this server frees the licence so you can use it on another server.

Portals in Microsoft Teams

Open a portal in the editor and click Microsoft Teams — the same two-option guide as below is built in, with your links pre-filled. Both options need SnagBane on a public HTTPS address (Teams never loads http://). For a quick test on your PC, a tunnel such as Cloudflare Tunnel or ngrok gives you an https address; set it as the site URL.

Option 1 — Teams app (recommended)

The portal becomes an app with its own icon in the Teams app bar, can be added to any channel or chat, and people are signed in automatically with their Teams account (“My requests”, organisation-only forms and manager prefill all work).

  1. Download the app package: portal editor → Microsoft Teams → Teams app → Download Teams app (.zip). The zip holds the manifest (name, icon in the portal colour, address) — nothing to edit.
  2. Upload it: a Teams administrator opens admin.teams.microsoft.com → Teams apps → Manage apps → Upload new app and selects the zip.
  3. Make it available: check that the app is Allowed. To pin it for everyone: Teams apps → Setup policies → Global → Pinned apps → Add apps.
  4. Use it: people open it from Apps (or the pinned icon). To add it to a channel: open the channel → + → search for the app → Save.
  5. Just testing? Without admin rights: Teams → Apps → Manage your apps → Upload an app → Upload a custom app (if your organisation allows custom apps).

Automatic sign-in (Teams single sign-on) — Admin → Microsoft 365 → Microsoft Teams sign-in → Enable Teams sign-in. A Global Administrator signs in once more with Sign in with Microsoft (the same copy-the-address steps as the setup); SnagBane then adds “Expose an API” (api://<your-host>/<client-id>, scope access_as_user, pre-authorised for Teams, Microsoft 365 and Outlook) to its existing app registration. New installations on HTTPS get this automatically during the Microsoft 365 setup. Download the package again afterwards (it then contains the single sign-on block) and choose Update in Teams Admin Center. Without single sign-on the app still works: people click Sign in once and sign in in a pop-up (Microsoft, Google or email).

Manual / PowerShell setups: in Entra ID → App registrations → SnagBane Forms → Expose an API, set the Application ID URI to api://<your-host>/<client-id>, add a scope access_as_user (admins and users), and add the client applications 1fec8e78-bce4-4aaf-ab1b-5451cc387264 (Teams desktop/mobile) and 5e3ce6c0-2b1f-4285-8d4b-75ee78787346 (Teams web) for that scope. Then run Enable Teams sign-in once or set it on in Admin → Microsoft 365.

Updates: changes to forms, categories and design appear in Teams immediately. Only a new portal name or colour needs a new package (Update in Teams Admin Center).

Option 2 — Website tab (no admin needed)

  1. Portal editor → Microsoft Teams → Website tab → Copy the tab link (it ends in ?teams=1).
  2. In Teams, open the channel → + (Add a tab) → Website → give the tab a name, paste the link → Save.
  3. The portal is a tab for everyone in the channel; forms open inside it with a Back to the portal link.

Limits: Teams doesn't allow signing in inside a website tab, so use it for portals set to Anyone with the link. “My requests” and organisation-only forms need the Teams app (Option 1).

Technical notes

Save & resume, editing and receipts

Access control

ModeWho can respond
Anyone with the linkPublic, no sign-in
My organisationAnyone who signs in with your Microsoft 365 tenant
Specific people & groupsSelected users, or members of selected Entra ID groups (nested groups included)

You can also allow one response per person, make responses anonymous, set open and close dates, and cap the number of responses. Open and close times are in the organisation's timezone (Administration → General).

People who may not open a form (not signed in, not on the list, form closed or not published) see only its title and description — the questions are not sent to their browser.

Responses & export

The Results page has a summary (charts per question, NPS score, 30-day trend) and a searchable table. Click a row to see the full response with the respondent's department and manager. Export CSV produces an Excel-friendly UTF-8 file.

Languages & multilingual forms

SnagBane ships in 33 languages: English, Español, Français, Deutsch, Italiano, Português (Brasil), Português (Portugal), Nederlands, Polski, Čeština, Slovenčina, Magyar, Română, Български, Ελληνικά, Srpski (latinica), Српски (ћирилица), Hrvatski, Slovenščina, Українська, Русский, Türkçe, Svenska, Dansk, Norsk bokmål, Suomi, العربية (right-to-left), हिन्दी, Bahasa Indonesia, 日本語, 한국어, 简体中文 and 繁體中文. Emails, validation messages, dates and numbers follow the language too.

Branding (white-label)

In Administration → Branding & general you can set the site name, logo, favicon, primary and accent colours, login headline and footer text, and hide "Powered by SnagBane". Each form also has its own theme: accent colour, background, logo and cover image.

Integration catalog

Open a form's Integrations tab (or Integrations & API for all forms), pick a connector, fill in the settings and click Send test. Every integration can run on new, edited, approved or rejected responses, and only when your conditions match (e.g. post to Telegram only when Priority is Urgent). Settings support merge tags like {{respondent.name}}, {{all_answers}} or any {{variable_name}}. Secrets are stored encrypted and never shown again; each run is logged with its result.

CategoryConnectors
Chat & notificationsMicrosoft Teams, Slack, Telegram, Discord, Google Chat, Mattermost / Rocket.Chat
Email & marketingSend email (custom, branded, with attachments), Mailchimp, Brevo, MailerLite, Kit (ConvertKit), ActiveCampaign
Spreadsheets & databasesGoogle Sheets, Excel tables (auto-creates the table and columns), SharePoint lists (auto-creates the list and columns), Airtable, Notion
Files & storageDropbox, Google Drive, SharePoint / OneDrive, Amazon S3 / Cloudflare R2 / Wasabi / MinIO
CRM & customHubSpot, Custom HTTP request (any REST API)
SMSTwilio SMS / WhatsApp
Local serverLocal folder export (CSV + JSON lines + per-response folders with files), Local log file (JSON lines for SIEM)
AutomationPower Automate, Zapier, Make, n8n and signed webhooks (see below)

Setup guides

The same steps are shown in SnagBane above each integration's settings, in the user's language, with a link to the provider's official guide. Menu names are the provider's own (in English).

ActiveCampaign
  1. In ActiveCampaign, click Settings (gear icon) → Developer.
  2. Copy the API URL and the API key into the fields below.
  3. Optional: the numeric ID of the list to subscribe contacts to.

Official guide: help.activecampaign.com

Airtable
  1. Open airtable.com/create/tokens (Developer hub → Personal access tokens) and create a token.
  2. Add the scope data.records:write.
  3. Under Access, add the base to write to, create the token and copy it (it is shown only once).
  4. Base ID: the part starting with “app” in the base’s address (airtable.com/app…/…).
  5. Table: the table name exactly as in Airtable, or its ID (tbl…).

Official guide: airtable.com

Amazon S3 / R2 / Wasabi / MinIO
  1. AWS: create the bucket, then IAM → Users → Create user (no console access needed).
  2. Attach a policy that allows s3:PutObject on arn:aws:s3:::your-bucket/*.
  3. Open the user → Security credentials → Create access key (“Application running outside AWS”) and copy both keys.
  4. Leave Endpoint empty and enter the bucket’s region.
  5. Cloudflare R2: R2 → Manage API tokens → Create API token with Object Read & Write for the bucket. Endpoint: https://<ACCOUNT_ID>.r2.cloudflarestorage.com, region: auto.
  6. MinIO, Wasabi and others: use their S3 endpoint and access keys.

Official guide: docs.aws.amazon.com

Brevo contacts
  1. In Brevo, open the account menu → Settings → SMTP & API → API Keys & MCP.
  2. Click Generate a new API key, name it, choose an expiry and copy it (it is shown only once).
  3. Optional: the IDs of the lists to add contacts to (Contacts → Lists), separated by commas.

Official guide: help.brevo.com

Custom HTTP request
  1. Take the URL and method from the service’s API documentation.
  2. Add headers such as Authorization: Bearer … (stored encrypted).
  3. Write the JSON body with merge tags; values are escaped automatically.
  4. Save and click Send test to see the reply.
Discord
  1. In Discord, open Server Settings → Integrations (you need the Manage Webhooks permission).
  2. Click Create Webhook (or Webhooks → New Webhook), give it a name and choose the channel.
  3. Click Copy Webhook URL and paste it below.

Official guide: support.discord.com

Dropbox
  1. Open dropbox.com/developers/apps → Create app → Scoped access → App folder (or Full Dropbox).
  2. On the Permissions tab tick files.content.write and files.content.read, then click Submit.
  3. Copy the App key and App secret from the Settings tab into the fields below and save.
  4. Click Get authorisation code, allow access, paste the code and click Connect.

Official guide: www.dropbox.com

Excel (Enterprise)
  1. In Administration → Microsoft 365, turn on “Save responses to SharePoint / Excel” and connect an Excel account (Microsoft’s Excel API only works as a user).
  2. Open the workbook in Excel or SharePoint → Share → Copy link, and paste it below. The connected account must be able to edit it.
  3. Enter a table name. With automatic creation on, the table and missing columns are created for you.
  4. Save and click Send test.
Google Chat (Enterprise)
  1. Open the space in Google Chat in a browser (webhooks can’t be added in the mobile app).
  2. Click the arrow next to the space name → Apps & integrations → Add webhooks.
  3. Enter a name and click Save, then click More (⋮) → Copy link and paste it below.
  4. Option missing? Your Google Workspace admin has to allow incoming webhooks.

Official guide: developers.google.com

Google Drive (Enterprise)
  1. Set up the service account once in Administration → Google Workspace, with the Google Drive API enabled.
  2. Service accounts have no storage of their own: open a Shared Drive → Manage members and add the service account email as Content manager.
  3. Open the folder in that Shared Drive and paste its link below.
  4. Or fill in “Upload as user (domain-wide delegation)” to save into that person’s My Drive.

Official guide: support.google.com

Google Sheets (Enterprise)
  1. Set up the service account once in Administration → Google Workspace, with the Google Sheets API enabled.
  2. Open the spreadsheet → Share and add the service account email as Editor.
  3. Paste the spreadsheet link below. The tab is added if it doesn’t exist.

Official guide: developers.google.com

HubSpot
  1. In HubSpot, open Development → Keys → Service keys (or Settings → Integrations → Service keys) and create a key. You need Developer tools access.
  2. Name it and add the scope crm.objects.contacts.write.
  3. Copy the key and paste it below. A token from an existing legacy private app works too.
  4. Map answers to HubSpot contact properties below.

Official guide: developers.hubspot.com

Kit (ConvertKit)
  1. In Kit, open Settings → Developer and add a new API key (v4).
  2. Copy the key (it is shown only once) and paste it below.
  3. Optional tag ID: open the tag under Grow → Subscribers; the number in the page address is its ID.

Official guide: help.kit.com

Local folder export
  1. Files are written on the server below the export root shown under Sub-folder.
  2. With Docker, mount that folder as a volume or network share to reach the files.
  3. Choose what to write: responses.csv, responses.jsonl and/or a folder per response.
Local log file
  1. Each event is appended as one JSON line to storage/logs/forms/<form>.log.
  2. Point your log shipper (Filebeat, Fluent Bit, Vector, Splunk forwarder…) at storage/logs/forms/*.log.
  3. Turn off the Include answers option for privacy-sensitive forms.
Mailchimp
  1. In Mailchimp, click your profile icon → Profile → Extras → API keys.
  2. Click Create A Key, name it, click Generate Key and copy it (it is shown only once).
  3. Audience ID: Audience → Settings → Audience name and defaults → Audience ID.

Official guide: mailchimp.com

MailerLite
  1. In MailerLite, open Integrations → MailerLite API → Use.
  2. Click Generate new token, name it and copy it right away (it is shown only once).
  3. Group IDs are listed in the Groups section of the same page; separate several with commas.

Official guide: www.mailerlite.com

Mattermost / Rocket.Chat
  1. In Mattermost, open the Product menu → Integrations → Incoming Webhooks → Add Incoming Webhook.
  2. Enter a name, choose the channel and click Save.
  3. Copy the URL (https://your-server/hooks/…) and paste it below.
  4. No Integrations menu? A System Admin enables incoming webhooks in System Console → Integrations → Integration Management.

Official guide: developers.mattermost.com

Microsoft Teams (Enterprise)
  1. In Teams, click ⋯ next to the channel or chat and choose Workflows.
  2. Pick “Send webhook alerts to a channel” (or “Send webhook alerts to a chat”).
  3. Check the team and channel, click Save and copy the webhook link.
  4. Paste it below, save, then click Send test.

Official guide: support.microsoft.com

Notion
  1. In Notion, open Settings → Developer and turn on developer features (workspace owners only).
  2. Click + New connection, give it a name and choose the workspace.
  3. Click ••• next to the connection, copy its token and paste it below.
  4. Give the connection access to your database: its Content access tab, or the database’s ••• menu → Connections.
  5. Paste the database link below. Property types are detected automatically.

Official guide: developers.notion.com

Send email
  1. Email is sent through the service set up in Administration → Email.
  2. Enter recipients separated by commas; a merge tag such as {{email}} sends to an address from the form.
  3. Add conditions below to route by answer, e.g. a different team per department.
SharePoint / OneDrive files (Enterprise)
  1. In Administration → Microsoft 365, turn on “Save responses to SharePoint / Excel” and grant admin consent.
  2. Enter the site address; files are saved in the site’s Documents library.
  3. Choose a folder; each response gets its own sub-folder.
  4. Save and click Send test to check the permissions.
SharePoint list (Enterprise)
  1. In Administration → Microsoft 365, turn on “Save responses to SharePoint / Excel” and grant admin consent.
  2. Enter the site address, e.g. https://contoso.sharepoint.com/sites/HR.
  3. Choose a list name. With automatic creation on, the list and its columns are created for you.
  4. Save and click Send test to check the permissions.
Slack
  1. Go to api.slack.com/apps and create an app (From scratch) in your workspace.
  2. Open Incoming Webhooks and turn on Activate Incoming Webhooks.
  3. Click Add New Webhook, pick the channel and authorise it.
  4. Copy the webhook URL (https://hooks.slack.com/services/…) and paste it below.

Official guide: docs.slack.dev

Telegram
  1. In Telegram, message @BotFather and send /newbot.
  2. Choose a name and a username ending in “bot”, then copy the token BotFather sends you.
  3. Add the bot to your group, or as an administrator of your channel.
  4. Chat ID: @channelname for a public channel. For a group, send /start in it, open https://api.telegram.org/bot<token>/getUpdates and copy chat → id (a negative number).

Official guide: core.telegram.org

Twilio SMS / WhatsApp
  1. Sign in to the Twilio Console (console.twilio.com). The Account SID and Auth token are under Account Info.
  2. From: a Twilio phone number you own (+1555…), or whatsapp:+… for a WhatsApp sender.
  3. To try WhatsApp: Console → Messaging → Try WhatsApp, and join the sandbox from your phone first.
  4. To: a number in international format, or a merge tag such as {{phone}}.

Official guide: help.twilio.com

Email providers

Admin → Email supports SMTP, Microsoft 365 (Graph), Google Workspace (Gmail API), SendGrid, Mailgun (US/EU), Postmark, Brevo, Resend and Amazon SES — all via their official HTTPS APIs, no extra software. Use Log only while testing, or Mailpit in the Docker stack.

Power Automate (Enterprise)

Option A: HTTP trigger (2 minutes)

  1. Create a flow with the trigger When an HTTP request is received.
  2. In SnagBane, open the form, go to Automations and click Copy JSON schema. Paste the schema into the trigger's Request Body JSON Schema.
  3. Save the flow and copy its URL. Back in SnagBane, click Power Automate, paste the URL and click Test.

Your answers are available as response.fields.<variable_name>, and the respondent as response.respondent.name/email/department/manager.

Option B: Custom connector (recommended for organisations)

  1. Download snagbane.swagger.json, which is also available under Automations & API.
  2. In Power Automate, go to Data → Custom connectors → New custom connector → Import an OpenAPI file.
  3. Change Host to your SnagBane domain. Under Security, choose API Key, set the parameter label to "API token", the name to Authorization and the location to Header.
  4. Create a connection and enter Bearer sb_… (from Automations & API → New token).

Flow makers now get the trigger When a response is submitted (with a form dropdown) and the actions List forms, Get responses and Get a response. SnagBane registers the flow's callback URL automatically and removes it when the flow is deleted.

Zapier, Make and n8n

Make and n8n work in every edition; Zapier is part of Enterprise.

All three receive the same JSON as a webhook. In SnagBane open the form → Automations → pick the service; the same three steps are shown there.

ServiceIn the serviceIn SnagBane
ZapierCreate a Zap with the trigger Webhooks by Zapier → Catch Hook and copy the webhook URL Zapier shows.Paste it as Catch Hook URL, create the automation, click Test, then Test trigger in Zapier: the answers appear as fields for the next steps.
MakeCreate a scenario and add the Webhooks → Custom webhook module; click Add and copy the webhook address.Paste it as Webhook URL, create the automation and click Test: Make learns the data structure, then add the next modules.
n8nAdd a Webhook node with the HTTP method POST; copy its Production URL (not the Test URL) and activate the workflow.Paste it as Production webhook URL, create the automation, click Test and check the execution in n8n.

Failed deliveries are retried; every attempt is listed under the automation's Log. To check that a call really came from SnagBane, verify the signature as described under Webhooks & signatures (n8n and Make can do it with a code step).

Microsoft Teams notifications

In Teams, open the chat or channel, then ⋯ → Workflows and pick “Send webhook alerts to a chat” or “Send webhook alerts to a channel”. Finish the wizard and copy the webhook URL. In SnagBane, add the Microsoft Teams integration (form → Integrations, or Integrations & API for all forms) and paste the URL. Each response then posts an Adaptive Card with the answers and an Open in SnagBane button.

Webhooks & signatures

SnagBane POSTs JSON for the events response.created, response.updated, approval.requested, approval.approved, approval.rejected, form.published and form.closed. A failed delivery is retried up to 3 times, and every attempt is logged. You can re-send any delivery from the log.

POST /your-endpoint
X-SnagBane-Event: response.created
X-SnagBane-Signature: sha256=5d7f…

{ "event": "response.created", "form": {"id": 2, "title": "…"},
  "response": { "id": 91, "respondent": {"name": "Adele Vance", "email": "…"},
                "fields": {"nps": 9, "rate_our_support": 5}, "answers": [ … ] } }

Every request also carries X-SnagBane-Timestamp and X-SnagBane-Signature-V2 (HMAC-SHA256 of timestamp.body), so receivers can reject replayed requests, and an X-SnagBane-Delivery id that stays the same across retries. Verify it in PHP:

$body = file_get_contents('php://input'); $ts = $_SERVER['HTTP_X_SNAGBANE_TIMESTAMP'] ?? '';
$valid = ctype_digit($ts) && abs(time() - (int) $ts) <= 300
      && hash_equals('sha256='.hash_hmac('sha256', $ts.'.'.$body, $secret), $_SERVER['HTTP_X_SNAGBANE_SIGNATURE_V2'] ?? '');

Examples for Node.js, Python and C#, and the full event list, are in the API reference.

REST API (Enterprise)

Read forms and responses from scripts and other systems with a personal API token (Automations & API → Personal API tokens). Base URL: https://your-domain/api/v1, header Authorization: Bearer sb_…, 120 requests per minute.

Method & pathDescription
GET /meThe token's user
GET /formsList forms
GET /forms/{id}Form with its questions (key, label, type, options)
GET /forms/{id}/responses?since=&top=&page=Paginated responses, newest first
GET /forms/{id}/responses/{responseId}One response
POST /hooks {form_id, url, event}Subscribe a REST hook (returns its id and signing secret)
DELETE /hooks/{id}Unsubscribe
GET /hooks/sample/{form}Sample payload for mapping fields
curl https://forms.example.com/api/v1/forms/2/responses?top=10 -H "Authorization: Bearer sb_xxx"

Full reference with examples (curl, PowerShell, Python), errors, pagination, answer formats and webhook verification: REST API reference →

Security features

Send logs to a SIEM (Microsoft Sentinel, Elastic, Splunk…)

SnagBane writes everything a security team usually wants as JSON lines (one flat JSON object per line, UTF-8, appended, never rewritten) — the format the log agents of Microsoft, Elastic, Splunk and most other SIEMs read directly. Nothing is sent to anyone unless you set it up.

WhatWhereContents
Audit logstorage/logs/audit/audit-YYYY-MM-DD.log (one file per UTC day)Who did what: sign-ins and failed sign-ins (auth.login, auth.failed, auth.password_blocked, auth.recovery_code_used), users and roles, API tokens, settings, integrations and webhooks, domains, forms and portals, response deletions and exports, updates, licence, database password changes, audit exports.
Form eventsstorage/logs/forms/<form>.log — the Local log file integration on a form (Connect apps)One line per submission, approval and the other events you pick.
Application errorsstorage/logs/laravel-*.log; in Docker also the container output (docker compose logs)Errors and warnings of the app itself.

Audit record fields (names are stable; new fields may be added, existing ones are never renamed):

FieldTypeMeaning
timestring, ISO 8601 UTCWhen it happened, e.g. 2026-10-01T08:15:02.113Z
app / sitestringAlways snagbane / the host name of the site address (to tell installations apart)
idnumberAudit entry number (unique per installation, increasing)
actionstringWhat happened, e.g. user.updated
user_id, user_email, user_namenumber / string, or nullWho did it (null for the system, e.g. scheduled jobs or failed sign-ins)
subject_type, subject_idstring or nullWhat it was done to, e.g. Form / 42
ipstring or nullClient address as SnagBane saw it (behind a reverse proxy on a public address, set TRUSTED_PROXIES in .env so this is the real client; private networks are trusted by default)
metastring (JSON)Details as a JSON text, e.g. {"to":"editor"} — kept as text so the line stays flat

Example line:

{"time":"2026-10-01T08:15:02.113Z","app":"snagbane","site":"forms.contoso.com","id":5120,"action":"user.updated","user_id":1,"user_email":"admin@contoso.com","user_name":"Ana Admin","subject_type":"User","subject_id":"17","ip":"203.0.113.10","meta":"{\"role\":\"editor\"}"}

Microsoft Sentinel (Azure Monitor Agent)

Microsoft Sentinel works on a Log Analytics workspace. The Azure Monitor Agent reads the audit files into a custom table of that workspace with a Custom JSON Logs data collection rule (DCR). The server must be an Azure virtual machine or an Azure Arc-enabled server (for servers outside Azure); you need at least contributor rights on the workspace and permission to create DCRs. Steps from Microsoft's documentation Collect JSON file from virtual machine with Azure Monitor and Collect guest log data from virtual machines:

  1. Create the table first — Microsoft states the table must exist before the DCR and must be created with a PowerShell script like this one (Azure PowerShell, Invoke-AzRestMethod). Replace the {…} parts:
    $tableParams = @'
    {
      "properties": {
        "schema": {
          "name": "SnagBaneAudit_CL",
          "columns": [
            { "name": "TimeGenerated", "type": "dateTime" },
            { "name": "Computer", "type": "string" },
            { "name": "FilePath", "type": "string" },
            { "name": "app", "type": "string" },
            { "name": "site", "type": "string" },
            { "name": "id", "type": "int" },
            { "name": "action", "type": "string" },
            { "name": "user_id", "type": "int" },
            { "name": "user_email", "type": "string" },
            { "name": "user_name", "type": "string" },
            { "name": "subject_type", "type": "string" },
            { "name": "subject_id", "type": "string" },
            { "name": "ip", "type": "string" },
            { "name": "meta", "type": "string" }
          ]
        }
      }
    }
    '@
    Invoke-AzRestMethod -Path "/subscriptions/{subscription}/resourcegroups/{resourcegroup}/providers/microsoft.operationalinsights/workspaces/{WorkspaceName}/tables/SnagBaneAudit_CL?api-version=2021-12-01-preview" -Method PUT -payload $tableParams
  2. In the Azure portal: Monitor → Data Collection Rules → Create. On Basics give a Rule Name, Subscription, Resource group and a Region that is the same as the workspace's. If the portal asks for a Data Collection Endpoint for this type of data (Help me choose next to Type of telemetry tells you), select or create one.
  3. Resources → Add resources → the SnagBane server. The portal installs the Azure Monitor Agent when needed.
  4. Collect and deliver → Add new dataflow → Data source type: Custom JSON Logs:
    • File pattern: the audit folder with a wildcard in the file name, e.g. /var/www/snagbane/storage/logs/audit/audit-*.log (Docker with the mount above: /opt/snagbane/audit-logs/audit-*.log; Windows: C:\snagbane\storage\logs\audit\audit-*.log). Wildcards are allowed only in the file name and the first folder above it.
    • Table name: SnagBaneAudit_CL.
    • Transform: use SnagBane's own time as the record time: source | extend TimeGenerated = todatetime(time) | project-away time
    • JSON Schema: the fields from the table above.
    • Destination: Azure Monitor Logs → your Sentinel workspace.
  5. Create the rule. Data can take up to 5 minutes to arrive. Check it in the workspace (Logs): SnagBaneAudit_CL | take 10. From there, write Sentinel analytics rules on it, e.g. many auth.failed for one ip, or user.updated where meta contains "owner".

Microsoft's notes that apply: files must be UTF-8 JSON lines and appended (SnagBane does both); don't rename or copy files into the watched folder; keep the number of watched folders small.

Elastic (Filebeat or Elastic Agent)

Use a filestream input with the ndjson parser — from Elastic's filestream input reference. In filebeat.yml:

filebeat.inputs:
- type: filestream
  id: snagbane-audit
  paths:
    - /var/www/snagbane/storage/logs/audit/audit-*.log
  parsers:
    - ndjson:
        target: ""
        add_error_key: true

Splunk (universal forwarder)

Install the Splunk universal forwarder on the SnagBane server (or, with Docker, on the host that has the ./audit-logs folder from above) and connect it to your Splunk indexers or Splunk Cloud Platform as Splunk describes for your environment. Then:

  1. Create an index for SnagBane in Splunk (for example snagbane), or use an existing one.
  2. On the forwarder, open $SPLUNK_HOME/etc/system/local/inputs.conf (create it if it doesn't exist) and add a monitor stanza for the audit folder. Use the real path of your installation:
    [monitor:///var/www/snagbane/storage/logs/audit]
    index = snagbane
    sourcetype = _json
    disabled = 0
    _json is Splunk's source type for JSON-formatted events; each line is one event. Add a second stanza for storage/logs/forms if you also want form events.
  3. Restart the forwarder ($SPLUNK_HOME/bin/splunk restart) and check the input with $SPLUNK_HOME/bin/splunk list monitor.
  4. In Splunk, search index=snagbane action=auth.failed to see failed sign-ins.

Splunk also accepts the input from the command line on the forwarder (splunk add monitor <path>), which writes the same inputs.conf for you. Checked against Splunk's documentation “Monitor files and directories with inputs.conf” and “Configure the universal forwarder using configuration files”.

Other SIEMs

Any agent that monitors a file and parses JSON lines works: point it at storage/logs/audit/audit-*.log (and, if you like, storage/logs/forms/*.log) and treat each line as one JSON event whose time is the time field (ISO 8601, UTC).

To push form events straight to an HTTP collector instead of a file, use the HTTP request integration (your own URL, headers such as Authorization — stored encrypted — and a JSON body with merge tags) or a signed webhook.

FAQ & troubleshooting

"AADSTS50011: redirect URI mismatch"

Your site URL changed. Re-run setup, or add https://your-domain/auth/microsoft/callback to the app registration's redirect URIs.

The health check fails right after connecting

A new client secret can take up to a minute to become active. Wait a minute and click Run health check again.

Device-code sign-in is blocked

Your Conditional Access policy blocks the device-code flow. Use the PowerShell or Manual tab instead.

Uploads fail

Raise upload_max_filesize and post_max_size in PHP, and the Max upload size in Branding & general.

Emails aren't sent

Configure Administration → Email and use Send test email. For Microsoft 365 SMTP, SMTP AUTH must be enabled on the mailbox. You can use the Graph option instead.

Credits & licences

Laravel validation translations from Laravel-Lang (MIT). Laravel (MIT), Vue.js (MIT), Vue Router (MIT), Tailwind CSS (MIT), Lucide icons (ISC), SortableJS (MIT), node-qrcode (MIT), Microsoft Teams JavaScript client library (MIT), Inter font (SIL OFL 1.1). Microsoft, Microsoft 365, Entra ID, Teams and Power Automate are trademarks of Microsoft Corporation. SnagBane is not affiliated with Microsoft.