# Read the workspace audit log Source: https://docs.dmly.io/account/audit-log Browse an append-only trail of high-value actions taken in your workspace, filterable by action and date range. The audit log is a record of *who did what, when* for a specific set of high-value or destructive actions in your workspace, not a complete history of everything that happens. Each row is written once and never edited. This page is an honest account of what it currently covers, so you know what to trust it for and what to still check elsewhere. Viewing the audit log needs the **View the audit log** permission (`team.audit_log`). The workspace owner and Admins hold it by default; Members and Viewers don't, though a custom role can grant it to a Member. See [Roles and permissions](/account/roles-and-permissions). ## Open the audit log Workspace Settings is a separate area from the main sidebar, with its own nav: **Settings**, **Team Members**, **Billing**, **Receipts**, and **Security**. **Audit log** is a tab on the **Settings** page, alongside **Business Details**, **Conversation settings**, **Appointment settings**, **Finance settings**, **Notification settings**, **AI Knowledge** and **Custom fields**. The Audit log tab with the Action and Date range filters highlighted above a list of entries showing when, who, the action code, a summary and the IP address ## What's on the page Each row shows **When**, **Who** (the team member's name, or **System** if no one was signed in when it happened), **Action** (a short code like `contact.delete`), **Summary** (a plain-language sentence of what happened), and **IP** (the address the request came from). The list is paginated, 30 rows at a time. Selecting a row doesn't open any further detail; the **Summary** column is the full description you get. There's no export or delete control on the page; it's read-only. ## Filtering A dropdown of action codes. It's built from the actions that have actually happened in your workspace, so a quiet workspace will show a short list. **All time**, **Last 24 hours**, **Last 7 days**, or **Last 30 days**. ## What's actually logged today This is the part worth reading carefully: the audit log covers three main groups of actions, plus a short list of other money and data actions, not every change made in the workspace. ### Actions taken in the dashboard | Action code | What it records | | - | - | | `contact.delete` | Deleting a single contact | | `contact.bulk_delete` | Bulk-deleting contacts | | `contact.bulk_add_tag` | Bulk-adding a tag to contacts | | `contact.bulk_remove_tag` | Bulk-removing a tag from contacts | | `contact.bulk_set_stage` | Bulk-changing contacts' lifecycle stage | | `offering.services_import` | Importing services from a CSV file | | `automation.bulk_delete` | Bulk-deleting automations | These rows come from the dashboard only. The same actions performed through the [REST API](/api-reference/introduction) write no audit row at all, so a contact deleted by a script or an integration leaves nothing here. ### An agency opening your workspace | Action code | What it records | | - | - | | `agency.impersonate` | A whitelabel agency signing in to your workspace as its owner, with **Open** | This one only appears in a workspace an agency manages. See [Client workspaces](/agency/client-workspaces). The row is written at the moment the agency signs in, and it is the only marker you get. From then on they are acting *as* the owner, so anything else they do while inside is recorded under the owner's name, not theirs. The **Who** on the `agency.impersonate` row itself is the agency's own user, which may be a name you don't otherwise see in your team list. ### Writes made by an AI client over MCP | Action code | What it records | | - | - | | `mcp.session_started` | An AI client connecting to the workspace, naming the client that connected | | `mcp.` | One row per write that client makes, for example `mcp.send_message`, `mcp.create_invoice` or `mcp.book_appointment` | Each of the 20 tools that change something writes its own row, so an AI booking an appointment, tagging a contact or queueing a broadcast all leave a trail. Reads don't: a model looking up a contact or reading a conversation writes nothing, on purpose, because a read tool called several times per reply would bury everything else. Nor does the broadcast preview step, which only works out who a broadcast would reach; the `mcp.submit_broadcast` row is what records the send being queued for approval. What the **Who** column shows depends on how the client connected. Through a connection someone authorized with their own login, it's that person's name. Through a workspace API key there's nobody signed in, so it reads **System**. The `mcp.` prefix on the action code is the reliable signal that an AI did it, because the **Summary** doesn't always say so. See [the MCP server](/mcp/overview). ### Other actions that are logged A few more actions also write a row: | Action code | What it records | | - | - | | `contact.restored` | A deleted contact brought back | | `contact.recovery_purged` | The recovery data for a deleted contact permanently erased | | `finance.bank_account_updated` | The bank account for manual transfers changed | | `payment.manual_confirmed` | A bank transfer confirmed | | `payment.manual_rejected` | A bank transfer rejected | | `payment.refund_reversed` | A refund reversed | | `calendar.staff_disconnected` | A staff member's Google Calendar disconnected | | `livechat.revoked` | A Live Chat widget retired (its conversations kept) | | `livechat.purged` | A Live Chat widget's data erased | | `sequence.subscriber_removed` | A contact removed from a sequence | | `review_request.attribution` | A review request's attribution corrected | | `subscription.paused_unpaid` | A client subscription paused for non-payment (system) | | `subscription.resumed_new_card` | A paused subscription resumed after the client saved a new card (system) | Nothing outside the lists on this page writes to the audit log. Deleting a single automation, editing a broadcast, refunding a payment (only reversing a refund is logged), removing a team member, and most other admin actions don't appear here at all. Don't rely on this page to answer "who changed X" for anything outside the lists above. For message and delivery failures, see [Logs](/troubleshooting/logs) instead, which is a different, unrelated record. ## Retention Audit log entries are kept for **180 days**, then removed automatically. There's no way to extend that from inside DMLY, and no export button, so if you need to keep a record longer than that, copy what you need out before it ages off. You need the **View the audit log** permission. The workspace owner and Admins have it automatically; a Member needs a custom role that grants it. See [Roles and permissions](/account/roles-and-permissions). Most actions in DMLY don't write an audit row; only the ones listed above do. This isn't a bug; that action simply isn't instrumented. Entries older than 180 days are pruned automatically and can't be recovered. ## Next Grant the audit log permission to a custom role. Invite people and set their base role. Two-factor authentication and other account protections. The separate, unrelated record of message and automation delivery events. # Set your business details Source: https://docs.dmly.io/account/business-details Set the business name, logo, brand colour, currency and contact details your workspace uses everywhere. Your workspace name **is** your business name; there is no separate field for the two. Change it here and it changes everywhere DMLY refers to your business. The rest of this page is the supporting detail: your logo, brand colour, currency, and the registration and contact details a business needs on record. Only workspace **Admins** can open Workspace Settings. Members and Viewers cannot reach it at all. See [Roles and permissions](/account/roles-and-permissions). ## Open Business Details Workspace Settings is a separate area from the main sidebar. It opens with its own nav (**Settings**, **Team Members**, **Billing**, **Receipts**, and **Security**) and a **Back** button to return to the app. **Business Details** is the first tab on the **Settings** page. Everything on the tab is one form. One save writes the lot. The Business Details tab in Settings, with the business name, registration number, tax ID, email and phone filled in ## What lives here The name of your workspace and the name of your business. One field, one source of truth. Upload your business logo. Your brand colour. Pick from the **Select currency** list. DMLY offers 30 currencies. **Business Email** and **Business Phone**. **Company Registration Number** and **Tax ID**. **Business Address** is six fields: **Address Line 1**, **Address Line 2**, **City**, **State / Region**, **Postal Code**, and **Country**. Set the business name carefully at the start. It is the name your customers see, and automation and AI replies use it too. See [AI replies](/automation/ai-replies). ## Timezone Timezone isn't on this tab. There are two of them, in two different places: * **Your own display timezone** is a personal preference, so each person on the team can work in their own. Set it under **Edit Profile → My Preferences**. See [Your profile](/account/profile). * **The timezone your public booking page shows times in** is a business setting, on the **Appointment settings** tab. Its **Timezone** section has a **Timezone mode** choice (**Detect automatically** or **Fixed timezone**), and picking **Fixed timezone** reveals a picker for which one. See [Appointments](/appointments/overview). ## The other Settings tabs Business Details sits alongside seven more tabs on the same **Settings** page: Human takeover, conversation ownership, reopening, and the AI reply policy. Booking rules and availability. Invoicing, tax and payment behaviour. Which events notify clients and staff, on which channel. Extra fields on your contact records. Who did what in the workspace. The one without a card is **AI Knowledge** (the documents your AI answers from). There is no **Tags** tab here: tags are managed with **Manage tags** on the [Contacts](/contacts/overview) page. Deleting a workspace deletes all its data and cannot be undone. It lives on **Workspace Settings → Security**, is available to the workspace owner only, and asks you to type the workspace name to confirm. See [Security](/account/security). # Set how conversations behave Source: https://docs.dmly.io/account/conversation-settings Choose what a teammate's reply does to the bot, who owns a conversation afterwards, when a closed one reopens, and whether the AI may answer. **Conversation settings** governs the handover between your team and your automations: whether replying takes a conversation off the bot, when the bot gets it back, who owns it, and whether the AI is allowed to answer at all. Every control on the tab already matches how DMLY behaves out of the box, and the tab says so in a banner at the top: **These already match how DMLY behaves out of the box — change them only if you want something different.** So this is somewhere you come to change your mind, not a setup step you have to complete. ## Open the tab Select your name and avatar at the bottom of the sidebar, then **Settings**. It is the second tab, after **Business Details**. The Conversation settings tab, highlighted in the Settings tab strip, with the out of the box banner and the Human takeover settings One **Save** button at the top of the page saves the whole tab. Nothing applies until you select it. Two things about the scope of this tab: * **It is workspace-wide.** There is no channel picker here. What you set applies to WhatsApp, Messenger, Instagram, Telegram, Live Chat, SMS and every other connected Channel, and to every teammate. * **It is admin territory.** Reaching the tab needs a permission in the **Team & Settings** module, and saving needs **Manage workspace settings**. Admins and the workspace owner hold both; Members and Viewers hold neither by default, though a custom role can grant them. Note that the **Settings** entry in the sidebar menu is only shown to Admins, so a teammate on a custom role reaches the tab from the tab strip on another Settings tab rather than from the menu. See [Roles and permissions](/account/roles-and-permissions). ## Human takeover ### When a teammate replies * **Pause the bot and the running flow** (the default) * **Change nothing — keep the bot running** The tab's own explanation: **Replying is usually a takeover: the automation stops where it is, keeps its place, and stops answering for you.** On the default, sending a reply does two things at once. It pauses the bot for that contact (the same state as **Pause bot (human takeover)** in the details panel), and it *parks* any automation the contact was part-way through: the flow keeps its current step, the answers already given, and whatever was left of a wait timer, rather than being cancelled or restarted. A parked flow shows as a banner in the details panel with **Resume**, **Skip this step** and **End automation**. See [Work a conversation as a team](/inbox/notes-and-collaboration). Two precise points that save confusion: * **The takeover only happens if the reply actually goes out.** A reply DMLY refuses to send (the WhatsApp 24-hour window has closed, the message body is empty, a template has a blank value) leaves the bot running and nothing parked. * **It applies to a person's reply.** A message sent by an automation, a notification or a broadcast is not a takeover. Choose **Change nothing — keep the bot running** and replying is just a message: no pause, no parked flow. Your automations and your team can then answer the same conversation, so use it when the bot handles one narrow job (an order lookup, a booking link) that a human reply should not interrupt. Ownership is a separate setting and still applies. ### When should automation resume after a human reply? * **Only when someone resumes it** (the default) * **When the conversation is closed** * **After a quiet period**, which reveals an hours box: anything from 1 to 720 hours (30 days), and 24 if you leave it alone * **Never — end it by hand** The tab's own explanation: **A paused flow keeps its place. Closing a conversation doesn't prove the automation is still wanted, so by default it waits for you. Flows holding a payment, invoice, booking or order are never resumed automatically.** What each one does: * **Only when someone resumes it**: nothing hands back on its own. Somebody selects **Resume bot**, or the banner's **Resume** or **Skip this step**. A flow's **Resume bot replies** step lifts the pause too, though it leaves the parked run itself frozen. * **When the conversation is closed**: closing hands the conversation back. In the web Inbox you are still asked (the prompt below), and this setting only pre-selects **Resume it from the same step**. Where nothing can ask, the hand-back happens by itself: a flow's **Close conversation** step, the [mobile app](/getting-started/mobile-app), or an AI assistant closing a chat over [MCP](/mcp/overview). * **After a quiet period**: DMLY checks every minute and hands the conversation back once the hours you set have passed. The clock runs from when the takeover happened, not from the last message, so a long conversation does not keep pushing it out. This one only lifts a pause that came from a takeover (a reply, or **Pause bot (human takeover)**); a pause set by a flow's **Handover to Human** step, or by blocking a contact, is never swept. * **Never — end it by hand**: a parked flow only ever moves because a person moved it. The close prompt drops **Resume it from the same step** entirely, and a close arriving from a flow step, the mobile app or MCP leaves the flow parked as well. This select is greyed out when the setting above is **Change nothing — keep the bot running**, with the line **Nothing to resume while replies don’t pause the bot.** Your stored choice is kept, and it still applies to a takeover you start by hand with **Pause bot (human takeover)**, which parks a running flow the same way a reply does. **An automatic hand-back never resumes a flow that moves money or commits a slot.** If the parked flow contains a payment, invoice, order, subscription, coupon, credit, booking, confirmation, cancellation, reschedule, class enrolment, discount, a sent product/service/plan, or a webhook, DMLY leaves it parked and leaves the bot paused, even when your policy says resume. The agent may have taken that payment or booking by hand, and replaying it is worse than waiting. A person resuming it themselves does resume it: that is a decision, not a sweep. ## Ownership ### Who owns the conversation after a reply * **Whoever replied last** (the default) * **Only claim it if nobody owns it** * **Replying never changes the owner** The tab's own explanation: **By default it follows whoever answered last — which also takes it off a teammate the moment somebody else replies.** The Ownership section with the Who owns the conversation after a reply dropdown set to Whoever replied last and highlighted, above Give new conversations an owner This is the setting behind the behaviour people notice first in a shared Inbox: a teammate answers a chat that was yours, and it quietly becomes theirs. **Only claim it if nobody owns it** keeps the reply-claim for unassigned conversations but never takes one off a teammate. **Replying never changes the owner** leaves assignment entirely to hand and to your automations. A claim from replying is silent either way: no bell for the new owner, no bell for the old one, and no `conversation.assigned` webhook. See [Assign chats to teammates](/inbox/assigning-chats). ### Give new conversations an owner * **Leave them unassigned** (the default) * **Round-robin across the team** The tab's own explanation: **Round-robin picks whoever currently has the fewest open conversations. Everyone in the workspace counts as an agent, so leave this off unless that is true for your team.** Round-robin fires once, on a contact's first ever inbound message, and never on a returning contact. It compares how many **Open** conversations each workspace member holds right now (not how many they have ever been given) and hands the new one to the lowest; ties break the same way every time, so the result is stable rather than random. It never takes a conversation that already has an owner, and it assigns before the new-conversation alert goes out, so the alert names the right person. The assignee gets a bell notification; no `conversation.assigned` webhook is sent. **There is no agent list.** Every user in the workspace is a candidate, including your bookkeeper and anyone who only logs in to read reports. There is no on-call or availability filter either: **On call duty** in the sidebar is for [WhatsApp calls](/inbox/whatsapp-calling) and has no effect here. If your workspace is not all-hands-on-the-Inbox, leave this off and triage the **Unassigned** filter by hand. ## Reopening ### When a closed conversation gets a new message * **Always reopen it** (the default) * **Reopen only within**, which reveals an hours box: anything from 1 to 8760 hours (a year), and 24 if you leave it alone * **Leave it closed** The tab's own explanation: **Someone writing back usually means the conversation is live again. Restrict it if your team treats closed as resolved.** The window is measured from the moment the conversation was closed, not from the last message. A second message that arrives after the window has passed does not get a fresh window; it stays closed too. This setting only decides the **Open** / **Done** flip. Whatever you choose, an inbound message is still recorded in the thread, still raises the unread count, still fires `message.received` and any automation built on it, and still un-archives an archived thread. So under **Leave it closed** a returning customer lands in **Done** carrying an unread badge, which is exactly why you would only choose it if someone watches **All** or **Unread only** rather than just **Open**. See [Open and done conversations](/inbox/conversation-status). ## AI replies ### Let the AI reply A switch, **on** by default. The AI replies section with the Let the AI reply switch on and highlighted, above Wait before an AI reply set to 0 seconds The tab's own explanation: **Turning this off stops every AI step across all your flows at once. The rest of each flow keeps running and the skipped step is noted in the conversation.** This is the workspace kill switch for the **AI** step inside a [flow](/automation/flow-builder). Turn it off and no flow is broken: each one keeps running, the AI step is skipped, and a line appears in the conversation reading `✨ AI step skipped — AI replies are turned off for this workspace.` so whoever opens the thread can see why the bot went quiet. Turning it back on takes effect on the next message; it does not replay the skipped steps. It does not touch **Suggest a reply with AI** in the Inbox reply box. That is a separate feature for your team, and it keeps working with this switch off. The AI agent itself is on every plan. What varies is the monthly AI replies allowance and whether you can connect your own provider key; see [Plans](/billing/plans). ### Wait before an AI reply A number of seconds, **0** by default, maximum **5**. The tab's own explanation: **A short pause makes the AI feel less instant. Max 5 seconds — it holds a worker while it waits. A flow step asking for longer keeps its own pause.** It is a floor, not an override: an AI step that asks for a longer pause of its own (**Typing delay before reply (seconds)** on the step's **Prompt** tab in the [flow builder](/automation/flow-builder)) keeps the longer of the two. Five seconds is the ceiling either way, though: whichever number wins, the pause DMLY actually takes never exceeds five seconds, because it is a real wait held open while the reply is prepared. Anything longer needs a **Smart Delay** step before the AI step rather than a bigger number here. ## How the settings interact The two halves of this tab meet in a few places worth knowing before you change anything: | If you set | Then | | - | - | | **Change nothing — keep the bot running** | The resume policy has nothing to act on for replies, and greys out. It still governs a pause you start with **Pause bot (human takeover)**. | | **Never — end it by hand** | The close prompt loses its **Resume it from the same step** option, and no other way of closing a conversation can hand the flow back either. | | **Round-robin across the team** plus **Whoever replied last** | A new conversation gets an owner immediately, then changes hands to whoever answers it. Pair round-robin with **Only claim it if nobody owns it** if you want the original assignment to stick. | | **Leave it closed** plus **Round-robin across the team** | A returning contact is never re-assigned: round-robin only ever fires on a contact's first message. | | **Let the AI reply** off plus a flow whose only reply is an AI step | The flow still starts and still finishes, but the contact hears nothing: all they get is the skipped-step note, which only your team can see. Deactivate the flow itself if you want no run at all. | Change one thing at a time and watch a real conversation. Every setting here alters something a customer experiences (whether the bot speaks, whether a chat reopens), and the defaults are the behaviour your team already learned. ## Related Taking over from the bot, the parked-flow banner, and the close prompt. Who owns a conversation, and what changes hands. Marking done, reopening, and archive. The first tab on the same Settings page. # Custom media storage Source: https://docs.dmly.io/account/custom-storage Keep the photos, videos and files your customers send in your own AWS S3 or S3-compatible bucket instead of DMLY's storage. By default, the media that arrives in your conversations is stored by DMLY. **Custom storage** sends it to a bucket you own instead, on AWS S3 or any S3-compatible provider. Use it when your privacy policy, a client contract or local data rules say customer files must stay in storage you control. ## What goes to your bucket Once it's on, these are saved to your bucket: * Photos, videos, voice notes and documents customers send you on WhatsApp, Facebook, Instagram, Telegram and Live Chat. * Media brought across from the WhatsApp Business app when a number connects in [co-existence](/channels/whatsapp). * Files your team sends from the [mobile app](/getting-started/mobile-app). Two things to know before you switch it on: * **It applies to new media only.** Files already stored stay where they are. Nothing is moved. * **Files in your bucket stay private.** DMLY never makes them public. When someone opens one in the Inbox, DMLY creates a short-lived link for that view, so your bucket doesn't need public access. ## Before you start * **You need to be the workspace owner.** Only the owner can open or change this page. * **Create a bucket** with your storage provider, and note its name and region. * **Create an access key** that can read, write and delete objects in that bucket. On AWS, an IAM user with a policy like this is enough (swap in your bucket name): ```json theme={"dark"} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::your-bucket-name/*" } ] } ``` On another provider, create a key with read, write and delete rights on the bucket in its own dashboard. ## What to enter Here's what each field looks like on AWS. Use your own values, not these. | Field | Example | Where to find it | | - | - | - | | **Bucket name** | `acme-salon-media` | The name you gave the bucket when you created it, exactly as shown in the S3 console. | | **Region** | `eu-west-2` | The bucket's region code, shown next to it in the S3 console. For example `us-east-1` (N. Virginia), `eu-west-2` (London), `eu-central-1` (Frankfurt) or `af-south-1` (Cape Town). Use the code, not the city name. | | **S3 endpoint URL** | Leave blank | Only needed for providers other than AWS. | | **Path-style addressing** | Off | Leave it off on AWS. | | **Access key ID** | `AKIAIOSFODNN7EXAMPLE` | 20 characters, starting `AKIA`. Shown when you create the IAM user's access key. | | **Secret access key** | `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY` | 40 characters. AWS shows it only once, when you create the key, so copy it then. | On an S3-compatible provider, the bucket and keys work the same way. The region and endpoint come from your provider: | Provider | Region | S3 endpoint URL | | - | - | - | | Cloudflare R2 | `auto` | `https://ACCOUNT_ID.r2.cloudflarestorage.com`, with your Cloudflare account ID | | DigitalOcean Spaces | `nyc3` | `https://nyc3.digitaloceanspaces.com` | | Wasabi | `eu-central-1` | `https://s3.eu-central-1.wasabisys.com` | | Backblaze B2 | `us-west-004` | `https://s3.us-west-004.backblazeb2.com` | These show one region each. Use the region your bucket is actually in, and the endpoint that matches it. If saving fails with a provider like these, try turning on **Path-style addressing**. ## Set it up Go to **Workspace Settings → Custom storage** and turn on **Use custom storage**. The Custom storage page with the Use custom storage switch turned on and highlighted Fill in **Bucket name** and **Region**. On AWS, leave **S3 endpoint URL** blank. On any other provider, paste its HTTPS endpoint into **S3 endpoint URL**. Your provider's dashboard lists it. If the provider asks for it, turn on **Path-style addressing**. The Connection details form with an example bucket name and the eu-west-2 region highlighted, a blank S3 endpoint URL, Path-style addressing off, and masked access keys Paste the **Access key ID** and **Secret access key**. Both are stored encrypted. Select **Save storage settings**. DMLY writes a small test file to your bucket, reads it back and deletes it before it switches anything over. If all three work, you'll see **Storage settings saved.** and new media goes to your bucket from then on. Changing the bucket, region or endpoint later doesn't need the keys again. Leave both key fields blank to keep the saved ones. ## If saving fails Nothing changes until the test passes, so media keeps going to the same place as before. * *Could not verify storage. Check the endpoint, credentials, and bucket read/write/delete permissions.* The test file couldn't be written, read back or deleted. Check the bucket name and region, and that the key has all three permissions. * *Use a publicly reachable HTTPS S3 endpoint without credentials or query parameters.* The endpoint must start with `https://`, be reachable from the internet, and contain nothing but the address. DMLY doesn't follow redirects, and won't connect to a private or internal address. ## Switching back, or changing bucket Turn off **Use custom storage** and save, and new media goes to DMLY storage again. Connect a different bucket, and new media goes there. Either way, DMLY keeps the old connection so it can still show the files already in that bucket. **Keep old buckets and their keys working.** Files saved to a bucket stay there. If you delete the bucket or revoke its key, those files stop showing in your conversations. DMLY can't bring them back. **DMLY doesn't fall back to its own storage.** If your bucket stops accepting uploads, for example because the key was revoked, new media isn't saved anywhere else. Check this page after any change to your storage account. # Set up notifications Source: https://docs.dmly.io/account/notifications Choose which events message your clients, which alert your team, and on which channel, all set once for the whole workspace. DMLY sends messages automatically when things happen in your workspace. Some of those messages go out to a contact over WhatsApp, SMS or email. Some go to your team. Some are alerts that only ever appear inside DMLY. All of it is controlled in one place, by an admin, for the whole workspace. What an admin sets here decides whether an event fires at all, for everyone and every contact. In the mobile app (**Settings > Notifications**), each member can also turn push notifications for single events on or off under **Notify me about**, switch push off for their devices under **How to reach me**, and turn on a **Daily digest**. Those choices are per user and per workspace, and only affect push and the digest. The dashboard has no per-user notification settings. See [Profile](/account/profile) and [Security](/account/security). ## Who can change them Notification settings live in **Workspace Settings → Settings → Notification settings**. The link to Notification settings sits in the admin-only **Workspace Settings** area, so in normal use only an admin reaches it. See [Roles and permissions](/account/roles-and-permissions). ## The two tabs Messages that leave DMLY (WhatsApp, SMS or email) and land with a contact or a member of your team. Alerts that stay inside DMLY. Nothing here is ever sent to a client. See [Inbox notifications](/inbox/notifications). That split is the one to keep in your head: **Client & staff messages** costs you a WhatsApp send and reaches a real person's phone. **In-app alerts** only ever reaches your own team, in the app. ## Which number they send from The **Client & staff messages** tab opens with a **Default WhatsApp number** picker, above the event cards. It sets the number every WhatsApp notification sends from, and it is the number a first-time inbox message goes out on unless you pick a different one with the **Send from** picker in the [Inbox](/inbox/overview). Set it before you look at any templates. The **WhatsApp template** dropdowns, and the **Approved**, **Pending** and **Rejected** badges beside them, only list templates approved on that number's WhatsApp Business Account, so changing the number changes which templates you can pick. The picker saves with the same **Save** button as the rest of the page. The list only offers numbers that can actually send, and the picker is not there at all if you have none connected. A contact who already messages you on one of your numbers keeps that number; the default is what DMLY uses when there is no WhatsApp conversation yet. It is one setting for the whole workspace, not a per-event choice, and it does not appear on the **In-app alerts** tab. ## Customer-facing or team-facing On the **Client & staff messages** tab, each card is one event and the cards are grouped under four headings. Each heading tells you who is on the receiving end: * **Client notifications** go out to the contact. * **Staff notifications** go to the staff member concerned. * **Other notifications** go to your team: the admin, receptionist or scheduler-type members rather than the staff member on the booking. * **Finance notifications** (invoice sent, payment receipt, gift card delivery, payment failed and renewal reminder) all go out to the contact. **Payment receipt** goes out whenever a client pays you online — a booking deposit, a pay link from a flow or the AI agent, an invoice — and when you confirm a bank transfer they told you they sent. It does not go out for a payment you record by hand, so back-filling old payments never messages anyone. A subscription renewal has its own receipt, set on the subscription. Each event is a card with a master toggle. The channel checkboxes (**Email**, **SMS** and **WhatsApp**) appear inside the card once the event is switched on, so you can send an event over WhatsApp only, or over WhatsApp and email, or turn it off entirely. WhatsApp is the one to lead with; leave the others off unless you have a reason. ## Turn a notification on Go to **Workspace Settings → Settings → Notification settings**. **Client & staff messages** for anything that leaves DMLY. **In-app alerts** for the alerts your team sees in the app. The Notification settings tab with the Client and staff messages and In-app alerts tabs highlighted, above the client notification cards Find the event's card and turn on its toggle. The channel checkboxes stay hidden until you do. Tick **Email**, **SMS** or **WhatsApp**, whichever you want the event delivered over. Two client notification cards switched on, with the Email, SMS and WhatsApp checkboxes highlighted on the first, above its WhatsApp template dropdowns At the top of the **Client & staff messages** tab, pick the **Default WhatsApp number** these messages send from. If you have only one connected number it is already selected. Do this before the next step: it decides which templates the dropdowns offer. A WhatsApp notification is carried by an approved WhatsApp template. Ticking **WhatsApp** reveals a **WhatsApp template** dropdown inside the card. It opens on the default option (labelled **Default template** followed by the template's own `dmly_` name) with an **Approved**, **Pending** or **Rejected** badge beside it. The client appointment confirmation, update and reminder events show two pickers instead: **WhatsApp template — with join link (Google Meet, Zoom, custom URL)** and **WhatsApp template — in person / no link**. Each has its own default and badge, and DMLY picks one per booking, so set both. The alternatives in the dropdown are your approved templates. See [Message templates](/broadcasts/message-templates). Press **Save** in the page header. Nothing is stored until you do, and your changes are lost if you navigate away first. The page states it plainly: **These settings apply to every contact in this workspace.** There is no per-contact exception and no opt-out list here. ## If a notification doesn't arrive Work through it in this order: * The event is switched on, and the channel is ticked, on the **Client & staff messages** tab, and you pressed **Save**. * The channel itself is connected: a WhatsApp notification needs a connected WhatsApp number. See [Connect WhatsApp](/channels/whatsapp). * The WhatsApp template it uses is approved by Meta. Without one, DMLY sends a normal WhatsApp message only while the contact's 24-hour window is open. Outside the window it falls back to the channel the contact arrived on (unless SMS is ticked for that event, when the SMS goes instead). If none of those apply, nothing goes out. See [Message templates](/broadcasts/message-templates). If all three check out, see [Messages not sending](/troubleshooting/messages-not-sending). ## Next Invite the people who receive staff notifications. Who can reach Workspace Settings in the first place. # Manage your profile Source: https://docs.dmly.io/account/profile Change your own name, email, language, timezone, password and two-factor authentication in DMLY. Your profile is your own login account. It is separate from **Workspace Settings**, which covers the business as a whole. See [Business details](/account/business-details). Every user who logs in has a profile, whatever their role. Select your name and avatar at the bottom of the sidebar to open your user menu, then select **Edit Profile**. Everything below (your name, email and phone, **My Preferences**, two-factor authentication, your password, and deleting your account, if your provider allows it) happens on that one page. The **Phone** field in **Profile Information** is optional. It fills in your number when you are linked to a staff record. ## Change your email Find the **Profile Information** section at the top and edit the **Email** field. The Profile Information section of Edit Profile with a name, email and phone filled in, and the Email field highlighted Select **Save**. The change takes effect immediately; there is no confirmation link sent to the new address. Email is what you sign in with, so the next time you sign in, use the new address. You stay signed in on your current device right away. ## Set your timezone and other preferences Find the **My Preferences** section and open the **Timezone** dropdown. The My Preferences section with the language, the Timezone dropdown highlighted and set to London, the time format and the first day of the week Pick your timezone and select **Save**. The same section also holds your **language**, your **time format** (12 or 24 hour) and the **first day of your week** (Sunday or Monday). These are simple dropdowns, saved the same way. Your timezone is a personal preference; it does not change what anyone else sees, so two teammates in different countries each set their own. The business-level timezone that controls how times appear on your public booking page is separate, under **Appointments → Settings**. ## Turn on two-factor authentication Two-factor authentication lives on your profile, not on the workspace **Security** page; that page only handles deleting the workspace. It is available by default, and each user turns it on for themselves. Scroll to the **Two Factor Authentication** section. It shows a red **Disabled** badge until you turn it on. Select **Enable**. The Two Factor Authentication section showing a red Disabled badge, with the Enable button highlighted If you don't already have one, install a TOTP-compatible authenticator app, such as Google Authenticator, on your phone or computer. DMLY shows a QR code and a secret key. Scan the QR code with your authenticator app, or type in the secret key if you'd rather add it manually. Type the 6-digit code your authenticator app shows into the **Code** field and select **Confirm**. The badge switches to **Enabled** and DMLY shows a set of recovery codes. Store them somewhere safe, such as a password manager; they're the only way back in if you lose your authenticator app. DMLY may ask you to re-enter your password before this section unlocks. That's a routine security check on a sensitive change, not something you need to redo afterward. See [Security](/account/security) for single active session, roles, and what DMLY does and doesn't protect against. ## Change your password Scroll to the **Update Password** section. Enter your **Current password**, then your **New password**, then repeat it in **Confirm new password**. The Update Password section with the Current password, New password and Confirm new password fields highlighted above the Save button Select **Save**. Changing your password signs you out everywhere else. Every other browser session is signed out the next time it loads a page, and every mobile app sign-in is revoked, including one on your own phone, so you sign in again there. The browser you change it from stays signed in. It does not touch workspace API keys or an AI client already connected over MCP. See [what changing your password signs out](/account/security#what-changing-your-password-signs-out). ## What is not on your profile Notification settings are workspace-wide and set by an admin. The dashboard has no per-user notification settings. In the mobile app you can choose which events push to your phone, switch push off, and turn on a daily digest. See [Notification settings](/account/notifications). ## Delete account **Delete account** permanently removes your login *and* every workspace you own, including all of that workspace's data. It is a different action from **Delete workspace** on the workspace **Security** page (which is owner-only and removes a single workspace), but it is not the smaller one. Workspaces you belong to but don't own are not deleted. You are simply removed from them, and their owner keeps full access, even if you were the only Admin. The **Delete Account** section only appears if your provider allows account deletion. The business name, address, currency and logo for the whole workspace. Invite teammates and set what each one can do. # Control what your team can do Source: https://docs.dmly.io/account/roles-and-permissions Set a base role for each team member and build custom roles that grant an exact set of permissions. Every person who logs in to your workspace has a **base role**: Admin, Member or Viewer. That role decides what they can open and change. When none of the three fits, you can build a **custom role** with an exact set of permissions and assign it to a member. Only an Admin (or the workspace owner) can reach **Workspace Settings → Team Members**. The **Team Roles** page needs the *Create & edit custom roles* permission. Admins and the owner have it by default, and a custom role can grant it to a Member. ## The three base roles Can access and edit everything, including billing, channels and team settings. Can access and edit everything except the configuration section: a full day-to-day operator. Can view the messaging modules (Inbox, Contacts, Broadcasts, Automation and so on) but cannot edit, send, or change configuration. **Member** is the everyday role: it covers the work, but stops short of the destructive and admin-only actions. Those are deleting, importing or exporting contacts, uploading or deleting contact documents, managing letter templates, approving or deleting broadcasts, deleting automations and templates, seeing, changing or cancelling the whole team's appointments, booking or blocking time for other people, managing working hours, booking settings or class batches, deleting or importing offerings, issuing refunds, changing finance settings, editing business info on Google, exporting reports, viewing the audit log, setting up integrations, and anything under team settings. **Viewer** gets read-only access and nothing else. The workspace **owner** always has full access and carries an **Owner** badge in the members list. You can't change the owner's role or remove them, and you can't change your own. There's no way to hand a workspace to a different owner from inside the app. Contact your agency or DMLY support if you need that. How many people you can have in a workspace depends on your plan. Everyone who has joined counts toward the limit, whatever their role. So do pending invitations and active appointment staff without a login. See [What uses up a seat](/account/team-members#what-uses-up-a-seat), [Plans](/billing/plans) and [Add-ons](/billing/add-ons). ## When to build a custom role DMLY ships **no ready-made custom roles**. A new workspace has the three base roles and nothing else, which is why the Team Roles page starts empty. Build one when you need someone in between: more than a regular Member, less than an Admin. A bookkeeper who should issue refunds and change finance settings but touch nothing else. A marketer who can approve and send broadcasts but can't delete contacts. A manager who can see the whole team's appointments. If a base role already fits, use it. A custom role is extra to maintain. ## The module and action model A custom role is built from **68 permissions** grouped into **13 modules**: | | | | - | - | | **Inbox** | **Contacts** | | **Broadcasts** | **Automation** | | **Templates** | **Appointments** | | **Offerings** | **Finance** | | **Reputation** | **Reports** | | **Growth Tools** | **Setup & Integrations** | | **Team & Settings** | | Each module holds separate actions (viewing, creating, editing, deleting, exporting and so on), so you grant the specific thing rather than the whole module. You can tick a module whole, or tick individual permissions inside it. Permissions are enforced when a page loads, not just by hiding menu items. A member without any permission in a module can't reach it even with a direct link. ## Build a role Go to **Workspace Settings → Team Members** and select **Team Roles** in the panel header. Select **Back to Team Members** to return. The Team Members page with the Team Roles button highlighted next to Invite member Select **New role**, name it something a person would recognise on a picker ("Bookkeeper", "Front desk") and open the **Permissions** matrix. Work module by module. Tick a whole module when the role owns that area, or tick individual actions when it doesn't. Start narrow; you can always add more later. The New role form on Team Roles with the name Front desk and a description, and the Inbox permissions highlighted with three actions ticked The role now appears on the Team Roles list, ready to assign. Creating a role changes nobody's access until you assign it. Grant a view permission wherever you grant an edit permission. A role built from edit permissions alone will reach the module, but pages and lists it can't view will be incomplete. ## Assign a role Members are invited by email; there's no way to add someone directly. Go to **Workspace Settings → Team Members** and select **Invite member**. Enter their email address and pick a base **Role**. Under **Custom role**, choose the role you built, or leave **None — use the base role above** to stay on the base role. Select **Invite**. The invitation appears at the top of the members list with a **Pending** badge until they accept. To change an existing member, open the actions menu on their row and select **Edit role**. You can set the base role and the custom role there, plus two more things: The Edit role window for a member with the Admin, Member and Viewer base roles, the Custom role dropdown highlighted, and the Profiles list * **Profiles**: pick connected profiles to limit a member to those profiles and the conversations that arrive on them. None selected means every profile. This applies to everyone below Admin, with or without a custom role. * **Access restrictions** (only when no custom role is picked): hide modules such as Inbox, Appointments, Broadcasts, Automation or Reports, mask phone numbers and emails, and allow approving broadcasts. A pending invitation's role can't be edited. If you picked the wrong one, select **Cancel** on that row and send a fresh invite. ## How access is decided DMLY works down this order for every member: 1. **Owner or base role Admin**: full access, always. A custom role assigned on top of Admin makes no difference. 2. **A custom role is assigned**: exactly the permissions ticked on that role, and nothing else. The custom role replaces the base role's defaults. 3. **Base role Viewer**: view permissions only. 4. **Base role Member**: the standard operator set described above, narrowed by any **Access restrictions** set on that member. A member's **Profiles** selection applies on top of all of this, for everyone below Admin. So a custom role decides access for base role Members. Base role Viewers stay read-only whatever the role grants: a Viewer with a custom role that grants editing or sending still can't write anything. Put someone on **Member** before assigning a custom role that needs to edit or send. Members on that role revert to their base role (Member or Viewer) and keep working. Nobody is locked out. The confirmation says so before you delete. Check their row in **Team Members** first. If they have a custom role, the base role is being ignored; the fix is a permission on the role, not a different base role. Then check that the role has the module's view permission as well as the action they're trying to take. If their base role is **Admin**, no custom role will narrow them. Drop them to **Member** and let the custom role do the work. ## Related Invite people, cancel invitations, and remove members. How many members your plan allows. Workspace settings an Admin controls. Owner-only workspace actions. # Secure your account Source: https://docs.dmly.io/account/security Turn on two-factor authentication, control who can do what in your workspace, and understand what DMLY protects and what it doesn't. DMLY's account security rests on three things: each person's own login controls (their password, two-factor authentication, and an optional single-session lock); the role you give them in the workspace; and the fact that the most destructive actions are restricted to the workspace owner. There is no separate security dashboard beyond that; this page is an honest account of what exists. ## Turn on two-factor authentication Two-factor authentication lives on each person's own profile, not in workspace settings. It is per user: you turn it on for your own login, and each team member turns it on for theirs. Open **Edit Profile** from your account menu. Find the **Two Factor Authentication** section and follow the setup there. The Two Factor Authentication section on Edit Profile showing a red Disabled badge, with the Enable button highlighted The same page is where you change your password, your name and email, your language and timezone, and where you can delete your own account. See [Your profile](/account/profile). Two-factor authentication starts switched off; you have to enable it yourself. Until you do, the section shows a red **Disabled** badge and an **Enable** button. DMLY has no workspace-wide setting that forces every member to use two-factor authentication, and no admin can turn it on for someone else. If you want it across your team, ask each person to switch it on and check with them. ## Limit yourself to one active session **Single active session** is also on your own profile page, not workspace settings. It's per user; you turn it on for your own login, and it has no effect on anyone else's. It is one lock covering everywhere you sign in, browsers and the [DMLY mobile app](/getting-started/mobile-app) together, not one lock per surface: one signed-in session at a time for your whole account, and whatever signs in next takes the slot from whatever held it. Open **Edit Profile** from your account menu. In the **Single active session** section, switch on **Only allow one active session**. The Single active session section on Edit Profile with the Only allow one active session switch highlighted Confirm **Enable single active session?**. This claims the slot for the browser you're on right now: every other browser session is signed out the next time it loads a page, and every mobile app sign-in is revoked, including one on your own phone. Single active session starts switched off, the same as two-factor authentication. Nothing changes until you turn it on. Turning it back off is immediate and needs no confirmation; it just stops the check. It doesn't sign anyone out, and it doesn't restore a mobile app sign-in that enabling it revoked: that phone has to sign in again. It isn't a lock to one specific device; it's a lock to one session at a time. Whichever browser signs in next, on any device (you on a different computer or phone browser, or anyone else who has your password), claims the slot, signs out whatever browser session held it before, including the one you turned the setting on from, and cuts off the mobile app as well. Turning it on also switches off "remember me" for your account, so every browser needs a fresh sign-in once its session ends. The [DMLY mobile app](/getting-started/mobile-app) shares that one slot rather than having its own, so the two surfaces do evict each other: * **A browser claims the slot** (you sign in on a browser, or turn the setting on there), and the app's sign-in is revoked. The next thing the app tries to do is refused and it drops you back to its sign-in screen, and that device stops receiving push notifications for the account until you sign in again. * **The app claims the slot** (you sign in on a phone), and every browser session is signed out the next time it loads a page, landing on the login screen. Another phone already signed in loses its sign-in too. Neither direction is a live push-based sign-out. The eviction lands on the next request, so a page or a screen already open can sit there looking signed in until something touches it. ## What changing your password signs out If you think someone else is in your account, changing your password is the right first move. It is not only a new password: it evicts the sessions the old one could still reach. It signs out: * **Every other browser session**, on any device, the next time that session loads a page. The check runs on each request, so a browser sitting on an open page is cut off as soon as it does anything. * **Every mobile app sign-in**, including the one on your own phone, straight away. The app drops back to its sign-in screen and stops receiving push notifications for the account until you sign in again. * **Every "remember me" cookie**, so a device that was remembering you can't quietly sign itself back in. Using the emailed **Forgot your password?** link does the same, which matters because that is the route you take when you've already been locked out: it revokes the mobile app and leaves every browser session to be evicted on its next request, then puts you on the login screen to sign in fresh. It does **not** sign out: * **The browser you changed it from.** That session deliberately stays signed in, so you don't lock yourself out mid-task. * **Workspace API keys.** A key is a workspace credential, not your login, and keeps working on your old password. If a key might be exposed, revoke it from **Integrations**. See [API authentication](/api-reference/authentication). * **An AI client already connected over MCP.** Its access rides on the authorization the connection was granted, not on your password, and DMLY has no page to cut one off from: disconnect it in the client that holds the connection, or revoke the API key it uses. See [the MCP server](/mcp/overview). Belt and braces during a suspected compromise: change your password, then turn on **Single active session** so the next sign-in anywhere evicts everything else too, and turn on two-factor authentication so a stolen password isn't enough on its own. ## Control who can do what The strongest lever you have is roles. Everyone who logs in has a base role of **Admin**, **Member**, or **Viewer**, and you can build custom roles that grant an exact set of permissions. Two boundaries are worth knowing: * **Workspace Settings is admin-only.** Members and Viewers cannot reach **Settings**, **Team Members**, **Billing**, or **Receipts** at all: not by clicking, and not by typing the URL. The exception is the tabs on the **Settings** page that carry a permission of their own, **Audit log** among them: a custom role can grant a Member one of those without opening up the rest. * **Security is owner-only.** Only the workspace owner sees the **Security** page. Permissions are enforced on the server, not just hidden in the sidebar, so a member who knows a URL still can't use a feature they lack permission for. Invite people, set their base role, and remove access when someone leaves. Build a custom role with exactly the permissions you want to grant. Removing someone's access is a team-members task, not a security-page task. When a person leaves, remove them from **Team Members**; that is what cuts off their access to the workspace. ## The Security page **Workspace Settings → Security** contains one thing: deleting the workspace. It is visible only to the workspace owner. The Security page in Workspace Settings with a single Delete workspace card and its Delete workspace button **Deleting this workspace will delete all data.** Deletion is permanent, and any active subscription is cancelled immediately. You have to type the workspace name to confirm. There is no undo and no export step built into the flow, so take what you need out first. See [Exporting reports](/analytics/exporting-reports) and [Contact data and privacy](/contacts/data-and-privacy). If what you actually want is to stop paying, cancel the subscription instead; that is reversible and leaves your data alone. See [Cancelling](/billing/cancelling). ## What DMLY does not have Saying this plainly is more useful than implying protection that isn't there: * **No per-device session list.** You cannot see which devices are signed in, and you cannot pick a single one to sign out. The two levers you have are blunter: each clears everything except the session doing the clearing. Those are changing your password (above) and **Single active session**, which repeats that eviction every time your account signs in anywhere while it's on. * **A partial audit log, not a complete one.** There *is* a record you can browse of who did what: **Workspace Settings → Settings → Audit log**. It covers deletions, bulk contact changes, service imports, an agency opening your workspace, every write an AI client makes over MCP, and some money and data actions: bank transfers confirmed or rejected, refund reversals, changes to the manual transfer bank account, contact restores and recovery purges, Live Chat widgets retired or erased, removals from a sequence, staff Google Calendar disconnects and review attribution corrections. It does not cover most everyday admin changes, so don't read an empty log as proof nothing happened. See [Audit log](/account/audit-log). * **No IP allowlist** and no login-location restrictions. * **No ownership transfer.** A workspace owner cannot hand the workspace to someone else from workspace settings. Only the platform operator can change a workspace's owner, so ask your agency or DMLY support. A workspace API key is a credential, so treat it like a password and never paste it into a chat or a public page. Keys are minted from **Integrations**, not from the Security page. See [API authentication](/api-reference/authentication). # Invite and manage team members Source: https://docs.dmly.io/account/team-members Invite teammates to your workspace by email, give them a role, and remove or revoke access when they leave. Everyone who works in your workspace needs their own login. You add them by sending an email invitation and choosing what they can do. Team members live under **Workspace Settings → Team Members**, which only workspace admins can open; a Member or Viewer can't reach the settings area at all. ## Invite someone There is no way to create a login for someone else or add an existing user directly. Everyone joins by accepting an emailed invitation, so the person who joins owns their own password. Go to **Workspace Settings → Team Members** and select **Invite member**. The Team Members page listing three members with their email, role and join date, with the Invite member button highlighted Use the address they'll sign in with. The invitation goes to that address. Choose **Admin**, **Member** or **Viewer** (see below). If you've built a custom role, you can pick it under **Custom role** instead; leave it on **None — use the base role above** to stick with the base role. The Invite member window, with an email address entered and the Admin, Member and Viewer roles Select **Invite**. The invitation appears at the top of the members list with a **Pending** badge until they accept. Without a custom role, a new member joins with their base role's full access. The invite form has no other access controls. ## Fine-tune a member's access Once they've joined, open the menu on their row and choose **Edit role**. There you can: * Tick the **Profiles** they can work with. * Tick **Can approve broadcasts** to let them approve scheduled broadcasts before they send. The members list shows this in its **Can approve** column. * Under **Access restrictions**, tick **Mask contact phone numbers**, **Mask contact email addresses**, **No Inbox module access**, **No Appointments module access**, **No Broadcast module access**, **No Automation module access** or **No Report module access**. The Edit role window for a member, scrolled to the Profiles list, Can approve broadcasts and the Access restrictions checkboxes, which are highlighted **Can approve broadcasts** and **Access restrictions** are hidden when a custom role is picked, because the role then defines the member's access. ## The three roles Every team member has one of three base roles. Can access and edit everything, including settings, billing and the team itself. The everyday operator role: they work the inbox, contacts, their own appointments and campaigns. They can't change how the workspace is set up, and some actions stay with admins: deleting, importing or exporting contacts, contact documents, approving or deleting broadcasts, deleting automations or templates, seeing or editing other staff's appointments, working hours and appointment settings, refunds and finance settings, report export, channels and API keys, the team and the audit log. Read-only: they can view Offerings, Finance, Reputation, reports and logs, and they see full contact phone numbers and email addresses. In Appointments they see only bookings on their own staff row, and they can't open the audit log. They cannot edit, send, or change configuration. If a base role plus the **Access restrictions** checkboxes still isn't granular enough, build a custom role with exactly the permissions you want and assign it when you invite. A custom role replaces the base role's access, except for **Admin**, which always has full access regardless. See [Roles and permissions](/account/roles-and-permissions), reachable from the **Team Roles** button in the **Team Members** panel. ## What uses up a seat Your plan sets how many people can be in the workspace, and three things count against it: * **People who have joined**, the obvious one. * **Invitations that have not been accepted yet.** If you invite someone and they never accept, that seat stays used until you cancel the invitation. * **Active [appointment staff](/appointments/staff-and-availability) who have no login of their own.** This is the one that catches people out. A stylist or practitioner you added purely so bookings can be assigned to them takes a seat exactly as a teammate does, even though they never sign in. Three unlinked staff on a five-seat plan leave you two seats for actual logins. Deactivating a staff member releases their seat. When you're full, the invite form shows `Members limit reached. Max limit: N` (N is your seat cap, add-ons included) and won't send. Two ways out: Free up seats people never claimed. See below. Move to a plan with a higher limit, or buy an **additional teammate** add-on. The limit depends on your plan. Check [Plans](/billing/plans) for yours, or [Add-ons](/billing/add-ons) to buy extra seats without changing plan. ## Cancel a pending invitation Open the menu on the invitation's row and select **Cancel**, then confirm. The link stops working and the seat is freed immediately. You can't change the role on an invitation that's still pending. Cancel it and send a new one with the right role. ## Remove someone Open the menu on their row and select the remove option, then confirm. They lose access to the workspace straight away, and the seat is freed. Two rows never show an actions menu: * **Yourself.** You can't remove your own access or change your own role. * **The workspace owner**, who carries an **Owner** badge instead of a role badge. There is no way to hand a workspace over to a new owner from within DMLY. If the owner is leaving the business, contact support before you remove their user; removing them does not transfer ownership. ## Next steps Build a custom role with exactly the permissions you want. Choose which events alert your staff. See what your plan includes. Buy extra teammate seats on top of your plan. # Brand DMLY as your own Source: https://docs.dmly.io/agency/branding Set your app name, logo, colour and Powered by link so your clients see your brand instead of DMLY. Your branding lives in the reseller dashboard and applies to everything your clients see: the app name in their browser tab, the logo in the sidebar, the accent colour, and the **Powered by** line on your public booking pages and invoices. In the browser, branding only applies on **your own domain**. On the DMLY app host your clients see DMLY's own name, logo and colours. Connect a custom domain first. See [Custom domain](/agency/custom-domain). The DMLY mobile app is different: once a client signs in, it shows your app name and your accent colour, because it follows the workspace's agency. While your agency is suspended, your domain still shows your app name and colours, sign-in page included. Custom scripts stop and your sub-accounts are locked. The mobile app falls back to DMLY's branding. ## Set your brand Select **Reseller Dashboard** from your user menu, then go to **Settings**. Set **Agency name**, **App name (shown to customers)**, **Logo URL**, **Dark-mode logo URL**, **Favicon URL**, **Powered by link**, **Help centre link** and **Primary color**. **App name (shown to customers)** is what replaces "DMLY" throughout the product: the browser tab title and the app name your clients read. **Agency name** is how your agency is identified internally; it is only used as a fallback if **App name** is blank. The Brand section of the agency Settings page with App name (shown to customers) highlighted, among the agency name, support email, logo, favicon, link and colour fields Select **Save brand**. You see **Settings saved.** and the change applies on your next page load on your branded domain. ### Logo **Logo URL** takes a link to an image you host, or select **Or upload a logo** to upload one (PNG, JPG or WebP, up to 2 MB and 4096 × 4096). An upload replaces the URL. It replaces the DMLY logo for your clients. If you leave it blank, your clients see the DMLY logo. **Dark-mode logo URL** sets a separate logo for clients using dark mode, and can be uploaded the same way. Leave it blank and your main logo is used in dark mode too. ### Favicon **Favicon URL** sets the small icon in the browser tab, or select **Or upload a favicon** (PNG or JPG, up to 1 MB and 1024 × 1024; a square image works best). Your clients see it in the dashboard's browser tab on your branded domain and in the Live Chat widget. ### Colour **Primary color** is a single colour. There is no full palette editor; you pick one colour and DMLY applies it to buttons, links and accents across the app. The lighter tints and darker shades the app needs, such as tinted backgrounds and focus outlines, are worked out from that one colour. The colour is saved as a six-digit hex code like `#4f46bb`, which is what the picker always gives you. Anything else is refused with *Colours must be 6-digit hex codes, like #4f46bb.* under the field. ### Help centre link **Help centre link** takes a link to your own documentation. It becomes the **Help Center** item at the foot of your clients' sidebar, and opens in a new tab. It saves with the rest of the Brand section under **Save brand**, not with a button of its own. Leave it blank and that menu item does not appear for your clients at all; they are never sent to DMLY's documentation. Your own workspace is the exception on the DMLY app host. You are a DMLY customer underneath your agency, so there you keep DMLY's docs whatever you set here. On your own domain the item is hidden for your workspace. The value must be a full `http://` or `https://` address, up to 2,048 characters. Typing `help.acme.com` without the scheme is rejected with a validation error under the field. The two labels are spelled differently. The field you fill in is **Help centre link**; the menu item your client clicks is **Help Center**. ## What Powered by renders **Powered by** appears on your public [booking pages](/appointments/overview), on the HTML version of an [invoice](/finance/invoices), in notification emails, and in the footer of the [Live Chat widget](/channels/livechat-widget) your clients embed on their own sites. It reads as your brand, not DMLY's: * The label is your **App name (shown to customers)**. If that's blank, it falls back to your **Agency name**. * It links to your **Powered by link**. If you leave that blank, it links to your custom domain instead. If you have neither, the label renders as plain text with no link. There is no switch to remove **Powered by**. It always renders in those places; the most you can do is make it point at you. It is omitted from the invoice PDF. ## What you cannot change The DMLY mobile app keeps DMLY's own icon. Your favicon only changes the browser tab and the Live Chat widget. It renders on booking pages, HTML invoices, notification emails and the Live Chat widget footer, with no toggle. One **Primary color**, no palette editor. ## Sending email as yourself Branding your app name doesn't change who your clients' emails come from. To send from your own domain, fill in the **Email (SMTP)** section on the same **Settings** page and select **Save SMTP**. As the confirmation says: set SPF/DKIM for your domain so those emails land in inboxes. ## Adding your own scripts The **Custom scripts** section on **Settings** adds your own code (analytics, pixels, a chat widget) to pages on your domain. Paste it into **Scripts inside the `` tag** or **Scripts before the `` tag**, then select **Save scripts**. Only the agency owner can see or change this section. Team members see *Only the agency owner can view or change custom scripts. Ask them if you need analytics, a chat widget or a pixel added.* in its place, whatever their role. No role you build on **Team** can grant it: the **Edit sign-up page and customer workspace options** permission covers the **Customer signup & workspaces** panel, not scripts. That permission used to be called **Edit signup and custom scripts**, and it let team members save scripts. If you ever gave it to anyone, open **Custom scripts** and check that everything there is code you put there or recognise. ### Where your scripts run Your scripts are added in two places, both on your domain: * **Your public sign-up page**, for visitors who aren't signed in. It's the only page a signed-out visitor gets them on. * **Your customers' dashboard pages**, such as the inbox, the members page and their billing page, when a customer of your agency is signed in. They are deliberately left off these pages: * Your Reseller Dashboard, and the page a new team member opens from their invitation. * Sign-in, password, two-factor and email verification pages. * A customer's profile and security pages, their workspace **Settings** page (where they create API keys) and **Workspace Services** page (where their own service credentials are saved), the integration pages where they manage API keys and other credentials, and the pages where they connect WhatsApp. And they don't run at all for these people, on any page: * You, and anyone on your team or on another agency's team. * DMLY staff. * Anyone inside a client workspace through **Open**, including you. * A customer who also belongs to a workspace outside your agency. Scripts also stop running while your agency is suspended, and never run on the DMLY address. Because your own visits are excluded, you won't see your scripts load while you're signed in. To check one is working, open your sign-up page in a private window, or sign in with a customer account that belongs only to your agency. A script runs with the access of whoever loaded the page. Once it loads on a customer's dashboard it keeps running as they move around the app, including onto pages it is never added to, and it can do anything that customer can do. Only paste code from a source you trust, and check it before you save. ## Next Branding needs your own domain. Connect one and verify its DNS. Assign plans, suspend or open a client's workspace. Bill your clients through your own Stripe account. How the reseller layer fits together. # Client workspaces Source: https://docs.dmly.io/agency/client-workspaces How client workspaces join your agency, and how to assign plans, grant add-ons, suspend access, and open one as the customer. Every workspace you sell sits under your agency as a **sub-account**. You manage them all from the reseller dashboard: assign one of your plans, grant an add-on, suspend or restore access, and open a workspace to work inside it. The **Reseller Dashboard** link only appears in your user menu once your workspace is on a reseller plan. If you don't see it, see [Agency overview](/agency/overview). Open it from your user menu → **Reseller Dashboard**, then select **Sub-accounts**. The list shows each workspace's **Name**, **Plan**, **Contacts**, **Add-ons** and **Status**. ## How client workspaces are created There is no button to create a client workspace in the reseller dashboard, by design. Workspaces arrive one of four ways: Your branded sign-up page creates the customer's account and their first workspace under your agency, on your first enabled plan. See [Custom domain](/agency/custom-domain). `POST /workspaces` on the Agency API creates the owner's user account and their workspace under your agency in one call, on a plan you name or your first enabled plan. The response includes the owner's name and email. See [Agency API](/api-reference/agency-introduction). A workspace created by someone who already belongs to your agency stays under your agency automatically. The workspace you were in when you took a reseller plan is moved under your agency automatically, which is why it shows in this list. The empty state points at the first of those: *No sub-accounts yet. Customers who sign up on your domain appear here.* The business name your customer types at sign-up becomes the workspace name exactly as entered; it is the business name throughout the product. See [Business details](/account/business-details). Whether customers can create a *second* workspace is your call, not the platform's. It's controlled by **Customer signup & workspaces** in **Settings**, where **Off closes your public sign-up page** turns sign-ups off entirely. Most agencies sell one workspace per customer and leave the extra-workspace setting off. ### What your sign-up page says The same **Settings → Customer signup & workspaces** panel controls the wording on your branded sign-up page. Alongside the two Yes/No dropdowns it carries three copy fields, all saved with **Save signup settings**: * **Sign-up form title** is the heading. It starts out as *Create your account*, and clearing it falls back to that same wording. Up to 255 characters. * **Sign-up form description** is the line under the heading. It starts out as *Start your free trial.* Clear it and no description line shows at all. It is plain text: line breaks are kept, HTML tags are not interpreted. Up to 500 characters. * **Terms text (shown by the accept checkbox)** changes behaviour, not just wording. Write anything here and your sign-up page gains a tick-box carrying that text, which the customer must tick before the account is created. Leave it empty and there is no checkbox and no consent step. Up to 2,000 characters. The terms text is plain text too. Line breaks are kept, and HTML tags show on the page exactly as you typed them. To link to your own terms page, paste its full address: an address that starts with `https://` becomes a link that opens in a new tab. ```text theme={"dark"} By signing up you agree to our terms: https://acme.com/terms ``` Only `https://` addresses become links; an `http://` address stays plain text. A full stop or closing bracket straight after the address is left out of the link, so the address can end a sentence. Terms text used to accept HTML. If you had saved a link such as `Terms`, it was converted for you to `Terms (https://acme.com/terms)`, which still shows as a link. Where a link was followed straight away by more text, a space was added after the address so each link still opens the right page. Other tags were removed and their text kept. Read your terms once in the panel to check they still say what you want. ### The sub-account limit The number of client workspaces you can carry depends on your reseller plan. When you hit it, the **Sub-accounts** page shows *You've reached your sub-account limit. Contact support to raise it.*, and your branded sign-up page stops accepting new customers. See [Plans](/billing/plans). ## Assign a plan A new sub-account lands on the first enabled plan in your plan list; there is no separate "default plan" setting. If you have no enabled plans yet, it lands with no plan at all. To move a sub-account to a different plan: Plans live under **Configuration → Plans** in the reseller dashboard. Each plan sets the limits the workspace gets. See [Plans and billing](/agency/plans-and-billing). On **Sub-accounts**, use the **— assign plan —** dropdown on the workspace's row and choose the plan. You'll see *Plan assigned to* the workspace name. The Sub-accounts page listing three client workspaces, with the plan dropdown on the first row highlighted Assigning a plan copies that plan's limits onto the workspace immediately. The customer keeps whatever they've already set up; only the limits change. A plan that's assigned to a sub-account can't be deleted; you'll get *This plan is assigned to sub-accounts. Reassign them before deleting it.* Move those workspaces to another plan first. ## Grant an add-on When a client needs more than their plan allows, grant an add-on instead of moving them up a plan. Under **Configuration → Plans**, switch on **Add-on plan**, pick an **Add-on type**: **Extra contacts**, **Extra profile per channel** or **Additional teammate**. Then set **Quantity granted (per unit)**. Your add-on plan then appears in the **+ grant add-on** dropdown on each sub-account's row, listed by the name you gave it. Until you create one, the dropdown doesn't appear at all. Granting adds that capacity to the workspace's limits straight away; grant again to add another unit. Each granted add-on shows as a chip on the row, for example `+500 contacts`. Select the **×** on it to take it back. Add-ons you grant are billed by you, to you; there is no checkout for the customer, and your clients never see DMLY's own add-on catalogue or prices. ## Open a client workspace **Open** signs you in to the workspace as its owner. You get exactly what the owner gets: the full inbox, contacts, automations, appointments, finance. Nothing is hidden from you. ### Opening a workspace is recorded in the client's own log Before you are signed in, DMLY writes an entry into that workspace's **Audit log**: the action `agency.impersonate`, the summary *Agency opened this workspace as its owner*, your name in the **Who** column, the IP address you opened it from, and the time. The entry belongs to the client's workspace, not to your agency, so your client can read it: the workspace owner, their Admins, and anyone whose role grants **View the audit log** see it under **Settings → Audit log**. See [Read the workspace audit log](/account/audit-log), which also covers how long entries are kept. What the entry does not do is label the work you then do. From the moment you are signed in, DMLY treats you as the owner, so anything you do inside the workspace that writes its own audit entry is attributed to the owner's account rather than to you. The log shows that you opened the workspace and when; it does not separate your actions inside it from the customer's own. While you're inside the workspace, your user menu shows an **Impersonating** badge. Select **Sign Out** in that menu; while impersonating it ends the session-as-owner and drops you back into your own account rather than logging you out. Because **Open** gives you the owner's full view, it includes your client's customer conversations and contact data. Treat it as access to their business, and tell your clients you have it before they find the entry themselves: every time you use **Open** leaves your name in their audit log. See [Contacts data and privacy](/contacts/data-and-privacy). Two cases where **Open** won't work: Nobody owns the workspace, so there's no account to sign in as. This is rare; it usually means the owner's user account is gone. The workspace is one of your own, not a client's. Switch to it from your workspace switcher instead. ## Suspend and restore **Suspend** locks a client out of their workspace. Use it when they stop paying you. Their data stays exactly where it is; only access stops. **Restore** puts it back. The **Status** column shows **Active** or **Suspended**. If **your** agency is suspended (most often because your own reseller subscription to DMLY lapsed), every one of your client workspaces is locked at once, including ones you never suspended and clients who are fully paid up. Restoring your own subscription lifts it. Keep your reseller plan current. See [Plans and billing](/agency/plans-and-billing). ## What clients see A client in one of your workspaces sees your brand, not DMLY's, and their **Billing** menu goes to *your* plans, charged through *your* Stripe account. They never see DMLY pricing. Set that up in [Plans and billing](/agency/plans-and-billing) and [Branding](/agency/branding). Build the plans you sell and connect your own Stripe account. Serve your agency (and your sign-up page) on your own domain. Set the app name, logo and colour your clients see. How the agency layer fits together. # Connect Stripe Source: https://docs.dmly.io/agency/connect-stripe Connect your agency's own Stripe account so client workspaces can subscribe and pay you online, and set up the webhook that keeps DMLY in sync. This page is about the Stripe account **you, the agency**, connect so your client workspaces can pay *you* for their plans. It is not the Stripe gateway a business connects under Finance to collect invoice payments from its own customers. For that, see [Payments & gateways](/finance/payments-and-gateways). ## What connecting Stripe unlocks You can run an agency without Stripe: you assign plans to clients by hand from **Sub-accounts**, grant add-ons, and collect payment however you like outside DMLY. What Stripe adds is the self-serve loop: clients subscribing and paying you online, without you touching anything. Your clients' **Plans & billing** page shows your plans, but every button is disabled and the page reads **Online billing isn't available yet.** with your support email. You assign plans manually from **Sub-accounts** and handle money outside DMLY. **Choose plan** goes live. A client picks a cycle, lands on a Stripe-hosted checkout against **your** Stripe account, and pays you directly. DMLY never sees the money and takes no cut. Two things stay the same either way: add-ons for client workspaces are always granted by you from **Sub-accounts** (clients can't buy them at checkout), and charges, refunds, disputes, and payouts live in your Stripe dashboard, not in DMLY. Connecting Stripe has two halves, and **both matter**. The keys let DMLY start a checkout; the webhook is how Stripe tells DMLY the payment happened. Skip the webhook and clients can pay you while DMLY never hears about it. ## Enter your keys in DMLY In your Stripe Dashboard, find your API keys under the developers section. You need two values: the **secret key** (starts `sk_live_`) and the **publishable key** (starts `pk_live_`). In your agency dashboard, go to **Configuration → Settings** and find the **Stripe (charge your customers)** panel. The badge next to the heading reads **Not connected** until a secret key is saved. Fill in **Secret key** and **Publishable key**, then select **Save Stripe keys**. You can leave **Webhook signing secret** empty for now; you'll get that value from Stripe in the next section and come back. On success you see *Stripe keys saved. Your customers will be billed through your own Stripe account.* and the badge switches to **Connected**. The Stripe (charge your customers) panel with the Secret key and Publishable key fields highlighted, and the Not connected badge Your keys are stored encrypted. The **Secret key** and **Webhook signing secret** are never shown back to you (those fields always render empty), and only the publishable key is prefilled on your next visit. DMLY does not test your keys when you save them. A mistyped secret key saves fine and the badge still says **Connected**; the error only surfaces later, when a client tries to check out. Copy the keys carefully, and run the end-to-end test below before pointing a real client at the page. ## Create the webhook endpoint at Stripe After a client pays, Stripe reports the subscription back to DMLY over a webhook. Without it, DMLY records nothing (no subscription, no receipts) even though the charge went through. Your webhook URL is shown in the same Stripe panel, under **Your webhook endpoint**. Copy it from there. It looks like: ```text theme={"dark"} https://dash.dmly.io/agency//stripe/payment-webhook ``` Use the URL exactly as shown. It's built on DMLY's own host on purpose, so it keeps working even if you later change your agency's custom domain. The Stripe panel with Your webhook endpoint highlighted, showing the webhook URL and its Copy button In your Stripe Dashboard, create a new webhook endpoint (under the developers / webhooks section) and paste the URL above as the destination. When Stripe asks which events to send, select these seven. This is the part that most often goes wrong: * `invoice.payment_succeeded`: records a payment receipt in DMLY * `customer.subscription.created`: records the new subscription * `customer.subscription.updated`: keeps its status, price, and trial in sync * `customer.subscription.deleted`: marks the subscription cancelled * `customer.updated`: refreshes the stored payment method * `customer.deleted`: clears the customer link from the workspace * `payment_method.automatically_updated`: keeps card details current Don't select "all events" instead: DMLY quietly accepts event types it doesn't handle, so every delivery looks successful in Stripe while the noise buries the ones that matter. Stripe shows a **signing secret** for the new endpoint, starting `whsec_`. Copy it, return to **Configuration → Settings → Stripe (charge your customers)**, paste it into **Webhook signing secret**, re-enter your **Secret key**, and select **Save Stripe keys** again. Every save needs **Secret key** and **Publishable key** filled in, and the secret key field starts empty on each visit, so paste it again whenever you save. **Webhook signing secret** is the exception: leave it blank and DMLY keeps the secret already stored, so re-saving the panel to fix a typo or rotate a key can't switch your webhook off. To replace the signing secret, paste the new `whsec_` value in and save; the panel has no way to clear one once it's stored. ## Give your plans Stripe prices A plan can only be bought online when its billing cycle carries a Stripe price. In Stripe, create a recurring price for each plan and cycle you sell; then, on your agency **Plans** page, paste each price's ID (starts `price_`) into **Monthly Stripe price ID** or **Yearly Stripe price ID** on the matching plan. A cycle without both a price ID and an amount shows a disabled **Unavailable** button on your clients' billing page. See [Price and bill your clients](/agency/plans-and-billing) for building plans and setting their limits. ## Test it end to end Prove the loop before a real client pays. Most Stripe webhook tooling lets you send a test event to an endpoint. Send one (any of the seven types) and check the response DMLY returns: * **2xx**: the URL and signing secret are both right. You're wired up. * **403 Invalid webhook signature**: the signing secret stored in DMLY doesn't match this endpoint. Re-copy it and re-save. * **404 Unknown agency**: the agency ID in the URL is wrong. * **422 Agency Stripe webhook is not configured**: DMLY has no signing secret stored (or no secret key), so the endpoint is off. From a client workspace (a test sub-account works well; see [Client workspaces](/agency/client-workspaces)), open **Plans & billing** and select **Choose plan** on a priced plan. You should land on a Stripe-hosted checkout page showing your plan's price. Complete the payment. In your Stripe dashboard: the subscription exists, and each webhook delivery for it shows a 2xx response with the body **Webhook Handled**. That body means DMLY processed the event. An empty response body is still a 2xx, but it means the event type isn't one DMLY handles. DMLY records the payment internally but doesn't currently display it to you, so the Stripe delivery log is your confirmation. If Stripe shows the money but the deliveries failed (or never happened), work through the failure modes below. Paying does not switch the client's plan by itself. After checkout, open **Sub-accounts** and assign the plan the client bought; that is what applies the plan's limits and updates the **Current** badge on their billing page. Make this part of your routine whenever a client subscribes or changes plan. A checkout against live keys charges the card for real, so if you test with your own card, refund it afterwards from your Stripe dashboard. DMLY also accepts Stripe **test-mode** keys (`sk_test_`), which run the whole flow in Stripe's test mode with test cards. If you rehearse that way, remember keys, price IDs, webhook endpoint, and signing secret all belong to one mode: switching to live means re-entering live keys, live price IDs, and a live-mode webhook endpoint with its own signing secret. ## When something goes wrong The nastiest one, because checkout itself works: the money is in your Stripe account, but DMLY has no record of the subscription or payment. It means Stripe's events never reached DMLY: the webhook endpoint was never created, points at the wrong URL, or doesn't include the subscription events. Diagnose it from your Stripe dashboard, in the webhook deliveries for your endpoint; DMLY has no log of traffic that never arrived. No endpoint at all? Create it as above. Deliveries listed but failing? Match the response code against the test step above. Deliveries succeeding but nothing recorded? Check the event types. See below. The client is not locked out while you sort this out: a client workspace's access follows your agency's standing, not its own subscription state. Once the webhook is fixed, Stripe can resend the missed events from the delivery history, and DMLY records each event only once; resending is safe. A signing secret is stored, but it isn't this endpoint's: usually the secret was copied from a different endpoint, or the endpoint's secret was rolled in Stripe, or the secret belongs to the other Stripe mode from the endpoint (test against live). Fix: open the endpoint in Stripe, copy its current `whsec_` secret, and re-save the Stripe panel in DMLY with the secret key and that signing secret both filled in. A **422** rather than a 403 means no signing secret is stored at all. The **Webhook signing secret** field is optional at save time, so you can finish the first section, see **Connected**, and have every delivery refused. The panel flags that case in red under **Your webhook endpoint**, telling you the endpoint is turned off until you paste the secret. DMLY returns a success response even for events it doesn't act on, so a wrongly-configured endpoint can look healthy in Stripe while doing nothing. Start with the event types: open the endpoint in Stripe and check the selected events against the list of seven above. In particular, `customer.subscription.created` and `invoice.payment_succeeded` must be selected, or no subscription or receipt is ever recorded. If the event types are right, the event is being refused deliberately. DMLY checks three things before acting on an agency webhook, and an event failing any of them is logged and answered with `Webhook Skipped`, which Stripe records as a success: * **The price must be one of yours.** An event naming a price id that isn't on one of your own plans is skipped. This is the usual culprit: a Stripe Product created outside DMLY, or a plan whose **Stripe price ID** was pasted with a typo. * **The workspace must be yours.** An event naming a workspace that isn't one of your sub-accounts is skipped, and so is one whose customer can't be traced to a workspace at all. * **The price must not be the platform operator's.** An event referencing an operator price id is always refused, whatever your own plans say. These are fences rather than faults, so nothing in Stripe tells you which one fired. Check your plans' Stripe price IDs first. If the page flashes *This agency has not connected Stripe yet.*, no secret key is saved; finish the first section above. If it flashes *That plan is not available.*, the plan is disabled or the chosen cycle has no Stripe price ID; fix it on **Plans**. If checkout fails with a generic server error, the saved secret key is wrong, or the key and the plan's price IDs belong to different modes (a live key with test-mode `price_` IDs, or the reverse). DMLY doesn't validate keys at save time, so this is where a bad key first shows itself. Re-enter the key and confirm every price ID comes from the same Stripe mode as the key. Expected: payment and plan assignment are separate steps. Checkout records the subscription and the payment; it does not change the workspace's plan. Assign the plan from **Sub-accounts** after the payment lands. Until you do, the client's **Plans & billing** page keeps the old **Current** badge and their limits stay as they were. Also expected: a client workspace is never locked out automatically over its own failed or cancelled payments; access follows your agency's standing with DMLY. Chasing payment and suspending the workspace is your call, from **Sub-accounts**. See [Client workspaces](/agency/client-workspaces). ## Related Build the plans and add-ons you sell, and what clients see. Assign plans, suspend, restore, and open a client's workspace. What the whitelabel layer includes. The other Stripe: how a workspace collects payments from its own customers. # Connect your agency domain Source: https://docs.dmly.io/agency/custom-domain Serve your agency's DMLY on a domain you own, so your customers sign up and work under your address instead of a DMLY one. The agency dashboard is only available once you have an agency, which comes from subscribing to a reseller plan. See [Agency overview](/agency/overview) and [Plans](/billing/plans). Your custom domain is what makes the whitelabel real. Your [branding](/agency/branding), your app name, your colour and your logo, is applied by the address the page was opened on. Your public sign-up page only exists on your own domain: on the DMLY address it isn't there at all. So until a domain is connected, there's no address you can hand to a prospect. You get one custom domain per agency. This is not the [booking domain](/appointments/custom-domain). A booking domain belongs to a single workspace and serves that workspace's public booking page. An agency domain belongs to you, the agency, and serves the whole product: sign-up, login and the dashboard your customers work in. A host can only be one or the other; if you try to connect a domain that's already in use as a booking domain, you'll see **That domain is already in use.** ## Connect it Open **Reseller Dashboard** from your user menu, then go to **Configuration → Domain**. The panel shows the records to create under **Create one of these records at your DNS provider:** a **CNAME** or an **A record**. Add one of them at whoever manages your domain's DNS, your registrar or your web host. DMLY doesn't do this part for you. Enter the host you want to use (for example `app.yourbrand.com`) and select **Add domain**. The Custom Domain page with the CNAME record to create, and the domain field and Add domain button highlighted Select **Verify**. If the record hasn't spread yet you'll get **DNS is not pointing here yet. Check the record and try again.** That's normal shortly after a DNS change. Leave it and try again later; DMLY also re-checks on its own roughly every ten minutes and verifies the domain as soon as it resolves. Once it verifies, SSL is requested and issued for you automatically. You don't buy or install a certificate. ## What the status means The panel shows one of three states: * **Pending DNS**: the domain is saved but nothing points at it yet. * **Verified · issuing SSL**: DNS resolves, and the certificate is being issued. This isn't instant. * **SSL active**: DMLY has loaded your domain over HTTPS and confirmed the certificate is really live. The badge only turns green on a real end-to-end check, so it can't tell you the domain is ready when it isn't. Wait for **SSL active** before you publish the address anywhere. The background check runs in both directions, so a live domain isn't permanently live. If the DNS record is removed or stops resolving for around a day, the domain drops back to **Pending DNS** on its own. Keep the record in place for as long as you want the domain. ## Remove a domain Select **Remove** on the domain row, then confirm with **Remove domain**. To move to a different host, remove the current one first; while a domain is connected the add field is replaced by **You’ve connected a custom domain. Remove it below to connect a different one.** Removing the domain takes away the only address your public sign-up page lives on, and your existing customers lose the address they log in at. Have the replacement domain verified and live before you switch, and tell your customers. ## If it won't verify DMLY can only verify a host that actually points at it, so the cause is almost always the DNS record. Re-check it against what the panel shows: the record type, the exact host, and that there isn't an older conflicting record for the same name still in place. Some DNS providers append your domain to the host automatically, which silently produces `app.youragency.com.youragency.com`. After a correction, select **Verify** again. The host is claimed elsewhere, most often as a workspace [booking domain](/appointments/custom-domain), including in one of your own sub-accounts. Pick a different host, or free the existing one first. You also can't use the DMLY address itself. Your app name, colour, logo and support email, applied on your domain. The sub-accounts that arrive when customers sign up on your domain. # Agency overview Source: https://docs.dmly.io/agency/overview How the DMLY agency layer lets you resell the platform under your own brand, domain, and Stripe account. The agency layer turns DMLY into a product you sell. You run your own brand on your own domain, your customers sign up with you, and you bill them through your own Stripe account. DMLY stays invisible to them, and you stay the one they pay. An **Agency** sits above workspaces. It owns a set of customer workspaces (called **sub-accounts**) and carries your branding, one custom domain, your Stripe keys, your mail server, and the plans you sell. The agency layer only exists once you subscribe to a reseller plan. Everything on this page assumes you have. See [Becoming an agency](#becoming-an-agency) below. Prefer to watch first? This walkthrough shows how to launch your own WhatsApp CRM brand on DMLY with the agency layer.