# Nuntly

[![CI](https://github.com/mindreframer/nuntly/actions/workflows/ci.yml/badge.svg)](https://github.com/mindreframer/nuntly/actions/workflows/ci.yml)
[![Hex.pm](https://img.shields.io/hexpm/v/nuntly.svg)](https://hex.pm/packages/nuntly)
[![Documentation](https://img.shields.io/badge/hex-docs-blue.svg)](https://hexdocs.pm/nuntly)

**A compact Req-backed Elixir client for the [Nuntly REST API](https://nuntly.com/docs)**

Nuntly is generated from the API's OpenAPI document and supports both
configured, Repo-style modules and explicit clients.

## Configured module API

For the common case where an application has one stable configuration, define
a Repo-style module:

```elixir
defmodule MyApp.Nuntly do
  use Nuntly, otp_app: :my_app
end
```

Configure it using the module as the application environment key:

```elixir
# config/runtime.exs
config :my_app, MyApp.Nuntly,
  api_key: System.fetch_env!("NUNTLY_API_KEY")
```

All operations are then available directly, without a client argument:

```elixir
{:ok, response} =
  MyApp.Nuntly.send_email(%{
    from: "hello@example.com",
    to: "person@example.net",
    subject: "Hello",
    text: "Sent from Elixir"
  })

MyApp.Nuntly.list_messages(%{"domainId" => domain_id, limit: 25})
```

`client/0` is overridable when an application needs custom credential lookup or
test behavior.

## Explicit client API

Use the resource modules directly when configuration varies by request or
tenant:

```elixir
client = Nuntly.new(api_key: tenant.api_key)

Nuntly.Emails.send_email(client, payload,
  headers: [{"idempotency-key", idempotency_key}]
)
```

`Nuntly.new/1` also reads `NUNTLY_API_KEY` when `:api_key` is omitted. Other
client options are passed to `Req.new/1`:

```elixir
client = Nuntly.new(receive_timeout: 10_000, retry: :transient)
```

Both APIs follow the same conventions:

- Path values are positional arguments and are URL-escaped.
- JSON payloads are plain maps passed as `body`.
- Query strings are plain maps passed as `params`.
- The final keyword list is passed to Req, allowing headers and other
  per-request options.

The explicit resource API additionally takes the client as its first argument.
Map keys are forwarded unchanged. For camel-cased API fields, use their exact
OpenAPI spelling, usually as strings.

Req returns `{:ok, %Req.Response{}}` for HTTP responses, including non-2xx
statuses, and `{:error, exception}` for transport failures.

See the generated [API reference](https://github.com/mindreframer/nuntly/blob/main/docs/rest-api-reference.md) for every function,
argument, request field, HTTP path, and examples for both calling styles.

The generated API modules are:

- `Nuntly.Agents`
- `Nuntly.ApiKeys`
- `Nuntly.Domains`
- `Nuntly.Emails`
- `Nuntly.Inboxes`
- `Nuntly.Messages`
- `Nuntly.Namespaces`
- `Nuntly.Organizations`
- `Nuntly.Threads`
- `Nuntly.Webhooks`
- `Nuntly.WebhooksEvents`

## Regenerating

The Bun generator lives in the [`generator/`](https://github.com/mindreframer/nuntly/tree/main/generator)
directory:

```bash
cd generator
bun install
bun test
bun run generate
cd ..
mix test
```

See [`generator/README.md`](https://github.com/mindreframer/nuntly/blob/main/generator/README.md)
for its design, options, and coverage commands. Generated Elixir source is kept in
`lib/nuntly/generated/`, and the generated Markdown reference is written to
`docs/rest-api-reference.md`. Custom code should live outside generated files.

## Installation

Add `nuntly` to your dependencies:

```elixir
def deps do
  [
    {:nuntly, "~> 0.1.0"}
  ]
end
```
