Security & Privacy

Your donors' trust is built on how we protect their data.

Donor records are among the most sensitive data a charity holds. GivingSignal is engineered so that data stays in Canada, stays encrypted, and never reaches our AI in a form that can identify a person.

Every request, checkedEight protections on by default
8 / 8 active
ConnectionTLS · HSTS · strict CSP
Sign-inRate-limited · 10 / min
Your organisationRe-checked on every API route
Data isolationRow-level security
Stored credentialsAES-256-GCM
Data locationStored in Canada
Email linksHMAC-SHA256 signed
Consent changesAppend-only audit log
  • PIPEDA-aligned
  • CASL consent tracking
  • Data stays in Canada
  • AES-256 encryption
Our practices

What that means in practice.

Plain commitments that cover your whole account — sign-in, storage, email and AI — so your team and board know exactly what to hold us to.

  • Access

    Each charity's data is isolated with database-level row security.

  • Access

    Every API route independently re-checks the user and their organisation — defence beyond the database.

  • Abuse protection

    Logins, AI calls, and campaign sends are rate-limited to stop brute-force and abuse.

  • Encryption

    OAuth tokens and SMTP passwords are encrypted with AES-256-GCM, never stored in plain text.

  • Integrity

    Unsubscribe and tracking links are HMAC-signed so they can't be tampered with.

  • Consent

    CASL consent is tracked per contact and blocks sends to expired or opted-out donors.

  • Consent

    Every consent change is written to an append-only audit log

  • Privacy

    Deleting a donor cascades through donations, scores, campaigns, and audit records.

  • AI

    Donor PII is anonymised before being sent to any AI model — the model sees Acct- identifiers, not people.

Threat protection

Built to withstand attacks.

The common ways web apps get breached — and exactly how GivingSignal shuts each one down.

XSSClickjackingBrute forceIDORSQL injectionLink tamperingMan-in-the-middleStolen secrets
  • Blocked

    Cross-site scripting (XSS)

    A strict Content-Security-Policy limits scripts to trusted sources, and everything users enter is escaped before it is ever displayed.

    > CSP · output escaping
  • Blocked

    Clickjacking

    X-Frame-Options: DENY and a frame-ancestors ‘none’ policy stop the app from being hidden inside a malicious page.

    > X-Frame-Options: DENY
  • Blocked

    Brute-force & credential stuffing

    Login attempts are rate-limited per IP address, so automated password-guessing stalls almost immediately.

    > Rate limit per IP
  • Blocked

    Cross-tenant access (IDOR)

    Every request re-verifies that you own the organisation and record you’re touching — you can only ever reach your own donors.

    > Ownership re-check
  • Blocked

    SQL injection

    All database access uses parameterised queries. User input is never concatenated into a SQL statement.

    > Parameterised queries
  • Blocked

    Request & link tampering

    Unsubscribe and tracking links are HMAC-signed, and OAuth sign-in flows use nonce validation to block CSRF.

    > HMAC · OAuth nonce
  • Blocked

    Man-in-the-middle

    HSTS forces encrypted HTTPS on every connection, with preload — there is no silent downgrade to plain HTTP.

    > HSTS · preload
  • Blocked

    Stolen secrets at rest

    Email and SMTP credentials are encrypted with AES-256-GCM; a tampered value fails its authentication tag and is rejected.

    > AES-256-GCM · auth tag
How we protect you

Security at every layer.

Your donor data sits at the centre, wrapped in six independent layers. To reach it, an attacker has to get past every one — from the outside world all the way in — and defeating one still leaves the rest.

01 · Encrypted perimeter02 · Verified sign-in03 · Ownership & isolation04 · Encryption at rest05 · AI boundary06 · Data governanceYour data
  1. 01

    Encrypted perimeter

    The outer wall. Every connection is forced over HTTPS with HSTS, and a strict Content-Security-Policy plus locked-down headers stop anyone intercepting traffic or injecting scripts to get a foothold.

    HTTPS · HSTSContent-Security-PolicySecurity headers
  2. 02

    Verified sign-in

    Nothing responds without an authenticated account — and sign-in attempts are rate-limited per IP, so stolen or guessed passwords stall before they ever get through.

    Authenticated sessionsRate-limited logins
  3. 03

    Ownership & isolation

    Even once signed in, every request re-checks that you own the organisation you're asking about, and row-level security walls each charity off from every other — so one account can never reach another's donors.

    Per-request ownership checkRow-level security
  4. 04

    Encryption at rest

    If storage itself were ever reached, sensitive credentials — email OAuth tokens and SMTP passwords — are AES-256-GCM encrypted and useless without the key.

    AES-256-GCMTLS
  5. 05

    The AI boundary

    Anything sent for AI analysis has names and emails stripped first and replaced with anonymous identifiers — personal data never leaves in a form that can identify a person.

    Anonymous identifiersNo names or emails
  6. 06

    Data governance

    The innermost layer, wrapped around the data itself — consent limits, purpose limitation and the right to be forgotten control what is ever kept, and deleting a donor cascades through every related record.

    Consent & purpose limitsRight to be forgotten
Under the hood

Defence in depth.

The protections most tools never talk about — because we built them before we needed to explain them.

An append-only consent audit trail

Every change to a donor's CASL consent is written to an append-only log — the old value, the new value, who made the change, and when. If a donor ever asks why they were contacted, you have a complete, tamper-evident record to show them.

casl_consent_logAppend-only
Old valueNew valueChanged byWhen

Tenant isolation enforced in the code, not just the database

Authentication isn't left to row-level security alone. Every API route independently re-checks the signed-in user and confirms they own the organisation they're asking about — so one charity can never reach another's donors, even with a hand-crafted request.

~50API routes — each one re-checks:
Signed-in userOwns the organisation

Rate limiting on every sensitive action

Logins, AI requests and campaign sends all have per-user and per-IP ceilings. Brute-force attempts and runaway automation are stopped before they do damage.

Campaign sends5 / min
429 · limit
Login attempts10 / min
429 · limit
Per user and per IP — examples of the limits in place.

Tamper-proof unsubscribe and tracking links

Every unsubscribe and open-tracking link carries an HMAC-SHA256 signature. Alter the URL by a single character and it simply stops working — recipients can't be tricked into actions they never requested.

…/unsubscribe?…&sig=a91f…
…/unsubscribe?…&sig=b91f…
One character changed — the link stops working.

Opt-outs are honoured before a send, not after

Unsubscribed and suppressed addresses are filtered out before a campaign ever goes out. A donor who said "stop" stays stopped — the suppression list is checked on every single send.

Campaign list
Suppression check
Send
Where AI fits

Our AI reasons over patterns, never over names.

Here is the path a donor record takes before any insight is generated — and we never use your data to train models.

  1. step_01

    Your donor records

    Stored on Canadian infrastructure and isolated to your organisation with row-level security.

  2. step_02

    Personal details stripped

    Names and emails are removed and replaced with anonymous identifiers before anything is sent to an AI model.

  3. step_03

    AI reads patterns only

    The model sees identifiers like Acct-0001 and giving patterns — never the people behind them.

  4. step_04

    Insights in your account

    Scores and campaign insights appear inside your GivingSignal account, where your roles and permissions apply.

donor_recordInside your account
{
  name: ●●●●●●●●,
  email: ●●●●●●●●,
  giving_history: [ … ]
}
sent_to_ai_modelWhat the AI sees
{
  id: "Acct-0001",
  name: removed,
  email: removed,
  giving_pattern: [ … ]▌
}
Questions welcome

Have a question about security?

We're happy to walk your team or board through exactly how donor data is handled. Reach out any time.