Security & data handling
If you have been asked to approve taskCNX for your organization, this page is the detail you need: every permission we request, every field that moves, and everything we keep. For the plain-language version, see the privacy policy.
- Permissions
- What data moves
- What we store
- What we never touch
- Security posture
- For security reviewers
- Subprocessors
- Revoking access
The short version. On the Microsoft side we hold two permissions: tasks, and the signed-in user's basic profile. No mail, no files, no directory. A third — read-only free/busy — is optional: offered when someone connects (a box ticked by default that they can untick), and read only while they use calendar-aware reminders or absence notices. Even then we read whether you are free, never what you are doing. We never store the content of a task: subjects, notes and contact details pass through memory during a sync and are never written to our database. On the HubSpot side we write back only status, due date and priority — plus, for users who switch on task scheduling, the time of day on a due date, never the date itself — and, for accounts whose owner switches on notes sync, the notes a user edits in To Do. Accounts whose owner switches on follow-up tasks also get a new task when a task tagged #follow-up is completed.
What we are granted
Microsoft 365 (each user consents, or an admin consents once for the tenant)
| Scope | What it allows | Why we need it |
|---|---|---|
Tasks.ReadWrite | Read and write Microsoft To Do tasks and lists | The sync itself. We create and update tasks in one dedicated list. |
User.Read | The signed-in user's own basic profile | To read the account's name and email so we can match it to the right HubSpot owner. It does not grant access to your directory or to other users. |
offline_access | Refresh the access token | So the sync keeps running without asking the user to sign in repeatedly. |
Calendars.ReadBasicoptional, per user | Read whether the signed-in user is free, busy, or out of office | Offered at sign-in through an “Also watch my calendar” box, ticked by default and easy to untick, or later when someone switches on a feature that needs it. Read only while one of those features is on. It powers calendar-aware reminder timing and, for those who also turn it on, absence notices. It is the least-privileged calendar permission Microsoft offers for this. Users who never grant it are not offered either feature and never have a calendar call made for them. |
That is the complete list. taskCNX requests no application permissions and no directory permissions. Every Microsoft call is made as the signed-in user, limited to what that user can already reach.
On the calendar permission specifically. We read two things: whether you are free or busy in a given slot, and whether a block is marked out-of-office. We do not read meeting subjects, locations, attendees, organisers, bodies or attachments — Microsoft returns some of those in the same response, and we skip them. We never write to a calendar or create an event. Nothing from a calendar is stored: what we read is held in memory for the moment it takes to choose reminder times or decide whether to send you an absence notice, and then discarded.
The permission is granted once and, once you have it, powers two optional features: calendar-aware reminder timing, and absence notices. Both are off until you switch them on, and the absence-notice control is only shown to people who granted the permission. Decline the permission and neither feature is offered, no calendar call is ever made for you, and everything else works unchanged.
If an administrator approves this permission for the organisation, Microsoft grants it tenant-wide; that is how Entra consent works rather than a choice taskCNX makes. The application still only requests it for individual users who have turned the feature on, so a tenant-wide grant does not mean tenant-wide use.
taskCNX is a Microsoft verified publisher. The consent screen your users see carries Microsoft's verified badge and names the publisher as sthreelabs LLC, rather than the unverified-publisher warning shown for applications whose publisher Microsoft has not confirmed. Verification required a Microsoft Partner Center account validated as a legal entity, and DNS proof of control over the sthreelabs.com domain.
HubSpot (your portal admin consents once at install)
| Scope | Why we need it |
|---|---|
crm.objects.contacts.read | Read tasks, and the name and email of a contact a task is linked to, so that context appears in To Do. |
crm.objects.contacts.write | HubSpot governs writes to task objects under this scope. We use it only to update a task's status, due date and priority, and its notes when notes sync is on, and to create a follow-up task when follow-up tasks is on (see below). |
crm.objects.companies.read | The company name on a linked record. |
crm.objects.deals.read | The deal name and amount on a linked record. |
crm.objects.owners.read | To map each HubSpot owner to the person who should receive their tasks. |
On crm.objects.contacts.write: this is the one permission that looks broader than it is. HubSpot does not expose a separate write scope for task objects, so task writes are governed by the contacts scope. In practice taskCNX writes only to task objects: it patches a task that exists, and, for accounts whose owner switches on follow-up tasks, creates one. We never create, modify or delete a contact, a company or a deal.
What data moves
Only tasks assigned to a connected user sync. Nothing else in your CRM is read, and no data is read in bulk.
| HubSpot | Direction | Microsoft To Do |
|---|---|---|
| Task title | → | Title, prefixed [Call], [Email] or [To-do] by task type |
| Task notes | → | Task body, converted from HTML to plain text. Two-way only if the account owner turns on notes sync |
| Linked contact name & email, company name, deal name & amount | → | Appended to the task body as context, with a link back to the HubSpot record |
| Status | ↔ | Complete / not started |
| Due date & time | ↔ | Due date. A date-only task lands at 9:00 AM in the user's timezone |
| Priority | ↔ | Importance (low / normal / high) |
| Reminder | → | Reminder. If no reminder is set, the due time is used so a due date always raises an alert. A HubSpot task can carry several reminders and a To Do task holds one, so the soonest one still ahead is used. Never read back, so a user's own edit in To Do stays put |
The write-back to HubSpot is deliberately narrow: status, due date and priority only. Those are the three things a rep genuinely changes while working from Outlook. Task notes are authored content, so by default HubSpot stays the system of record for them and we never write them back. One optional per-user setting also writes the time of day on a task's due date; it is described under scheduling tasks into the day below, and is off unless a user turns it on.
Notes sync (optional, per account, writes to HubSpot)
What it does: each task in To Do gets a marked line under its notes. Whatever a user types above that line is written to the task's Notes in HubSpot. The contact, company and deal details below the line are always regenerated from HubSpot, so an edit there never reaches the CRM, and if the line is deleted nothing is written.
How an edit lands: a line added to the end is appended to the existing notes, keeping their formatting. Any other change replaces the notes with the user's text, as plain text, because that is all Microsoft To Do holds. If the notes were changed in HubSpot since taskCNX last copied them, HubSpot's version is kept and the To Do edit is not written. Notes too long for To Do to show in full are never written back.
What bounds it: it is off unless the HubSpot account's owner turns it on, it can be disabled for an entire deployment by configuration, and the notes themselves are never stored: taskCNX keeps a SHA-256 hash of them to tell an edit from its own copy. Changes appear in HubSpot property history attributed to the integration.
Task duration does not sync in either direction, because Microsoft To Do has no duration field.
What is synced on first connection
The initial backfill covers open tasks due within the last ninety days, plus anything due in future or carrying no due date, so a new list is not seeded with years of stale work. An account owner can change that window for their portal, up to a maximum of one hundred and eighty days. After that everything syncs regardless of age: the cutoff applies only to the backfill, and a task already in a list is never removed as it ages. Anything left out is counted and shown to the person it happened to, rather than quietly missing.
Reminder timing (optional, per user)
HubSpot tasks commonly all default to the same time of day, so their reminders arrive together. Each user can choose from their own status page how their reminder times are picked. There are four settings, and the default changes nothing:
- Use HubSpot's times. Whatever the task says, untouched. This is the default.
- Spread out reminders that land together. Only reminders arriving at the same moment are separated, into fifteen minute slots, highest priority first. Nothing else moves, and the spread stops at the end of the working day rather than reaching into someone's evening.
- Fit reminders into part of the day. Everything due that day is queued into hours the user chooses, highest priority first. This one can raise a reminder earlier than a task is due, which is the point of it. It can optionally skip slots the user is already booked for, which is the only thing that reads a calendar and is off unless they turn it on.
- Schedule tasks into the day. The one setting that changes something in HubSpot. Described on its own below.
The first three change only the reminder time inside Microsoft To Do. No HubSpot data is written or altered, due dates included. A user who moves a reminder themselves keeps it, and a reminder time entered in HubSpot's own Reminder field is never moved. Anything already overdue is left alone, so old work is not queued back into a working morning.
Follow-up tasks (optional, per account, creates tasks in HubSpot)
What it does: when a task whose notes contain #follow-up is completed, in HubSpot or in To Do, taskCNX creates one new task in HubSpot. It is named "Follow up:" plus the original subject, assigned to the same owner, given the same type and priority, linked to the same contacts, companies and deals, and due three working days later. #follow-up 5d or #follow-up 2w sets a different delay. The new task carries the original notes with the tag removed.
What bounds it: it is off unless the HubSpot account's owner turns it on, and it can be disabled for an entire deployment by configuration. Each completed task produces at most one follow-up, even if it is reopened and completed again. Nothing else in HubSpot is created or changed: the completed task is left as it was, and no contact, company or deal is modified. To enforce the once-only rule taskCNX stores the two task IDs and nothing from the notes. The new task appears in HubSpot as created by the integration.
Scheduling tasks into the day (optional, per user, writes to HubSpot)
Every other feature described on this page leaves HubSpot's own data untouched. This one does not, so it is worth setting out exactly what it changes and what bounds it.
A rep who asks for it gets their tasks given distinct times across working hours they choose, highest priority first, optionally skipping slots they are already booked for. Because the time lives on the task itself rather than only on a reminder, HubSpot's own calendar sync can then show the day as blocks. That is the reason for writing at all.
What is written: the time of day on hs_timestamp, and nothing else. No other property is touched.
What is never written: the date. A task due Friday is given a time on Friday. Reporting that buckets by day is unaffected, "due today" still means today, and the only thing that moves is a time of day that carried no information — HubSpot's create dialog pre-fills one and offers no way to clear it.
How a person takes a task back: by changing its time in HubSpot. taskCNX records the time it wrote; if a task's time is no longer that value, a human or a workflow has set it deliberately and the task is released permanently. That check is re-derived from the data on every run rather than stored as a flag, so it cannot get stuck in the wrong position.
What bounds it: writes are capped per user per hour, and a settled schedule writes nothing at all — a task already sitting where it was placed is left alone, so a rep working through their day does not cause their other tasks to be rewritten. Changes appear in HubSpot property history attributed to the integration, like any other integration write.
It is off unless a user turns it on, it is theirs alone — nobody else, including an account owner, can set it for them — and it can be disabled for an entire deployment by configuration, in which case the option cannot be selected even by posting the value directly.
Where the tasks land
taskCNX creates and uses a single Microsoft To Do list, named taskCNX by default and yours to rename — the sync follows the list itself, not what it is called. Personal lists, My Day, flagged email and every other list are never read and never modified. A user's own tasks added to that list are ignored rather than pushed into your CRM.
This is enforced, not just intended. Tasks.ReadWrite would technically permit us to reach any of a user's lists, so every task operation checks the list it was handed against the one that user is linked to and refuses to send the request if they differ. A wrong list identifier fails closed, and the connector holds no code path that can delete a To Do list at all.
What we store
This is usually the question that matters most, so it is worth being precise.
| Per connected user | Per synced task |
|---|---|
| Work email and display name HubSpot owner ID Microsoft user ID Timezone An encrypted Microsoft token |
The HubSpot task ID The To Do task ID Last known status, due date and priority A SHA-256 hash of the rendered task A SHA-256 hash of the task's notes For a follow-up task, the ID of the task it followed A timestamped log of sync actions |
We do not store task content. Subjects, notes, contact names, emails, company names and deal amounts are held in memory only for the moment it takes to render a task, then discarded. What persists is the pair of IDs that links the two tasks together and a one-way hash used to detect changes, and a hash cannot be reversed back into the task it came from.
The sync activity log records what happened and when (the action, the direction, the task IDs and whether it succeeded) so that a support question can be answered. It does not record what the task said.
How long we keep it
Completed tasks are removed from the To Do list a week after they are closed. Everything else is kept while the app is installed, and erased thirty days after it is uninstalled. See Revoking access.
What we never touch
- Email, since we hold no mail permission of any kind
- Meeting names, locations, attendees and any other calendar content (the optional calendar permission reads free/busy only)
- OneDrive, SharePoint and any file content
- Your Entra directory, group membership or other users' accounts
- Teams messages
- Your CRM at large: no bulk export, no contact lists, no pipeline data, no marketing data
- Anything belonging to another customer, since each HubSpot portal is isolated
Security posture
- In transit: TLS on every connection, to your browser and to the HubSpot and Microsoft APIs.
- At rest: OAuth tokens are encrypted with Fernet (AES-128-CBC with HMAC authentication) before they are written to the database. The encryption key is held in the application environment, not in the database.
- Authentication: OAuth 2.0 throughout. taskCNX never sees, asks for, or stores a HubSpot or Microsoft password. There is no password to breach.
- Tenant isolation: every record carries the HubSpot portal that owns it as a foreign key, and every query is scoped to it, so one customer's data cannot be reached from another's session.
- Least privilege: the scope list above is the whole grant. We have asked for nothing we do not use.
- Access control: billing and team management are restricted to the portal's billing owner. Administrators can set a seat ceiling to cap who may connect.
For security reviewers
The questions a vendor review usually asks, answered directly. Anything not covered here: [email protected].
- Hosting and region
- A Hostinger virtual server in Phoenix, Arizona, United States, running the application and its PostgreSQL database. Nightly database backups are verified after every run and kept for 14 days on the server and 90 days in Cloudflare R2 object storage.
- Encryption
- In transit, TLS on every connection. At rest, OAuth tokens are encrypted by the application (Fernet) with a key kept outside the database and outside the backups. Task content is never stored at all. The other stored fields listed under What we store (work email, display name, account IDs, timezone, billing IDs) are protected by access control rather than application-level encryption, and the server's disk is not separately encrypted. Backups in Cloudflare R2 rely on Cloudflare's storage encryption.
- Security testing
- The codebase had an automated, whole-codebase security review in July 2026. There has not yet been an independent penetration test; we will say so here when there has been.
- Monitoring
- Application errors are reported in real time to Sentry, configured not to send request data, cookies or stack-frame variables. Backups raise an alert if a run fails or does not reach off-site storage.
- Breach notification
- If a personal data breach affects your data, we will notify you without undue delay and within 48 hours of becoming aware of it, describing what happened, what data and how many people are affected, and what we are doing about it.
- DPA and subprocessors
- A data processing agreement, with the EU Standard Contractual Clauses and the UK Addendum, is available on request. The complete subprocessor list is below, and we give notice before adding or replacing one.
Subprocessors
| Provider | Purpose | Receives task data? |
|---|---|---|
| Hostinger | Runs the application and database (Phoenix, Arizona, US) | Yes, it hosts the service |
| Stripe | Billing and payment | No |
| Postmark | Transactional email (invites, receipts) | No |
| Cloudflare (R2) | Off-site storage for nightly database backups, kept 90 days | No task content, which is never stored. The backups hold the account data listed under What we store. |
| Sentry | Error monitoring (United States) | No task data. An error message can include the work email of the user it concerns. |
| Google Analytics | Visit statistics on the public marketing pages only | No, it does not run inside the application |
Each receives only what its function requires. We do not sell data, use it for advertising, or train models on it.
Where your data is processed
Application data is hosted in the United States (Phoenix, Arizona). If you are in the EU or the UK, that means your data is transferred outside the EEA, and we cover those transfers with the European Commission's Standard Contractual Clauses and the UK Addendum, in a data processing agreement available on request.
Revoking access
- One person: disconnecting in taskCNX stops that user's sync immediately and frees their seat.
- The whole portal: uninstalling the app in HubSpot revokes our token and stops all syncing for the account.
- From Microsoft: an Entra administrator can revoke consent for the tenant at any time from the Azure portal, with no action needed on our side.
- Automatic erasure: thirty days after an uninstall we delete the account's data: every connected user, task link, sync cursor and activity record. It is a scheduled job, not a manual process, and it does not need to be requested.
- Deletion on request: email us to have your data erased sooner, or to request a copy, and we will take care of it.
Questions
If your security team needs something this page does not cover, whether a questionnaire, a specific control, or a call, email [email protected] or use the contact form. taskCNX is built by a small team and a real person will answer.