Using AI Coding Agents with NotifiedBy
This page is for developers who use AI coding agents to build integrations with NotifiedBy.
The key rule is simple: treat NotifiedBy as a black-box service. Your agent should rely only on the public website, the public documentation, the published API, and the documented behaviour of the Django package.
Why these files exist
Most coding agents already have a project-level AGENTS.md or CLAUDE.md.
Those files usually describe the application’s own coding standards, tests, and
architecture.
NotifiedBy guidance is different. It is vendor-specific integration guidance. It tells an agent:
what public product surfaces it may rely on
which assumptions are safe
which assumptions are unsafe
how to integrate with the API without inventing internal behaviour
how to use the public Django package when the project uses Django
Available files
Download the files here:
How to use them
Recommended approach:
Keep your application’s existing
AGENTS.mdorCLAUDE.md.Add the NotifiedBy files to your own repository under a vendor or integrations folder.
Add a short routing note to your main agent instructions so the agent knows when to read them.
Suggested repo layout:
docs/
integrations/
notifiedby/
NOTIFIEDBY.md
NOTIFIEDBY-django.md
Suggested routing block for your own AGENTS.md or CLAUDE.md:
## NotifiedBy integration guidance
When working on code that integrates with NotifiedBy, read `NOTIFIEDBY.md` first.
If the project uses Django and the NotifiedBy Django package is relevant, also read `NOTIFIEDBY-django.md`.
These files may live anywhere in the repo; follow your project's own conventions for where vendor-specific agent guidance lives.
Treat NotifiedBy as a black-box external service. Do not assume access to its source code or undocumented internals. Use only the public docs, website, API reference, and documented package behaviour.
When to use each file
Use NOTIFIEDBY.md when:
your code integrates with the NotifiedBy API directly
your project is not Django-based
you want the general vendor rules first
Use NOTIFIEDBY-django.md when:
your project uses Django
you are using or considering the public
django-notifiedbypackagethe agent needs package-specific guidance in addition to the general rules
Important public caveat
The current documentation homepage states that the documentation is a work in progress and not guaranteed to be correct.
That means a good coding agent should:
prefer documented contracts over inference
avoid inventing undocumented endpoints or package behaviour
call out ambiguous or conflicting docs instead of guessing
Downloadable file: NOTIFIEDBY.md
# NOTIFIEDBY.md
## Purpose
Use this file when writing or modifying application code that integrates with **NotifiedBy** as an external service.
Treat NotifiedBy as a **black-box hosted product**. You do **not** have access to NotifiedBy source code, internal queues, private models, delivery workers, or undocumented implementation details. Build only against the public product surface.
## Allowed Sources of Truth
Use only:
- the public NotifiedBy website
- the public docs at `docs.notifiedby.com`
- the published API base at `https://api.notifiedby.com/v1/`
- documented dashboard behaviour visible to customers
- credentials, sender details, and requirements explicitly provided by the user
If a behaviour is not documented or directly observable from the public product surface, treat it as **unknown**.
## Public Product Model
From the public docs, the integration model is:
- you create a **sender** in the NotifiedBy dashboard
- creating a sender enables generation of an **API key**
- emails are sent through the public API using that API key
- NotifiedBy can optionally wrap email content in the sender's **account template**
- NotifiedBy supports **development mode** on a sender, which reroutes outgoing emails to a configured development recipient
- NotifiedBy supports **unsubscribe placeholders** and an **unsubscribe webhook**
- NotifiedBy also exposes **email flows**, recipients, and recipient flags through the public API
## Important Public Constraints
1. **A sender must exist before code can send mail.**
- Public docs say a sender and API key are prerequisites.
- The sender acts like the mailbox used by the service.
2. **Authentication uses an API key header.**
- Use: `Authorization: Api-Key <key>`
3. **API versioning matters.**
- Public docs use `/v1/` endpoints.
- Do not invent newer versions or alternate paths.
4. **The public docs themselves say they are a work in progress.**
- If documentation pages conflict, do not guess.
- Call out the ambiguity explicitly.
## Core Email Endpoint
Documented send endpoint:
- `POST https://api.notifiedby.com/v1/email/send/`
Documented request fields:
- `recipient` — required
- `subject` — required
- `body` — required HTML body
- `plain_body` — optional plain-text body
- `first_name` — optional
- `last_name` — optional
- `flags` — optional; list or comma-separated string
- `use_account_template` — optional; defaults to `true`
Documented attachment behaviour:
- attachments are supported when sending **multipart form data**
- use the `attachment` field
- multiple files can be attached by repeating the field
## Success and Error Semantics
Documented success response:
- HTTP `200` with JSON like `{ "id": "JDT" }`
Documented blocked-recipient response:
- still HTTP `200`
- body includes `{ "id": "...", "error": "Recipient is blocked" }`
This matters: **a 200 does not always mean an email will actually be delivered.**
Client code should inspect the response body, not only the status code.
Documented error cases include:
- `400` for malformed or missing required fields
- auth errors for missing/invalid API key
- `429` for rate limits such as daily email limits
## Development Mode
Public docs say that if a sender has **Development Mode** enabled:
- emails are redirected to the sender's configured development recipient
- this happens regardless of the `recipient` value sent by the client
Implication for agent-written code:
- do not diagnose recipient mismatches as application bugs until development mode is checked
- document this behaviour in integration notes or test setup if relevant
## Account Template Behaviour
Public docs say `use_account_template` defaults to `true`.
That means:
- by default, NotifiedBy may wrap the HTML body using the sender's configured template
- set `use_account_template=false` only when you intentionally want to send the HTML body as-is
Do not assume the exact shape of the wrapped output beyond what docs state.
## Encryption Support
Public docs show optional support for a custom encryption key via header:
- `Encryption-Key: <key>`
If the integration uses custom encryption:
- only send the header where explicitly required
- do not invent additional encryption protocol behaviour beyond the docs
## Unsubscribe Placeholder
Public docs define the unsubscribe placeholder exactly as:
- `[[notifiedby:unsubscribe]]`
Documented placement:
- in the sender unsubscribe template/footer text
- directly in the HTML email content sent through the API
Documented rendering behaviour:
- NotifiedBy replaces the placeholder at send time with a real unsubscribe anchor tag
- the rendered anchor includes CSS class `unsubscribe-link`
- the visible link text comes from the sender's `unsubscribe_link_text` setting
Important:
- the placeholder is intended for **HTML** email content
- do not assume it works the same way in plain-text content
## Unsubscribe Webhook Contract
Public docs describe a specific webhook model.
When a recipient unsubscribes:
- NotifiedBy validates the token
- adds the recipient to the sender's blocked list
- records the unsubscribe event
- then attempts to call the configured unsubscribe webhook
Important ordering guarantee from docs:
- the unsubscribe is applied inside NotifiedBy **before** webhook delivery is attempted
- even if the client application is down, the recipient remains suppressed in NotifiedBy
Documented webhook URL template placeholders:
- `{sender_slug}`
- `{email}`
Documented delivery format:
- NotifiedBy sends a **POST** request
- useful values are placed in the **URL query string**
- handlers should read `sender` and `email` from query params
- the `sender` query parameter contains the NotifiedBy sender slug
Documented compatibility rule:
- NotifiedBy currently treats the webhook as successful only when the endpoint returns HTTP `200`
- avoid redirects and avoid assuming `202` or `204` will be accepted as success
## Webhook Handler Rules for Client Apps
If your code handles unsubscribe callbacks, it should:
- accept `POST`
- read `sender` from the query string
- read `email` from the query string
- update your own app's unsubscribe state
- return HTTP `200` after applying the change
- be idempotent, because the docs say failed deliveries may be retried
Do not redesign the webhook contract into a JSON body unless public docs change.
## Email Flows
Public docs describe email flows as scheduled multi-step sequences.
Documented related concepts include:
- flow `trigger_keyword`
- recipient records
- recipient flags
- flow subscriptions
- conditional/wait/action steps inside flows
Documented flow endpoints include:
- `POST /v1/flows/subscribe/`
- `POST /v1/flows/unsubscribe/`
- `GET /v1/flows/subscriptions/`
- `DELETE /v1/flows/subscriptions/`
Documented caveat:
- email flows are a **gated feature**
- clients may get `403` if flows are not enabled for the team
So agent-written code should not assume flow APIs are always available on every account.
## Recipient and Flag APIs
Public docs expose recipient and flag management endpoints:
- `POST /v1/recipients/`
- `GET /v1/recipients/`
- `POST /v1/recipients/flags/set/`
- `POST /v1/recipients/flags/clear/`
Use these documented endpoints rather than inventing recipient patch/update routes.
## Integration Design Rules
1. **Use documented contracts literally.**
- Verify paths, methods, headers, field names, and response handling against public docs.
2. **Do not treat examples as proof of undocumented features.**
- If the docs show one example, do not generalise beyond what the docs explicitly state.
3. **Keep NotifiedBy logic isolated.**
- Prefer a dedicated client, adapter, or service layer.
- Avoid scattering NotifiedBy-specific request shapes across the app.
4. **Make failure handling explicit.**
- Surface malformed requests, auth failures, rate limits, and blocked-recipient responses clearly.
5. **Do not infer internal delivery behaviour.**
- Public docs expose delivery status endpoints, but they do not grant knowledge of internal worker logic.
## Verification Checklist
Before finishing an integration task, verify:
- sender/API-key prerequisites are reflected in setup docs or config
- endpoint path and method match public docs
- auth header uses `Authorization: Api-Key <key>`
- request fields match documented names
- response handling checks for blocked-recipient responses as well as HTTP status
- any use of `use_account_template` is intentional
- any unsubscribe placeholder uses the exact token `[[notifiedby:unsubscribe]]`
- any webhook handler reads values from the query string on a `POST`
- webhook success path returns HTTP `200`
- any flow-related code handles the possibility of `403` when the feature is not enabled
- no code depends on guessed internal NotifiedBy behaviour
## Escalation Rule
If the task requires behaviour that is unavailable or ambiguous in the public docs, stop and say so clearly. The right output is an explicit blocker or assumption list, not a fabricated integration.
Downloadable file: NOTIFIEDBY-django.md
# NOTIFIEDBY-django.md
Read `NOTIFIEDBY.md` first.
## Purpose
Use this file when integrating **NotifiedBy** into a **Python/Django** application.
Public docs describe an official package named `django-notifiedby`. Prefer that package where it covers the use case instead of writing raw HTTP calls from scratch.
## Public Package Facts
From the public docs, the Django package provides:
- a Django email backend: `notifiedby.NotifiedByEmailBackend`
- direct API send helpers
- email querying helpers
- flow subscription helpers
- recipient and flag helpers
- exception classes in `notifiedby.exceptions`
- a `NotifiedByClient` for custom API keys or base URLs
Do **not** invent package settings, helper names, exception types, or behaviours beyond the public package docs.
## Installation and Baseline Configuration
Documented installation:
```bash
pip install django-notifiedby
pip install requests
```
Documented Django settings:
```python
INSTALLED_APPS = [
# ...
'notifiedby',
]
EMAIL_BACKEND = "notifiedby.NotifiedByEmailBackend"
NOTIFIEDBY_API_KEY = "YOUR_API_KEY"
```
Optional documented setting for encrypted email content:
```python
NOTIFIEDBY_ENCRYPTION_KEY = "your-encryption-key"
```
## Configuration Rules
- Keep `NOTIFIEDBY_API_KEY` and any encryption key in environment-backed settings or your standard secret-management path.
- Do not hard-code keys in application code.
- Only set `EMAIL_BACKEND` if you intend NotifiedBy to become the default backend for Django email sending.
- Public docs say the backend setting is optional if you only need flow/recipient helpers.
## Decision Rules
### Prefer the Django package when:
- sending standard transactional emails from Django
- using Django's existing email APIs such as `send_mail()` or `EmailMessage`
- subscribing/unsubscribing recipients to flows
- managing recipients or flags
- querying delivery status through documented helpers
### Use direct raw API calls only when:
- the required capability is publicly documented
- the package docs do not expose it
- you keep the fallback isolated and clearly marked as a raw API path
Do not bypass the package just because direct HTTP is quicker to type.
## Sending Email Through Django
Public docs say that when you set:
```python
EMAIL_BACKEND = "notifiedby.NotifiedByEmailBackend"
```
standard Django email functions automatically use NotifiedBy.
Documented examples include:
- `django.core.mail.send_mail(...)`
- `django.core.mail.EmailMessage(...).send()`
- HTML email and attachments through `EmailMessage`
Agent rule:
- prefer standard Django mail objects first when they already fit the use case
- do not write a custom HTTP sender if the backend integration is enough
## Account Template Behaviour in Django
Public docs say the sender's account template is used by default.
Documented package behaviour:
- when using the email backend, set `email.use_account_template = False` before sending if you want to bypass the account template
- when using direct package API sending, pass `use_account_template=False`
Use this only when the app intentionally sends fully rendered HTML as-is.
## Direct Package API Sending
Public docs show direct package use via `send_email_via_api()`.
Implications:
- this is the preferred package-level escape hatch when the Django backend path is too limiting
- prefer this over hand-rolled `requests` code if the package already exposes the needed send behaviour
## Querying Sent Email and Delivery Status
Public docs show helpers:
- `get_email_detail(...)`
- `list_sent_emails(...)`
- `get_email_delivery_status(...)`
Documented delivery status values include:
- `pending`
- `delivered`
- `bounced`
- `complained`
- `failed`
Agent rule:
- if product code tracks post-send state, use these documented helpers/statuses rather than inventing your own interpretation of delivery events
## Working with Flows in Django
Public docs show helpers such as:
- `subscribe_to_flow(...)`
- `unsubscribe_from_flow(...)`
- `list_flow_subscriptions(...)`
- `delete_all_subscriptions()`
- `create_recipient(...)`
- `list_recipients()`
- `set_recipient_flags(...)`
- `clear_recipient_flags(...)`
Documented `subscribe_to_flow(...)` parameters include:
- `flow_trigger`
- `email`
- `first_name`
- `last_name`
- `flags`
- optional `client`
Important public constraint:
- flow features may not be enabled for every account
- auth/feature failures can present as `401`/`403`
So Django code should not assume flow APIs are universally available.
## Error Handling
Public docs say package functions raise exceptions from `notifiedby.exceptions`.
Documented exception types include:
- `NotifiedByError`
- `NotifiedByConfigError`
- `NotifiedByAPIError`
- `NotifiedByValidationError`
- `NotifiedByAuthError`
- `NotifiedByNotFoundError`
Agent rules:
- catch specific NotifiedBy exceptions where useful
- do not collapse everything into bare `except Exception`
- treat config/auth/validation problems differently in user-facing or operational code
Documented meanings:
- config error: missing/invalid local configuration such as absent API key
- validation error: HTTP `400`
- auth error: HTTP `401` or `403`
- not found: HTTP `404`
- API error: other API-side failures
## Custom Client Support
Public docs describe `NotifiedByClient` with documented options:
- `api_key`
- `base_url`
Documented use cases include:
- testing against mock/local servers
- multi-tenant apps using different API keys per organisation
- environment-specific endpoints
Agent rules:
- use `NotifiedByClient` instead of monkey-patching globals when per-tenant or per-environment configuration is needed
- do not invent extra constructor options beyond the public docs
- remember the default base URL in public docs is `https://api.notifiedby.com`
## Recommended Django Integration Shape
Prefer this structure:
- settings layer for `NOTIFIEDBY_API_KEY` and optional `NOTIFIEDBY_ENCRYPTION_KEY`
- dedicated integration module or service wrapper
- thin views/signals/tasks that call that wrapper
- standard Django mail APIs when the email backend is sufficient
- package helper functions for flows/recipients/flags
- targeted exception handling around integration boundaries
Good boundary examples:
- `services/notifiedby.py`
- `integrations/notifiedby/client.py`
- `emails/providers/notifiedby.py`
## Testing Guidance
Use public package features in tests without assuming private internals.
Recommended approach:
- mock at the wrapper or package-call boundary for unit tests
- use `NotifiedByClient(base_url=...)` for controlled test environments when integration-style tests are needed
- test both success and failure paths, especially config/auth/validation errors
- if using flows, test the behaviour when flows are not enabled as well as when they are
Do not make tests depend on undocumented package internals.
## Verification Checklist
Before finishing Django integration work, verify:
- `django-notifiedby` is the documented package being used
- `'notifiedby'` is added to `INSTALLED_APPS` when required
- `NOTIFIEDBY_API_KEY` is configured through approved settings/secrets
- `EMAIL_BACKEND` is only enabled when intended
- use of `send_mail()` / `EmailMessage` / `send_email_via_api()` matches the documented package path
- any `use_account_template=False` usage is intentional
- delivery-status code checks use documented values only
- flow code handles possible `401`/`403` feature/auth failures
- exception handling uses documented `notifiedby.exceptions` classes where appropriate
- any multi-tenant or test-specific behaviour uses `NotifiedByClient`
- no code depends on guessed private package internals
## Escalation Rule
If the required behaviour is not covered by the public Django package docs or the public NotifiedBy API docs, stop and flag the gap. Do not invent a package method, hidden setting, or undocumented workflow.