# 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.
