# Reloop Documentation (full)
> Full-document snapshot of Reloop docs for long-context agents.
> Hosted on the marketing web app (source of truth for agent files).
> Curated docs index: https://reloop.sh/llms-docs.txt
> Site index: https://reloop.sh/llms.txt
> Product skill: https://reloop.sh/skill.md
> Prefer per-page markdown: append `.md` under https://reloop.sh/docs/
Generated from 382 source files.
---
# introduction.mdx
Source: https://reloop.sh/docs
Markdown: https://reloop.sh/docs/introduction.md
To get started with Reloop, you'll need:
1. Your own domain, [verified with Reloop](/docs/learn/domain)
2. A Reloop [API key](/docs/learn/api-keys)
Then, you'll be ready to send [transactional emails](/docs/learn/emails):
- **Transactional emails:** personalized, event-driven communication
- **SMTP relay:** deliver through any standard SMTP client
## Quickstart
Learn how to get Reloop set up in your project.
### Node.js
### Python
### Go
### Ruby
### PHP
### Rust
### SMTP
---
# learn/ai/api-keys.curl.md
Source: https://reloop.sh/docs/learn/ai/api-keys.curl
Markdown: https://reloop.sh/docs/learn/ai/api-keys.curl.md
# API Keys (cURL)
> Agent-optimized samples for managing Reloop API keys in cURL. Index: [api-keys.md](./api-keys.md)
## Auth reminder
- Header: `x-api-key: rl_...`
- Secret shown once on create/rotate
## Create API key
`POST /api/api-key/v1/`
```bash
curl -X POST https://reloop.sh/api/api-key/v1/ \
-H "x-api-key: rl_123456789" \
-H "Content-Type: application/json" \
-d '{"name": "Production Key"}'
```
## List API keys
`GET /api/api-key/v1/`
```bash
curl "https://reloop.sh/api/api-key/v1/?page=1&limit=10" \
-H "x-api-key: rl_123456789"
```
## Get API key
`GET /api/api-key/v1/:api_key_id`
```bash
curl "https://reloop.sh/api/api-key/v1/key_123456789" \
-H "x-api-key: rl_123456789"
```
## Update API key
`PATCH /api/api-key/v1/:api_key_id`
```bash
curl -X PATCH https://reloop.sh/api/api-key/v1/key_123456789 \
-H "x-api-key: rl_123456789" \
-H "Content-Type: application/json" \
-d '{"name": "Updated Key Name"}'
```
## Rotate API key
`POST /api/api-key/v1/rotate/:api_key_id`
```bash
curl -X POST https://reloop.sh/api/api-key/v1/rotate/key_123456789 \
-H "x-api-key: rl_123456789"
```
## Disable API key
`POST /api/api-key/v1/disable/:api_key_id`
```bash
curl -X POST https://reloop.sh/api/api-key/v1/disable/key_123456789 \
-H "x-api-key: rl_123456789"
```
## Enable API key
`POST /api/api-key/v1/enable/:api_key_id`
```bash
curl -X POST https://reloop.sh/api/api-key/v1/enable/key_123456789 \
-H "x-api-key: rl_123456789"
```
## Delete API key
`DELETE /api/api-key/v1/:api_key_id`
```bash
curl -X DELETE https://reloop.sh/api/api-key/v1/key_123456789 \
-H "x-api-key: rl_123456789"
```
---
# learn/ai/api-keys.dotnet.md
Source: https://reloop.sh/docs/learn/ai/api-keys.dotnet
Markdown: https://reloop.sh/docs/learn/ai/api-keys.dotnet.md
# API Keys (.NET)
> Agent-optimized samples for managing Reloop API keys in .NET. Index: [api-keys.md](./api-keys.md)
## Auth reminder
- Header: `x-api-key: rl_...`
- Secret shown once on create/rotate
## Create API key
`POST /api/api-key/v1/`
```csharp
using Reloop;
using Reloop.Models;
var reloop = new ReloopClient("rl_123456789");
var apiKey = await reloop.ApiKeys.CreateAsync(new CreateApiKeyParams(Name: "Production Key"));
```
## List API keys
`GET /api/api-key/v1/`
```csharp
using Reloop;
using Reloop.Models;
var reloop = new ReloopClient("rl_123456789");
var apiKeys = await reloop.ApiKeys.ListAsync(new ApiKeyListParams
{
Page = 1,
Limit = 10,
});
```
## Get API key
`GET /api/api-key/v1/:api_key_id`
```csharp
using Reloop;
using Reloop.Models;
var reloop = new ReloopClient("rl_123456789");
await reloop.ApiKeys.GetAsync("key_123456789");
```
## Update API key
`PATCH /api/api-key/v1/:api_key_id`
```csharp
using Reloop;
using Reloop.Models;
var reloop = new ReloopClient("rl_123456789");
var apiKey = await reloop.ApiKeys.UpdateAsync("key_123456789", new UpdateApiKeyParams(Name: "Updated Key Name"));
```
## Rotate API key
`POST /api/api-key/v1/rotate/:api_key_id`
```csharp
using Reloop;
using Reloop.Models;
var reloop = new ReloopClient("rl_123456789");
await reloop.ApiKeys.RotateAsync("key_123456789");
```
## Disable API key
`POST /api/api-key/v1/disable/:api_key_id`
```csharp
using Reloop;
using Reloop.Models;
var reloop = new ReloopClient("rl_123456789");
await reloop.ApiKeys.DisableAsync("key_123456789");
```
## Enable API key
`POST /api/api-key/v1/enable/:api_key_id`
```csharp
using Reloop;
using Reloop.Models;
var reloop = new ReloopClient("rl_123456789");
await reloop.ApiKeys.EnableAsync("key_123456789");
```
## Delete API key
`DELETE /api/api-key/v1/:api_key_id`
```csharp
using Reloop;
using Reloop.Models;
var reloop = new ReloopClient("rl_123456789");
await reloop.ApiKeys.DeleteAsync("key_123456789");
```
---
# learn/ai/api-keys.go.md
Source: https://reloop.sh/docs/learn/ai/api-keys.go
Markdown: https://reloop.sh/docs/learn/ai/api-keys.go.md
# API Keys (Go)
> Agent-optimized samples for managing Reloop API keys in Go. Index: [api-keys.md](./api-keys.md)
## Auth reminder
- Header: `x-api-key: rl_...`
- Secret shown once on create/rotate
## Create API key
`POST /api/api-key/v1/`
```go
client, _ := reloop.NewClient(reloop.ClientOptions{
APIKey: "rl_123456789",
})
apiKey, _ := client.ApiKeys.Create(reloop.CreateApiKeyParams{
Name: "Production Key",
})
```
## List API keys
`GET /api/api-key/v1/`
```go
client, _ := reloop.NewClient(reloop.ClientOptions{
APIKey: "rl_123456789",
})
apiKeys, _ := client.ApiKeys.List(&reloop.ApiKeyListParams{
Page: reloop.Int(1),
Limit: reloop.Int(10),
})
```
## Get API key
`GET /api/api-key/v1/:api_key_id`
```go
client, _ := reloop.NewClient(reloop.ClientOptions{
APIKey: "rl_123456789",
})
_, _ = client.ApiKeys.Get("key_123456789")
```
## Update API key
`PATCH /api/api-key/v1/:api_key_id`
```go
client, _ := reloop.NewClient(reloop.ClientOptions{
APIKey: "rl_123456789",
})
apiKey, _ := client.ApiKeys.Update("key_123456789", reloop.UpdateApiKeyParams{
Name: "Updated Key Name",
})
```
## Rotate API key
`POST /api/api-key/v1/rotate/:api_key_id`
```go
client, _ := reloop.NewClient(reloop.ClientOptions{
APIKey: "rl_123456789",
})
_, _ = client.ApiKeys.Rotate("key_123456789")
```
## Disable API key
`POST /api/api-key/v1/disable/:api_key_id`
```go
client, _ := reloop.NewClient(reloop.ClientOptions{
APIKey: "rl_123456789",
})
_, _ = client.ApiKeys.Disable("key_123456789")
```
## Enable API key
`POST /api/api-key/v1/enable/:api_key_id`
```go
client, _ := reloop.NewClient(reloop.ClientOptions{
APIKey: "rl_123456789",
})
_, _ = client.ApiKeys.Enable("key_123456789")
```
## Delete API key
`DELETE /api/api-key/v1/:api_key_id`
```go
client, _ := reloop.NewClient(reloop.ClientOptions{
APIKey: "rl_123456789",
})
_, _ = client.ApiKeys.Delete("key_123456789")
```
---
# learn/ai/api-keys.java.md
Source: https://reloop.sh/docs/learn/ai/api-keys.java
Markdown: https://reloop.sh/docs/learn/ai/api-keys.java.md
# API Keys (Java)
> Agent-optimized samples for managing Reloop API keys in Java. Index: [api-keys.md](./api-keys.md)
## Auth reminder
- Header: `x-api-key: rl_...`
- Secret shown once on create/rotate
## Create API key
`POST /api/api-key/v1/`
```java
ReloopClient reloop = new ReloopClient("rl_123456789");
CreateApiKeyParams params = new CreateApiKeyParams();
params.name = "Production Key";
var apiKey = reloop.apiKey.create(params);
System.out.println(apiKey.id + " " + apiKey.key);
```
## List API keys
`GET /api/api-key/v1/`
```java
ReloopClient reloop = new ReloopClient("rl_123456789");
ApiKeyListParams params = new ApiKeyListParams();
params.page = 1;
params.limit = 10;
params.enabled = true;
var apiKeys = reloop.apiKey.list(params);
System.out.println(apiKeys.total + " " + apiKeys.apiKeys);
```
## Get API key
`GET /api/api-key/v1/:api_key_id`
```java
ReloopClient reloop = new ReloopClient("rl_123456789");
var apiKey = reloop.apiKey.get("key_123456789");
System.out.println(apiKey.id + " " + apiKey.name + " " + apiKey.enabled);
```
## Update API key
`PATCH /api/api-key/v1/:api_key_id`
```java
ReloopClient reloop = new ReloopClient("rl_123456789");
UpdateApiKeyParams params = new UpdateApiKeyParams();
params.name = "Updated Key Name";
var apiKey = reloop.apiKey.update("key_123456789", params);
System.out.println(apiKey.id + " " + apiKey.name);
```
## Rotate API key
`POST /api/api-key/v1/rotate/:api_key_id`
```java
ReloopClient reloop = new ReloopClient("rl_123456789");
var apiKey = reloop.apiKey.rotate("key_123456789");
System.out.println(apiKey.id + " " + apiKey.key);
```
## Disable API key
`POST /api/api-key/v1/disable/:api_key_id`
```java
ReloopClient reloop = new ReloopClient("rl_123456789");
var apiKey = reloop.apiKey.disable("key_123456789");
System.out.println(apiKey.id + " " + apiKey.enabled);
```
## Enable API key
`POST /api/api-key/v1/enable/:api_key_id`
```java
ReloopClient reloop = new ReloopClient("rl_123456789");
var apiKey = reloop.apiKey.enable("key_123456789");
System.out.println(apiKey.id + " " + apiKey.enabled);
```
## Delete API key
`DELETE /api/api-key/v1/:api_key_id`
```java
ReloopClient reloop = new ReloopClient("rl_123456789");
var apiKey = reloop.apiKey.delete("key_123456789");
System.out.println(apiKey.id + " " + apiKey.message);
```
---
# learn/ai/api-keys.md
Source: https://reloop.sh/docs/learn/ai/api-keys
Markdown: https://reloop.sh/docs/learn/ai/api-keys.md
# API Keys (agent guide)
> Prefer this guide over the human dashboard page at `/docs/learn/api-keys`. Use a language-specific file below for runnable SDK samples.
## Auth
- Send `x-api-key: ` on every request.
- Secrets are prefixed with `rl_`.
- Store secrets in env vars / a secret manager. Never commit them.
- The full secret is returned **once** on create and rotate. Reloop stores a hash; it cannot be retrieved again.
## Rules
- **Disable** pauses the key (requests return 401); you can re-enable later.
- **Delete** permanently revokes the key; irreversible.
- API keys work for REST (`x-api-key`) and SMTP (password = secret).
- Creating or managing keys requires an existing authenticated API key.
## Endpoints
| Action | Method | Path |
|--------|--------|------|
| Create | POST | /api/api-key/v1/ |
| List | GET | /api/api-key/v1/ |
| Get | GET | /api/api-key/v1/:api_key_id |
| Update | PATCH | /api/api-key/v1/:api_key_id |
| Rotate | POST | /api/api-key/v1/rotate/:api_key_id |
| Disable | POST | /api/api-key/v1/disable/:api_key_id |
| Enable | POST | /api/api-key/v1/enable/:api_key_id |
| Delete | DELETE | /api/api-key/v1/:api_key_id |
Base URL: `https://reloop.sh`
## Language guides
- [Node](./api-keys.node.md)
- [cURL](./api-keys.curl.md)
- [Python](./api-keys.python.md)
- [PHP](./api-keys.php.md)
- [Java](./api-keys.java.md)
- [.NET](./api-keys.dotnet.md)
- [Go](./api-keys.go.md)
- [Rust](./api-keys.rust.md)
- [Ruby](./api-keys.ruby.md)
## Dashboard (humans)
Dashboard UI walkthrough (GIFs, tabs): [/docs/learn/api-keys](https://reloop.sh/docs/learn/api-keys)
---
# learn/ai/api-keys.node.md
Source: https://reloop.sh/docs/learn/ai/api-keys.node
Markdown: https://reloop.sh/docs/learn/ai/api-keys.node.md
# API Keys (Node)
> Agent-optimized samples for managing Reloop API keys in Node. Index: [api-keys.md](./api-keys.md)
## Auth reminder
- Header: `x-api-key: rl_...`
- Secret shown once on create/rotate
## Create API key
`POST /api/api-key/v1/`
```javascript
const reloop = new Reloop({ apiKey: "rl_123456789" });
const { apiKey, apiKeyError } = await reloop.apiKey.create({
name: "Production Key",
});
if (apiKeyError) throw apiKeyError;
console.log(apiKey.id, apiKey.key);
```
## List API keys
`GET /api/api-key/v1/`
```javascript
const reloop = new Reloop({ apiKey: "rl_123456789" });
const { apiKeys, apiKeyError } = await reloop.apiKey.list({
page: 1,
limit: 10,
enabled: true,
});
if (apiKeyError) throw apiKeyError;
console.log(apiKeys.total, apiKeys.apiKeys);
```
## Get API key
`GET /api/api-key/v1/:api_key_id`
```javascript
const reloop = new Reloop({ apiKey: "rl_123456789" });
const { apiKey, apiKeyError } = await reloop.apiKey.get("key_123456789");
if (apiKeyError) throw apiKeyError;
console.log(apiKey.id, apiKey.name, apiKey.enabled);
```
## Update API key
`PATCH /api/api-key/v1/:api_key_id`
```javascript
const reloop = new Reloop({ apiKey: "rl_123456789" });
const { apiKey, apiKeyError } = await reloop.apiKey.update("key_123456789", {
name: "Updated Key Name",
});
if (apiKeyError) throw apiKeyError;
console.log(apiKey.id, apiKey.name);
```
## Rotate API key
`POST /api/api-key/v1/rotate/:api_key_id`
```javascript
const reloop = new Reloop({ apiKey: "rl_123456789" });
const { apiKey, apiKeyError } = await reloop.apiKey.rotate("key_123456789");
if (apiKeyError) throw apiKeyError;
console.log(apiKey.id, apiKey.key);
```
## Disable API key
`POST /api/api-key/v1/disable/:api_key_id`
```javascript
const reloop = new Reloop({ apiKey: "rl_123456789" });
const { apiKey, apiKeyError } = await reloop.apiKey.disable("key_123456789");
if (apiKeyError) throw apiKeyError;
console.log(apiKey.id, apiKey.enabled);
```
## Enable API key
`POST /api/api-key/v1/enable/:api_key_id`
```javascript
const reloop = new Reloop({ apiKey: "rl_123456789" });
const { apiKey, apiKeyError } = await reloop.apiKey.enable("key_123456789");
if (apiKeyError) throw apiKeyError;
console.log(apiKey.id, apiKey.enabled);
```
## Delete API key
`DELETE /api/api-key/v1/:api_key_id`
```javascript
const reloop = new Reloop({ apiKey: "rl_123456789" });
const { apiKey, apiKeyError } = await reloop.apiKey.delete("key_123456789");
if (apiKeyError) throw apiKeyError;
console.log(apiKey.id, apiKey.message);
```
---
# learn/ai/api-keys.php.md
Source: https://reloop.sh/docs/learn/ai/api-keys.php
Markdown: https://reloop.sh/docs/learn/ai/api-keys.php.md
# API Keys (PHP)
> Agent-optimized samples for managing Reloop API keys in PHP. Index: [api-keys.md](./api-keys.md)
## Auth reminder
- Header: `x-api-key: rl_...`
- Secret shown once on create/rotate
## Create API key
`POST /api/api-key/v1/`
```php
apiKey->create([
'name' => 'Production Key',
]);
echo $apiKey['id'] . ' ' . $apiKey['key'] . PHP_EOL;
```
## List API keys
`GET /api/api-key/v1/`
```php
apiKey->list([
'page' => 1,
'limit' => 10,
'enabled' => true,
]);
echo $apiKeys['total'] . ' ' . $apiKeys['apiKeys'] . PHP_EOL;
```
## Get API key
`GET /api/api-key/v1/:api_key_id`
```php
apiKey->get('key_123456789');
echo $apiKey['id'] . ' ' . $apiKey['name'] . ' ' . $apiKey['enabled'] . PHP_EOL;
```
## Update API key
`PATCH /api/api-key/v1/:api_key_id`
```php
apiKey->update('key_123456789', [
'name' => 'Updated Key Name',
]);
echo $apiKey['id'] . ' ' . $apiKey['name'] . PHP_EOL;
```
## Rotate API key
`POST /api/api-key/v1/rotate/:api_key_id`
```php
apiKey->rotate('key_123456789');
echo $apiKey['id'] . ' ' . $apiKey['key'] . PHP_EOL;
```
## Disable API key
`POST /api/api-key/v1/disable/:api_key_id`
```php
apiKey->disable('key_123456789');
echo $apiKey['id'] . ' ' . $apiKey['enabled'] . PHP_EOL;
```
## Enable API key
`POST /api/api-key/v1/enable/:api_key_id`
```php
apiKey->enable('key_123456789');
echo $apiKey['id'] . ' ' . $apiKey['enabled'] . PHP_EOL;
```
## Delete API key
`DELETE /api/api-key/v1/:api_key_id`
```php
apiKey->delete('key_123456789');
echo $apiKey['id'] . ' ' . $apiKey['message'] . PHP_EOL;
```
---
# learn/ai/api-keys.python.md
Source: https://reloop.sh/docs/learn/ai/api-keys.python
Markdown: https://reloop.sh/docs/learn/ai/api-keys.python.md
# API Keys (Python)
> Agent-optimized samples for managing Reloop API keys in Python. Index: [api-keys.md](./api-keys.md)
## Auth reminder
- Header: `x-api-key: rl_...`
- Secret shown once on create/rotate
## Create API key
`POST /api/api-key/v1/`
```python
from reloop_email import Reloop
reloop = Reloop(api_key="rl_123456789")
result = reloop.api_key.create({
"name": "Production Key",
})
if result.api_key_error:
raise result.api_key_error
print(result.api_key["id"], result.api_key["key"])
```
## List API keys
`GET /api/api-key/v1/`
```python
from reloop_email import Reloop
reloop = Reloop(api_key="rl_123456789")
result = reloop.api_key.list({
"page": 1,
"limit": 10,
"enabled": True,
})
if result.api_key_error:
raise result.api_key_error
print(result.api_keys["total"], result.api_keys["apiKeys"])
```
## Get API key
`GET /api/api-key/v1/:api_key_id`
```python
from reloop_email import Reloop
reloop = Reloop(api_key="rl_123456789")
result = reloop.api_key.get("key_123456789")
if result.api_key_error:
raise result.api_key_error
print(result.api_key["id"], result.api_key["name"], result.api_key["enabled"])
```
## Update API key
`PATCH /api/api-key/v1/:api_key_id`
```python
from reloop_email import Reloop
reloop = Reloop(api_key="rl_123456789")
result = reloop.api_key.update("key_123456789", {
"name": "Updated Key Name",
})
if result.api_key_error:
raise result.api_key_error
print(result.api_key["id"], result.api_key["name"])
```
## Rotate API key
`POST /api/api-key/v1/rotate/:api_key_id`
```python
from reloop_email import Reloop
reloop = Reloop(api_key="rl_123456789")
result = reloop.api_key.rotate("key_123456789")
if result.api_key_error:
raise result.api_key_error
print(result.api_key["id"], result.api_key["key"])
```
## Disable API key
`POST /api/api-key/v1/disable/:api_key_id`
```python
from reloop_email import Reloop
reloop = Reloop(api_key="rl_123456789")
result = reloop.api_key.disable("key_123456789")
if result.api_key_error:
raise result.api_key_error
print(result.api_key["id"], result.api_key["enabled"])
```
## Enable API key
`POST /api/api-key/v1/enable/:api_key_id`
```python
from reloop_email import Reloop
reloop = Reloop(api_key="rl_123456789")
result = reloop.api_key.enable("key_123456789")
if result.api_key_error:
raise result.api_key_error
print(result.api_key["id"], result.api_key["enabled"])
```
## Delete API key
`DELETE /api/api-key/v1/:api_key_id`
```python
from reloop_email import Reloop
reloop = Reloop(api_key="rl_123456789")
result = reloop.api_key.delete("key_123456789")
if result.api_key_error:
raise result.api_key_error
print(result.api_key["id"], result.api_key["message"])
```
---
# learn/ai/api-keys.ruby.md
Source: https://reloop.sh/docs/learn/ai/api-keys.ruby
Markdown: https://reloop.sh/docs/learn/ai/api-keys.ruby.md
# API Keys (Ruby)
> Agent-optimized samples for managing Reloop API keys in Ruby. Index: [api-keys.md](./api-keys.md)
## Auth reminder
- Header: `x-api-key: rl_...`
- Secret shown once on create/rotate
## Create API key
`POST /api/api-key/v1/`
```ruby
require "reloop"
reloop = Reloop::Client.new(api_key: "rl_123456789")
api_key = reloop.api_keys.create(name: "Production Key")
```
## List API keys
`GET /api/api-key/v1/`
```ruby
require "reloop"
reloop = Reloop::Client.new(api_key: "rl_123456789")
api_keys = reloop.api_keys.list(page: 1, limit: 10)
```
## Get API key
`GET /api/api-key/v1/:api_key_id`
```ruby
require "reloop"
reloop = Reloop::Client.new(api_key: "rl_123456789")
reloop.api_keys.get("key_123456789")
```
## Update API key
`PATCH /api/api-key/v1/:api_key_id`
```ruby
require "reloop"
reloop = Reloop::Client.new(api_key: "rl_123456789")
api_key = reloop.api_keys.update("key_123456789", name: "Updated Key Name")
```
## Rotate API key
`POST /api/api-key/v1/rotate/:api_key_id`
```ruby
require "reloop"
reloop = Reloop::Client.new(api_key: "rl_123456789")
reloop.api_keys.rotate("key_123456789")
```
## Disable API key
`POST /api/api-key/v1/disable/:api_key_id`
```ruby
require "reloop"
reloop = Reloop::Client.new(api_key: "rl_123456789")
reloop.api_keys.disable("key_123456789")
```
## Enable API key
`POST /api/api-key/v1/enable/:api_key_id`
```ruby
require "reloop"
reloop = Reloop::Client.new(api_key: "rl_123456789")
reloop.api_keys.enable("key_123456789")
```
## Delete API key
`DELETE /api/api-key/v1/:api_key_id`
```ruby
require "reloop"
reloop = Reloop::Client.new(api_key: "rl_123456789")
reloop.api_keys.delete("key_123456789")
```
---
# learn/ai/api-keys.rust.md
Source: https://reloop.sh/docs/learn/ai/api-keys.rust
Markdown: https://reloop.sh/docs/learn/ai/api-keys.rust.md
# API Keys (Rust)
> Agent-optimized samples for managing Reloop API keys in Rust. Index: [api-keys.md](./api-keys.md)
## Auth reminder
- Header: `x-api-key: rl_...`
- Secret shown once on create/rotate
## Create API key
`POST /api/api-key/v1/`
```rust
use reloop::ReloopClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let reloop = ReloopClient::new("rl_123456789".to_string(), None);
reloop.api_keys().create(CreateApiKeyParams {
name: "Production Key".to_string(),
}).await?;
Ok(())
}
```
## List API keys
`GET /api/api-key/v1/`
```rust
use reloop::ReloopClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let reloop = ReloopClient::new("rl_123456789".to_string(), None);
reloop.api_keys().list(Some(ApiKeyListParams {
page: Some(1),
limit: Some(10),
..Default::default()
})).await?;
Ok(())
}
```
## Get API key
`GET /api/api-key/v1/:api_key_id`
```rust
use reloop::ReloopClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let reloop = ReloopClient::new("rl_123456789".to_string(), None);
reloop.api_keys().get("key_123456789").await?;
Ok(())
}
```
## Update API key
`PATCH /api/api-key/v1/:api_key_id`
```rust
use reloop::ReloopClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let reloop = ReloopClient::new("rl_123456789".to_string(), None);
reloop.api_keys().update("key_123456789", UpdateApiKeyParams {
name: "Updated Key Name".to_string(),
}).await?;
Ok(())
}
```
## Rotate API key
`POST /api/api-key/v1/rotate/:api_key_id`
```rust
use reloop::ReloopClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let reloop = ReloopClient::new("rl_123456789".to_string(), None);
reloop.api_keys().rotate("key_123456789").await?;
Ok(())
}
```
## Disable API key
`POST /api/api-key/v1/disable/:api_key_id`
```rust
use reloop::ReloopClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let reloop = ReloopClient::new("rl_123456789".to_string(), None);
reloop.api_keys().disable("key_123456789").await?;
Ok(())
}
```
## Enable API key
`POST /api/api-key/v1/enable/:api_key_id`
```rust
use reloop::ReloopClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let reloop = ReloopClient::new("rl_123456789".to_string(), None);
reloop.api_keys().enable("key_123456789").await?;
Ok(())
}
```
## Delete API key
`DELETE /api/api-key/v1/:api_key_id`
```rust
use reloop::ReloopClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let reloop = ReloopClient::new("rl_123456789".to_string(), None);
reloop.api_keys().delete("key_123456789").await?;
Ok(())
}
```
---
# learn/api-keys.mdx
Source: https://reloop.sh/docs/learn/api-keys
Markdown: https://reloop.sh/docs/learn/api-keys.md
## View all API Keys
The [API Keys Dashboard](https://reloop.sh/dashboard/api-keys) shows you all the API Keys you have created along with their details, including the last time you used an API Key.

## Create an API key
1. Go to [API Keys](https://reloop.sh/dashboard/api-keys)
2. Click **Create API key** (or press `C`)
3. Enter a descriptive name (e.g., `Production Server` or `Staging Worker`)
4. Click **Create API Key**
5. Copy the secret key, then click **Cancel**
Copy the secret key immediately upon creation. Reloop stores a secure hash, so the full secret can never be retrieved again after closing the dialog.
## Edit an API key
Currently, only the **name** of an API key can be edited. The API key secret itself cannot be changed. If you need a new secret, [rotate the key](#rotate-an-api-key) instead.
1. Go to [API Keys](https://reloop.sh/dashboard/api-keys)
2. Click the row menu (**···**) next to the key you want to rename
3. Select **Edit Api Key**
4. Update the key name and click **Save changes** (or press `Enter`)
## Rotate an API key
Rotate a key when you suspect it may have been exposed, when a team member with access leaves, or as part of regular security hygiene. Rotating issues a new secret and immediately revokes the old one. All previous request logs and usage history stay intact.
1. Open [API Keys](https://reloop.sh/dashboard/api-keys)
2. Click the row menu (**···**) → **Rotate Key**
3. Enter the key name to confirm rotation
4. Click **Rotate Key** (or press `Enter`)
Copy the new secret key immediately upon rotation. Reloop stores a secure hash, so the full secret can never be retrieved again after closing the dialog.
## Disable or enable an API key
Disable a key to temporarily block access without deleting it. You can re-enable it at any time. Common reasons:
- **Security breach**: key was exposed in a public repo or logs.
- **System maintenance**: block requests during upgrades to prevent data corruption.
- **Testing transitions**: verify your system works with a new key before removing the old one.
- **Cost control**: stop billable consumption from a runaway app or loop.
- **Suspicious activity**: pause access while you investigate unusual request patterns.
1. Open [API Keys](https://reloop.sh/dashboard/api-keys)
2. Click the row menu (**···**) → **Disable** (or **Enable** if already paused)
3. The key status updates instantly to **Disabled**
## Delete an API key
If an API Key hasn't been used in the last 30 days, consider deleting it to keep your account secure.
1. Open [API Keys](https://reloop.sh/dashboard/api-keys)
2. Click the row menu (**···**) → **Delete API Key**
3. Enter the key name to confirm permanent removal
4. Click **Delete API Key** (or press `Enter`)
## View all API Keys
Fetch a paginated list of API keys in your organization.
## Create an API key
Use an existing API key to programmatically create new keys for services or team members.
Copy the secret key immediately upon creation. Reloop stores a secure hash, so the full secret can never be retrieved again.
## Edit an API key
## Rotate an API key
## Disable or enable an API key
## Delete an API key
## FAQ
No. Reloop displays the full secret key string only once when created or rotated. After closing the modal, only the prefix (e.g. `rl_prod_17aQUCC-e`) is visible. Reloop stores a secure hash, so if a secret is lost, you must rotate the key or create a new one.
**Disabling** pauses access temporarily. Requests using the key fail with `401 Unauthorized`, but you can re-enable it later. **Deleting** permanently removes the key record and cannot be undone.
Yes. Reloop API keys work across both REST API endpoints (as the `x-api-key` header) and SMTP authentication (using the API key prefix/secret as SMTP credentials).
Reloop does not enforce a hard limit on total API keys per organization. However, we recommend maintaining clean key hygiene by deleting stale or unused keys.
---
# api/api-key/delete-api-api-key-v1by-api_key_id.mdx
Source: https://reloop.sh/docs/api/api-key/delete-api-api-key-v1by-api_key_id
Markdown: https://reloop.sh/docs/api/api-key/delete-api-api-key-v1by-api_key_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/api-key/get-api-api-key-v1.mdx
Source: https://reloop.sh/docs/api/api-key/get-api-api-key-v1
Markdown: https://reloop.sh/docs/api/api-key/get-api-api-key-v1.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/api-key/get-api-api-key-v1by-api_key_id.mdx
Source: https://reloop.sh/docs/api/api-key/get-api-api-key-v1by-api_key_id
Markdown: https://reloop.sh/docs/api/api-key/get-api-api-key-v1by-api_key_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/api-key/patch-api-api-key-v1by-api_key_id.mdx
Source: https://reloop.sh/docs/api/api-key/patch-api-api-key-v1by-api_key_id
Markdown: https://reloop.sh/docs/api/api-key/patch-api-api-key-v1by-api_key_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/api-key/post-api-api-key-v1.mdx
Source: https://reloop.sh/docs/api/api-key/post-api-api-key-v1
Markdown: https://reloop.sh/docs/api/api-key/post-api-api-key-v1.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/api-key/post-api-api-key-v1disable-by-api_key_id.mdx
Source: https://reloop.sh/docs/api/api-key/post-api-api-key-v1disable-by-api_key_id
Markdown: https://reloop.sh/docs/api/api-key/post-api-api-key-v1disable-by-api_key_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/api-key/post-api-api-key-v1enable-by-api_key_id.mdx
Source: https://reloop.sh/docs/api/api-key/post-api-api-key-v1enable-by-api_key_id
Markdown: https://reloop.sh/docs/api/api-key/post-api-api-key-v1enable-by-api_key_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/api-key/post-api-api-key-v1rotate-by-api_key_id.mdx
Source: https://reloop.sh/docs/api/api-key/post-api-api-key-v1rotate-by-api_key_id
Markdown: https://reloop.sh/docs/api/api-key/post-api-api-key-v1rotate-by-api_key_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/channels/delete-api-contacts-v1channels-by-channel_id.mdx
Source: https://reloop.sh/docs/api/contacts/channels/delete-api-contacts-v1channels-by-channel_id
Markdown: https://reloop.sh/docs/api/contacts/channels/delete-api-contacts-v1channels-by-channel_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/channels/get-api-contacts-v1channels-by-channel_id.mdx
Source: https://reloop.sh/docs/api/contacts/channels/get-api-contacts-v1channels-by-channel_id
Markdown: https://reloop.sh/docs/api/contacts/channels/get-api-contacts-v1channels-by-channel_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/channels/get-api-contacts-v1channels-list.mdx
Source: https://reloop.sh/docs/api/contacts/channels/get-api-contacts-v1channels-list
Markdown: https://reloop.sh/docs/api/contacts/channels/get-api-contacts-v1channels-list.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/channels/patch-api-contacts-v1channels-by-channel_id.mdx
Source: https://reloop.sh/docs/api/contacts/channels/patch-api-contacts-v1channels-by-channel_id
Markdown: https://reloop.sh/docs/api/contacts/channels/patch-api-contacts-v1channels-by-channel_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/channels/post-api-contacts-v1channels-create.mdx
Source: https://reloop.sh/docs/api/contacts/channels/post-api-contacts-v1channels-create
Markdown: https://reloop.sh/docs/api/contacts/channels/post-api-contacts-v1channels-create.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/contact-properties/delete-api-contacts-v1properties-by-contact_property_id.mdx
Source: https://reloop.sh/docs/api/contacts/contact-properties/delete-api-contacts-v1properties-by-contact_property_id
Markdown: https://reloop.sh/docs/api/contacts/contact-properties/delete-api-contacts-v1properties-by-contact_property_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/contact-properties/get-api-contacts-v1properties-list.mdx
Source: https://reloop.sh/docs/api/contacts/contact-properties/get-api-contacts-v1properties-list
Markdown: https://reloop.sh/docs/api/contacts/contact-properties/get-api-contacts-v1properties-list.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/contact-properties/patch-api-contacts-v1properties-by-contact_property_id.mdx
Source: https://reloop.sh/docs/api/contacts/contact-properties/patch-api-contacts-v1properties-by-contact_property_id
Markdown: https://reloop.sh/docs/api/contacts/contact-properties/patch-api-contacts-v1properties-by-contact_property_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/contact-properties/post-api-contacts-v1properties-create.mdx
Source: https://reloop.sh/docs/api/contacts/contact-properties/post-api-contacts-v1properties-create
Markdown: https://reloop.sh/docs/api/contacts/contact-properties/post-api-contacts-v1properties-create.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/delete-api-contacts-v1by-contact_id.mdx
Source: https://reloop.sh/docs/api/contacts/delete-api-contacts-v1by-contact_id
Markdown: https://reloop.sh/docs/api/contacts/delete-api-contacts-v1by-contact_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/delete-api-contacts-v1group-by-group_id.mdx
Source: https://reloop.sh/docs/api/contacts/delete-api-contacts-v1group-by-group_id
Markdown: https://reloop.sh/docs/api/contacts/delete-api-contacts-v1group-by-group_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/get-api-contacts-v1export-download.mdx
Source: https://reloop.sh/docs/api/contacts/get-api-contacts-v1export-download
Markdown: https://reloop.sh/docs/api/contacts/get-api-contacts-v1export-download.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/get-api-contacts-v1export.mdx
Source: https://reloop.sh/docs/api/contacts/get-api-contacts-v1export
Markdown: https://reloop.sh/docs/api/contacts/get-api-contacts-v1export.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/get-api-contacts-v1exports-by-export_id.mdx
Source: https://reloop.sh/docs/api/contacts/get-api-contacts-v1exports-by-export_id
Markdown: https://reloop.sh/docs/api/contacts/get-api-contacts-v1exports-by-export_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/get-api-contacts-v1list.mdx
Source: https://reloop.sh/docs/api/contacts/get-api-contacts-v1list
Markdown: https://reloop.sh/docs/api/contacts/get-api-contacts-v1list.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/get-api-contacts-v1retrieve-by-contact_id.mdx
Source: https://reloop.sh/docs/api/contacts/get-api-contacts-v1retrieve-by-contact_id
Markdown: https://reloop.sh/docs/api/contacts/get-api-contacts-v1retrieve-by-contact_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/get-api-contacts-v1stats-subscription-activity.mdx
Source: https://reloop.sh/docs/api/contacts/get-api-contacts-v1stats-subscription-activity
Markdown: https://reloop.sh/docs/api/contacts/get-api-contacts-v1stats-subscription-activity.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/groups/delete-api-contacts-v1groups-by-group_id.mdx
Source: https://reloop.sh/docs/api/contacts/groups/delete-api-contacts-v1groups-by-group_id
Markdown: https://reloop.sh/docs/api/contacts/groups/delete-api-contacts-v1groups-by-group_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/groups/get-api-contacts-v1groups-by-group_id-contacts.mdx
Source: https://reloop.sh/docs/api/contacts/groups/get-api-contacts-v1groups-by-group_id-contacts
Markdown: https://reloop.sh/docs/api/contacts/groups/get-api-contacts-v1groups-by-group_id-contacts.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/groups/get-api-contacts-v1groups-by-group_id.mdx
Source: https://reloop.sh/docs/api/contacts/groups/get-api-contacts-v1groups-by-group_id
Markdown: https://reloop.sh/docs/api/contacts/groups/get-api-contacts-v1groups-by-group_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/groups/get-api-contacts-v1groups-list.mdx
Source: https://reloop.sh/docs/api/contacts/groups/get-api-contacts-v1groups-list
Markdown: https://reloop.sh/docs/api/contacts/groups/get-api-contacts-v1groups-list.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/groups/patch-api-contacts-v1groups-by-group_id.mdx
Source: https://reloop.sh/docs/api/contacts/groups/patch-api-contacts-v1groups-by-group_id
Markdown: https://reloop.sh/docs/api/contacts/groups/patch-api-contacts-v1groups-by-group_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/groups/post-api-contacts-v1groups-create.mdx
Source: https://reloop.sh/docs/api/contacts/groups/post-api-contacts-v1groups-create
Markdown: https://reloop.sh/docs/api/contacts/groups/post-api-contacts-v1groups-create.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/patch-api-contacts-v1by-contact_id.mdx
Source: https://reloop.sh/docs/api/contacts/patch-api-contacts-v1by-contact_id
Markdown: https://reloop.sh/docs/api/contacts/patch-api-contacts-v1by-contact_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/patch-api-contacts-v1channel-by-channel_id.mdx
Source: https://reloop.sh/docs/api/contacts/patch-api-contacts-v1channel-by-channel_id
Markdown: https://reloop.sh/docs/api/contacts/patch-api-contacts-v1channel-by-channel_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/post-api-contacts-v1channel-by-channel_id.mdx
Source: https://reloop.sh/docs/api/contacts/post-api-contacts-v1channel-by-channel_id
Markdown: https://reloop.sh/docs/api/contacts/post-api-contacts-v1channel-by-channel_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/post-api-contacts-v1create.mdx
Source: https://reloop.sh/docs/api/contacts/post-api-contacts-v1create
Markdown: https://reloop.sh/docs/api/contacts/post-api-contacts-v1create.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/post-api-contacts-v1export-email.mdx
Source: https://reloop.sh/docs/api/contacts/post-api-contacts-v1export-email
Markdown: https://reloop.sh/docs/api/contacts/post-api-contacts-v1export-email.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/post-api-contacts-v1group-by-group_id.mdx
Source: https://reloop.sh/docs/api/contacts/post-api-contacts-v1group-by-group_id
Markdown: https://reloop.sh/docs/api/contacts/post-api-contacts-v1group-by-group_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/contacts/post-api-contacts-v1suppressions.mdx
Source: https://reloop.sh/docs/api/contacts/post-api-contacts-v1suppressions
Markdown: https://reloop.sh/docs/api/contacts/post-api-contacts-v1suppressions.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/domain/delete-api-domain-v1by-domain_id.mdx
Source: https://reloop.sh/docs/api/domain/delete-api-domain-v1by-domain_id
Markdown: https://reloop.sh/docs/api/domain/delete-api-domain-v1by-domain_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/domain/get-api-domain-v1by-domain_id.mdx
Source: https://reloop.sh/docs/api/domain/get-api-domain-v1by-domain_id
Markdown: https://reloop.sh/docs/api/domain/get-api-domain-v1by-domain_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/domain/get-api-domain-v1list.mdx
Source: https://reloop.sh/docs/api/domain/get-api-domain-v1list
Markdown: https://reloop.sh/docs/api/domain/get-api-domain-v1list.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/domain/get-api-domain-v1nameservers-by-domain_id.mdx
Source: https://reloop.sh/docs/api/domain/get-api-domain-v1nameservers-by-domain_id
Markdown: https://reloop.sh/docs/api/domain/get-api-domain-v1nameservers-by-domain_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/domain/patch-api-domain-v1by-domain_id.mdx
Source: https://reloop.sh/docs/api/domain/patch-api-domain-v1by-domain_id
Markdown: https://reloop.sh/docs/api/domain/patch-api-domain-v1by-domain_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/domain/post-api-domain-v1create.mdx
Source: https://reloop.sh/docs/api/domain/post-api-domain-v1create
Markdown: https://reloop.sh/docs/api/domain/post-api-domain-v1create.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/domain/post-api-domain-v1verify-by-domain_id-forward-dns.mdx
Source: https://reloop.sh/docs/api/domain/post-api-domain-v1verify-by-domain_id-forward-dns
Markdown: https://reloop.sh/docs/api/domain/post-api-domain-v1verify-by-domain_id-forward-dns.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/domain/post-api-domain-v1verify-by-domain_id.mdx
Source: https://reloop.sh/docs/api/domain/post-api-domain-v1verify-by-domain_id
Markdown: https://reloop.sh/docs/api/domain/post-api-domain-v1verify-by-domain_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/errors.mdx
Source: https://reloop.sh/docs/api/errors
Markdown: https://reloop.sh/docs/api/errors.md
Reloop uses standard HTTP response codes to indicate the success or failure of an API request. Every error response includes structured fields so you can programmatically handle failures without parsing human-readable messages.
## Error Response Format
Every error response is a JSON object matching the `Error` schema in the [OpenAPI document](https://reloop.sh/openapi.json):
```json
{
"code": "not_found",
"message": "Domain not found",
"why": "The domain acme.com was not found or is not authorized for your organization",
"fix": "Ensure the domain is registered and verified in your dashboard",
"link": "https://reloop.sh/docs/api/errors"
}
```
| Field | Type | Description |
|-------|------|-------------|
| `code` | `string` | A stable, machine-readable error code. Branch on this, not on `message`. Always present. |
| `message` | `string` | A short summary of what went wrong (e.g., `"Domain not found"`). Always present. |
| `why` | `string` | A detailed explanation of what triggered the error, often including the offending value. |
| `fix` | `string` | Actionable steps to resolve the issue. |
| `link` | `string` | A URL to the relevant documentation page. Always present. |
| `errors` | `array` | On `validation_error` only: one `{ "path", "message" }` entry per invalid field, where `path` is a JSON Pointer such as `/email`. |
## Error Codes
| `code` | Status | Meaning |
|--------|--------|---------|
| `bad_request` | `400` | The request was invalid or its body could not be parsed. |
| `unauthorized` | `401` | The API key or token is missing, invalid or revoked. |
| `payment_required` | `402` | Your plan or credits do not cover this request. |
| `forbidden` | `403` | You are authenticated but not allowed to do this. |
| `not_found` | `404` | The resource or endpoint does not exist. |
| `conflict` | `409` | The request conflicts with an existing resource. |
| `payload_too_large` | `413` | The request body or an attachment is too large. |
| `validation_error` | `422` | The request did not match the endpoint's schema. See `errors`. |
| `rate_limited` | `429` | You exceeded a [rate limit](/docs/api/usage-limits). |
| `internal_error` | `500` | Something went wrong on Reloop's side. |
| `service_unavailable` | `503` | A dependency is temporarily unavailable. Retry later. |
| `invalid_idempotency_key` | `400` | The `Idempotency-Key` header is too long. See [Idempotency](/docs/api/idempotency). |
| `idempotency_in_progress` | `409` | A request with the same `Idempotency-Key` is still running. |
| `idempotency_key_reused` | `422` | The `Idempotency-Key` was used before with a different request. |
New codes may be added. Treat an unknown `code` by its HTTP status.
## Error Codes Reference
### 400 Bad Request
The request was invalid. This usually means a required parameter is missing, a value is the wrong type, or an input failed validation.
**Common causes:**
- Missing required fields in the request body.
- A DNS health check failed because records are not configured correctly.
- An email body was missing both `html` and `text` content, and the referenced template had no rendered HTML.
```json
{
"code": "bad_request",
"message": "DNS health check failed",
"why": "Domain acme.com is missing required DNS records: SPF, DKIM",
"fix": "Update your DNS configuration with the required SPF, DKIM, and DMARC records"
}
```
---
### 401 Unauthorized
The API key is missing, invalid, or has been revoked.
**Common causes:**
- The `x-api-key` header was not included in the request.
- The API key has been disabled or rotated.
- The key format is incorrect (valid keys start with `rl_prod_`).
```json
{
"code": "unauthorized",
"message": "Unauthorized",
"why": "No valid API key was provided in the x-api-key header",
"fix": "Please provide valid credentials"
}
```
---
### 403 Forbidden
The API key is valid, but it doesn't have permission to access the requested resource.
**Common causes:**
- Attempting to access a resource that belongs to a different organization.
- The authenticated user is not a member of the organization tied to the API key.
```json
{
"code": "forbidden",
"message": "Forbidden",
"why": "User is not a member of an organization",
"fix": "Ensure your API key is associated with the correct organization"
}
```
---
### 404 Not Found
The requested resource doesn't exist or isn't accessible to your organization.
**Common causes:**
- The resource ID is incorrect or was deleted.
- The resource belongs to a different organization than your API key.
```json
{
"code": "not_found",
"message": "Domain not found",
"why": "The domain acme.com was not found or is not authorized for your organization",
"fix": "Ensure the domain is registered and verified in your dashboard"
}
```
---
### 409 Conflict
The request conflicts with an existing resource.
**Common causes:**
- Creating a domain that is already registered.
- Creating a channel or contact property with a name that already exists in your organization.
```json
{
"code": "conflict",
"message": "Channel already exists",
"why": "A channel with the name 'Weekly Newsletter' already exists in your organization",
"fix": "Use a unique channel name or update the existing channel"
}
```
---
### 422 Validation Error
The request body, query or path parameters did not match the endpoint's schema. `errors` lists each invalid field.
```json
{
"code": "validation_error",
"message": "Invalid request",
"why": "The body did not match the schema for this endpoint.",
"fix": "Fix the fields listed in `errors` and retry. Schemas are in the OpenAPI document at https://reloop.sh/openapi.json.",
"link": "https://reloop.sh/docs/api/errors",
"errors": [
{ "path": "/to", "message": "Expected string" }
]
}
```
---
### 429 Too Many Requests
You've exceeded a rate limit. The response includes additional fields and headers to help you retry.
```json
{
"code": "rate_limited",
"message": "Too many requests",
"why": "You have exceeded the limit of 10 requests per 60s window for this operation.",
"fix": "Wait 42 second(s) before retrying.",
"retryAfter": 42
}
```
Check the `Retry-After` response header for the number of seconds to wait, or use the `retryAfter` field in the body. See [Usage Limits](/docs/api/usage-limits) for per-endpoint limits.
---
### 500 Internal Server Error
Something went wrong on Reloop's end. These errors are automatically logged and investigated.
**What to do:**
- Retry the request after a short delay.
- If the issue persists, contact [support@reloop.sh](mailto:support@reloop.sh) with the request details and timestamp.
---
# api/idempotency.mdx
Source: https://reloop.sh/docs/api/idempotency
Markdown: https://reloop.sh/docs/api/idempotency.md
Network failures and timeouts leave you unsure whether a request went through. Send an `Idempotency-Key` header on `POST` requests and you can retry them without sending the same email twice or creating duplicate resources.
## How it works
Generate a unique key for each operation, such as a UUID, and send it with the request. If you retry with the same key and the same body, Reloop returns the original response instead of running the request again.
```bash
curl -X POST "https://reloop.sh/api/contacts/create" \
-H "x-api-key: rl_prod_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5f1c2a9e-8d3b-4c7a-9f60-2b7e1d4a8c35" \
-d '{"email": "ada@example.com"}'
```
A replayed response has the same status code and body as the original, plus this header:
```http
Idempotent-Replayed: true
```
## Rules
- **Scope.** Keys are scoped to the API key (or dashboard session) that sent them. Two different API keys can use the same key value without colliding.
- **Lifetime.** A successful response can be replayed for 24 hours. After that the key can be reused.
- **Length.** Keys can be up to 255 characters. Sending email accepts up to 500.
- **Same request only.** Reusing a key with a different body or a different endpoint returns `422`.
- **Failures are not stored.** If the request fails (any non-`2xx` response), the key is released, so you can fix the problem and retry with the same key.
- **JSON bodies only.** File uploads do not support idempotency keys.
## Error responses
| Status | When | What to do |
|--------|------|------------|
| `400` | The key is longer than 255 characters. | Use a shorter key, such as a UUID. |
| `409` | A request with this key is still running. The response includes `Retry-After: 1`. | Wait and retry with the same key to get the original result. |
| `422` | The key was already used with a different body or endpoint. | Use a new key for each distinct request. |
## Supported endpoints
Every `POST` endpoint in these services accepts `Idempotency-Key`: send email, agent inboxes, domains, webhooks, contacts, automations, templates and campaigns. The [OpenAPI document](https://reloop.sh/openapi.json) marks each supported operation.
These endpoints do not accept it because their responses contain secrets that Reloop will not store for replay:
- Creating an API key, and rotating one
- Creating a webhook, which returns its signing secret
## Sending email
`POST /api/mail/v1/send` stores keys with the email log rather than for 24 hours, so a retry returns the original send for as long as the log exists. If the earlier attempt failed to transmit, a retry with the same key sends the email again. You can also pass the key as `idempotency_key` in the body; the header takes precedence.
---
# api/inbox/mailboxes/delete-api-inbox-v1mailboxes-by-id.mdx
Source: https://reloop.sh/docs/api/inbox/mailboxes/delete-api-inbox-v1mailboxes-by-id
Markdown: https://reloop.sh/docs/api/inbox/mailboxes/delete-api-inbox-v1mailboxes-by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/mailboxes/get-api-inbox-v1mailboxes-by-id.mdx
Source: https://reloop.sh/docs/api/inbox/mailboxes/get-api-inbox-v1mailboxes-by-id
Markdown: https://reloop.sh/docs/api/inbox/mailboxes/get-api-inbox-v1mailboxes-by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/mailboxes/get-api-inbox-v1mailboxes-list.mdx
Source: https://reloop.sh/docs/api/inbox/mailboxes/get-api-inbox-v1mailboxes-list
Markdown: https://reloop.sh/docs/api/inbox/mailboxes/get-api-inbox-v1mailboxes-list.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/mailboxes/patch-api-inbox-v1mailboxes-by-id.mdx
Source: https://reloop.sh/docs/api/inbox/mailboxes/patch-api-inbox-v1mailboxes-by-id
Markdown: https://reloop.sh/docs/api/inbox/mailboxes/patch-api-inbox-v1mailboxes-by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/mailboxes/post-api-inbox-v1mailboxes-create.mdx
Source: https://reloop.sh/docs/api/inbox/mailboxes/post-api-inbox-v1mailboxes-create
Markdown: https://reloop.sh/docs/api/inbox/mailboxes/post-api-inbox-v1mailboxes-create.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/delete-api-inbox-v1messages-by-id.mdx
Source: https://reloop.sh/docs/api/inbox/messages/delete-api-inbox-v1messages-by-id
Markdown: https://reloop.sh/docs/api/inbox/messages/delete-api-inbox-v1messages-by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/get-api-inbox-v1messages-by-id-attachments-by-attachment-id.mdx
Source: https://reloop.sh/docs/api/inbox/messages/get-api-inbox-v1messages-by-id-attachments-by-attachment-id
Markdown: https://reloop.sh/docs/api/inbox/messages/get-api-inbox-v1messages-by-id-attachments-by-attachment-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/get-api-inbox-v1messages-by-id-raw.mdx
Source: https://reloop.sh/docs/api/inbox/messages/get-api-inbox-v1messages-by-id-raw
Markdown: https://reloop.sh/docs/api/inbox/messages/get-api-inbox-v1messages-by-id-raw.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/get-api-inbox-v1messages-by-id.mdx
Source: https://reloop.sh/docs/api/inbox/messages/get-api-inbox-v1messages-by-id
Markdown: https://reloop.sh/docs/api/inbox/messages/get-api-inbox-v1messages-by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/get-api-inbox-v1messages.mdx
Source: https://reloop.sh/docs/api/inbox/messages/get-api-inbox-v1messages
Markdown: https://reloop.sh/docs/api/inbox/messages/get-api-inbox-v1messages.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/patch-api-inbox-v1messages-by-id-read.mdx
Source: https://reloop.sh/docs/api/inbox/messages/patch-api-inbox-v1messages-by-id-read
Markdown: https://reloop.sh/docs/api/inbox/messages/patch-api-inbox-v1messages-by-id-read.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/patch-api-inbox-v1messages-by-id-star.mdx
Source: https://reloop.sh/docs/api/inbox/messages/patch-api-inbox-v1messages-by-id-star
Markdown: https://reloop.sh/docs/api/inbox/messages/patch-api-inbox-v1messages-by-id-star.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/patch-api-inbox-v1messages-by-id.mdx
Source: https://reloop.sh/docs/api/inbox/messages/patch-api-inbox-v1messages-by-id
Markdown: https://reloop.sh/docs/api/inbox/messages/patch-api-inbox-v1messages-by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/post-api-inbox-v1messages-batch.mdx
Source: https://reloop.sh/docs/api/inbox/messages/post-api-inbox-v1messages-batch
Markdown: https://reloop.sh/docs/api/inbox/messages/post-api-inbox-v1messages-batch.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/post-api-inbox-v1messages-by-id-forward.mdx
Source: https://reloop.sh/docs/api/inbox/messages/post-api-inbox-v1messages-by-id-forward
Markdown: https://reloop.sh/docs/api/inbox/messages/post-api-inbox-v1messages-by-id-forward.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/post-api-inbox-v1messages-by-id-reply-all.mdx
Source: https://reloop.sh/docs/api/inbox/messages/post-api-inbox-v1messages-by-id-reply-all
Markdown: https://reloop.sh/docs/api/inbox/messages/post-api-inbox-v1messages-by-id-reply-all.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/post-api-inbox-v1messages-by-id-reply.mdx
Source: https://reloop.sh/docs/api/inbox/messages/post-api-inbox-v1messages-by-id-reply
Markdown: https://reloop.sh/docs/api/inbox/messages/post-api-inbox-v1messages-by-id-reply.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/messages/post-api-inbox-v1messages-send.mdx
Source: https://reloop.sh/docs/api/inbox/messages/post-api-inbox-v1messages-send
Markdown: https://reloop.sh/docs/api/inbox/messages/post-api-inbox-v1messages-send.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/threads/delete-api-inbox-v1threads-by-id.mdx
Source: https://reloop.sh/docs/api/inbox/threads/delete-api-inbox-v1threads-by-id
Markdown: https://reloop.sh/docs/api/inbox/threads/delete-api-inbox-v1threads-by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/threads/get-api-inbox-v1threads-by-id-attachments-by-attachment-id.mdx
Source: https://reloop.sh/docs/api/inbox/threads/get-api-inbox-v1threads-by-id-attachments-by-attachment-id
Markdown: https://reloop.sh/docs/api/inbox/threads/get-api-inbox-v1threads-by-id-attachments-by-attachment-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/threads/get-api-inbox-v1threads-by-id.mdx
Source: https://reloop.sh/docs/api/inbox/threads/get-api-inbox-v1threads-by-id
Markdown: https://reloop.sh/docs/api/inbox/threads/get-api-inbox-v1threads-by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/threads/get-api-inbox-v1threads.mdx
Source: https://reloop.sh/docs/api/inbox/threads/get-api-inbox-v1threads
Markdown: https://reloop.sh/docs/api/inbox/threads/get-api-inbox-v1threads.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/threads/patch-api-inbox-v1threads-by-id-read.mdx
Source: https://reloop.sh/docs/api/inbox/threads/patch-api-inbox-v1threads-by-id-read
Markdown: https://reloop.sh/docs/api/inbox/threads/patch-api-inbox-v1threads-by-id-read.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/threads/patch-api-inbox-v1threads-by-id-star.mdx
Source: https://reloop.sh/docs/api/inbox/threads/patch-api-inbox-v1threads-by-id-star
Markdown: https://reloop.sh/docs/api/inbox/threads/patch-api-inbox-v1threads-by-id-star.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/threads/patch-api-inbox-v1threads-by-id.mdx
Source: https://reloop.sh/docs/api/inbox/threads/patch-api-inbox-v1threads-by-id
Markdown: https://reloop.sh/docs/api/inbox/threads/patch-api-inbox-v1threads-by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/inbox/threads/post-api-inbox-v1threads-by-id-archive.mdx
Source: https://reloop.sh/docs/api/inbox/threads/post-api-inbox-v1threads-by-id-archive
Markdown: https://reloop.sh/docs/api/inbox/threads/post-api-inbox-v1threads-by-id-archive.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/index.mdx
Source: https://reloop.sh/docs/api
Markdown: https://reloop.sh/docs/api.md
Welcome to the Reloop API Reference. The Reloop API is built on **REST** principles. We enforce HTTPS in every request to improve data security, integrity, and privacy.
## Authentication
Every request must include your API key in the `x-api-key` header. API keys are scoped to a single **organization**: all resources you create, read, or modify are automatically associated with that organization.
```bash
curl -X GET "https://reloop.sh/api/domain/v1/list" \
-H "x-api-key: rl_prod_YOUR_API_KEY"
```
You can create, rotate, and revoke API keys from the [Reloop Dashboard](https://reloop.sh/dashboard/api-keys).
### Required Headers
| Header | Value | When |
|--------|-------|------|
| `x-api-key` | Your Reloop API key (prefixed `rl_prod_`) | Every request |
| `Content-Type` | `application/json` | Requests with a body (`POST`, `PUT`, `PATCH`) |
## Response Format
All responses, including errors, are returned as JSON. Successful responses typically include an `event` field describing the action performed:
```json
{
"event": "contact.created",
...
}
```
## Response Codes
The API uses standard HTTP status codes to indicate the outcome of a request:
| Code | Meaning | When You'll See It |
|------|---------|-------------------|
| `200` | OK | Request succeeded. |
| `201` | Created | A new resource was created successfully. |
| `202` | Accepted | A background job was started. Poll the URL in the `Location` header (see [Long-running operations](#long-running-operations)). |
| `400` | Bad Request | Missing or invalid parameters. Check the `why` field for details. |
| `401` | Unauthorized | The `x-api-key` header is missing, invalid, or the key has been revoked. |
| `403` | Forbidden | The API key doesn't have permission to access the requested resource. |
| `404` | Not Found | The resource doesn't exist, or it belongs to a different organization. |
| `409` | Conflict | A resource with the same unique identifier already exists (e.g., duplicate domain or channel name), or a request with the same [Idempotency-Key](/docs/api/idempotency) is still running. |
| `422` | Unprocessable Content | The request did not match the endpoint's schema (`errors` lists each field), or an [Idempotency-Key](/docs/api/idempotency) was reused with a different request. |
| `429` | Too Many Requests | You've exceeded a [rate limit](/docs/api/usage-limits). Retry after the `Retry-After` header value. |
| `500` | Internal Server Error | Something went wrong on our end. If the issue persists, contact [support](mailto:support@reloop.sh). |
## Long-running operations
Operations that take longer than a request, such as emailing a contacts export, return `202 Accepted` right away and run in the background. The response includes:
- A `Location` header with the URL of the job's status resource (also in the body as `statusUrl`).
- A `Retry-After` header with the seconds to wait before polling.
```bash
curl -X POST "https://reloop.sh/api/contacts/v1/export/email" \
-H "x-api-key: rl_prod_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "subscribed"}'
# HTTP/1.1 202 Accepted
# Location: /api/contacts/v1/exports/exp_abc123
# Retry-After: 5
curl "https://reloop.sh/api/contacts/v1/exports/exp_abc123" \
-H "x-api-key: rl_prod_YOUR_API_KEY"
# { "object": "contact_export", "id": "exp_abc123", "status": "processing", ... }
```
Poll the `Location` URL with `GET` until `status` is final (`ready` or `failed`), waiting `Retry-After` seconds between requests. Job status is kept for 7 days.
## Errors
When a request fails, the response body is a JSON object with a stable `code` plus fields that help you diagnose and fix the issue:
```json
{
"code": "not_found",
"message": "Domain not found",
"why": "The domain acme.com was not found or is not authorized for your organization",
"fix": "Ensure the domain is registered and verified in your dashboard",
"link": "https://reloop.sh/docs/api/errors"
}
```
| Field | Description |
|-------|-------------|
| `code` | A stable, machine-readable error code such as `validation_error` or `rate_limited`. Branch on this. |
| `message` | A short, human-readable summary of the error. |
| `why` | A detailed explanation of what went wrong and what triggered the error. |
| `fix` | Actionable steps you can take to resolve the issue. |
| `link` | A URL to the relevant documentation page for more context. |
See the [Errors](/docs/api/errors) page for a full reference of common error codes and troubleshooting tips.
## Rate Limits
Rate limits are applied per **organization** and vary by endpoint. Every API response includes headers so you can monitor your usage:
| Header | Description |
|--------|-------------|
| `X-RateLimit-Limit` | Maximum requests allowed in the current window. |
| `X-RateLimit-Remaining` | Requests remaining before the limit is reached. |
| `X-RateLimit-Window` | Window size in seconds (management endpoints like domains, contacts, and channels). |
| `X-RateLimit-Reset` | Unix epoch timestamp when the window resets (email sending endpoints). |
| `Retry-After` | Seconds to wait before retrying (only returned with `429` responses). |
For the full breakdown of per-endpoint limits and the multi-layer email sending strategy, see [Usage Limits](/docs/api/usage-limits).
---
# api/logs/get-api-logs-v1by-log_id.mdx
Source: https://reloop.sh/docs/api/logs/get-api-logs-v1by-log_id
Markdown: https://reloop.sh/docs/api/logs/get-api-logs-v1by-log_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/logs/get-api-logs-v1emails-by-id.mdx
Source: https://reloop.sh/docs/api/logs/get-api-logs-v1emails-by-id
Markdown: https://reloop.sh/docs/api/logs/get-api-logs-v1emails-by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/logs/get-api-logs-v1emails-contact-activity.mdx
Source: https://reloop.sh/docs/api/logs/get-api-logs-v1emails-contact-activity
Markdown: https://reloop.sh/docs/api/logs/get-api-logs-v1emails-contact-activity.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/logs/get-api-logs-v1emails-stats.mdx
Source: https://reloop.sh/docs/api/logs/get-api-logs-v1emails-stats
Markdown: https://reloop.sh/docs/api/logs/get-api-logs-v1emails-stats.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/logs/get-api-logs-v1emails.mdx
Source: https://reloop.sh/docs/api/logs/get-api-logs-v1emails
Markdown: https://reloop.sh/docs/api/logs/get-api-logs-v1emails.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/logs/get-api-logs-v1list.mdx
Source: https://reloop.sh/docs/api/logs/get-api-logs-v1list
Markdown: https://reloop.sh/docs/api/logs/get-api-logs-v1list.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/mail/post-api-mail-v1send.mdx
Source: https://reloop.sh/docs/api/mail/post-api-mail-v1send
Markdown: https://reloop.sh/docs/api/mail/post-api-mail-v1send.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/pagination.mdx
Source: https://reloop.sh/docs/api/pagination
Markdown: https://reloop.sh/docs/api/pagination.md
List endpoints support two ways to page through results:
- **Cursor pagination (recommended).** Pass the `nextCursor` from one response as `cursor` in the next request. Pages stay stable while new records are created, so you never skip or repeat a record. Use this for syncing, exports and agents.
- **Page numbers.** Pass `page` and `limit`. Useful for numbered pages in a UI.
Every list response includes the fields for both.
## Cursor Pagination
Request the first page without a cursor, then follow `nextCursor` until `hasMore` is `false`:
```bash
curl "https://reloop.sh/api/contacts/v1/list?limit=100" \
-H "x-api-key: rl_prod_YOUR_API_KEY"
# Response includes: "hasMore": true, "nextCursor": "eyJ0IjoiMjAyNi0xMC0yNVQx..."
curl "https://reloop.sh/api/contacts/v1/list?limit=100&cursor=eyJ0IjoiMjAyNi0xMC0yNVQx..." \
-H "x-api-key: rl_prod_YOUR_API_KEY"
```
```javascript
let cursor = null;
do {
const url = new URL("https://reloop.sh/api/contacts/v1/list");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers: { "x-api-key": process.env.RELOOP_API_KEY } });
const page = await res.json();
for (const contact of page.contacts) handle(contact);
cursor = page.nextCursor;
} while (cursor);
```
| Field | Type | Description |
|-------|------|-------------|
| `hasMore` | `boolean` | Whether more results exist after this page. |
| `nextCursor` | `string \| null` | Pass as `cursor` to get the next page. `null` on the last page. |
Rules:
- Treat cursors as opaque strings. Pass them back unchanged; do not build or edit them. An invalid cursor returns `400`.
- Keep the same filters (and `limit`, if you like) on every request in a walk. A cursor only marks a position.
- When `cursor` is set, `page` is ignored. You can start in page mode and switch: every page-mode response also returns a `nextCursor`.
- Results are ordered newest first unless an endpoint says otherwise.
- A few endpoints offer alternative sort orders. Cursors only work with the default order; combining `cursor` with another sort returns `400`, and `nextCursor` is `null` for those sorts:
- List contacts with `suppressed=true` (sorted by suppression date).
- List API keys with a `sort` other than `createdAt:desc`.
- Campaign recipients with `category=clicked` (sorted by clicks).
### Exceptions
- **Automations and custom events** use page numbers only. They are sorted by last update, which changes when a record is edited, so a cursor would not be stable. These lists are small; fetch them with `limit=100`.
- **Agent inbox messages and threads** use `limit` and `offset` and return a plain array.
- **Group members** (`/api/contacts/v1/groups/{group_id}/contacts`) are ordered oldest first.
## Page Numbers
### Query Parameters
| Parameter | Type | Default | Max | Description |
|-----------|------|---------|-----|-------------|
| `page` | `number` | `1` | - | The page number to retrieve (1-indexed). |
| `limit` | `number` | `20` | `100` | The number of records to return per page. |
**Example request:**
```bash
curl "https://reloop.sh/api/contacts/v1/list?page=2&limit=50" \
-H "x-api-key: rl_prod_YOUR_API_KEY"
```
### Response Structure
Pagination metadata is returned at the **root** of the JSON response alongside the data array. The exact field names vary by endpoint, but every paginated response includes `total`, `page`, and `limit`.
#### Contacts
The contacts list endpoint includes additional aggregate counts:
```json
{
"object": "contact",
"contacts": [...],
"total": 120,
"page": 1,
"limit": 20,
"hasMore": true,
"nextCursor": "eyJ0IjoiMjAyNi0xMC0yNVQx...",
"totalContacts": 120,
"subscribedContacts": 100,
"unsubscribedContacts": 20,
"event": "contact.list"
}
```
| Field | Type | Description |
|-------|------|-------------|
| `contacts` | `array` | The list of contact objects for the current page. |
| `total` | `number` | Total number of contacts matching the query. |
| `page` | `number` | The current page number. |
| `limit` | `number` | The page size used for this request. |
| `totalContacts` | `number` | Total contacts in the organization. |
| `subscribedContacts` | `number` | Number of contacts with an active subscription. |
| `unsubscribedContacts` | `number` | Number of contacts who have unsubscribed. |
#### Templates
```json
{
"templates": [...],
"total": 45,
"page": 1,
"limit": 20,
"hasMore": true,
"nextCursor": "eyJ0IjoiMjAyNi0xMC0yNVQx...",
"event": "template.list"
}
```
| Field | Type | Description |
|-------|------|-------------|
| `templates` | `array` | The list of template objects for the current page. |
| `total` | `number` | Total number of templates matching the query. |
| `page` | `number` | The current page number. |
| `limit` | `number` | The page size used for this request. |
### Calculating Total Pages
Use the `total` and `limit` fields to calculate the number of pages:
```javascript
const totalPages = Math.ceil(response.total / response.limit);
const hasNextPage = response.page < totalPages;
```
---
# api/template/delete-api-template-v1by-id-versions-by-version-id.mdx
Source: https://reloop.sh/docs/api/template/delete-api-template-v1by-id-versions-by-version-id
Markdown: https://reloop.sh/docs/api/template/delete-api-template-v1by-id-versions-by-version-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/template/delete-api-template-v1by-id.mdx
Source: https://reloop.sh/docs/api/template/delete-api-template-v1by-id
Markdown: https://reloop.sh/docs/api/template/delete-api-template-v1by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/template/get-api-template-v1by-id-versions.mdx
Source: https://reloop.sh/docs/api/template/get-api-template-v1by-id-versions
Markdown: https://reloop.sh/docs/api/template/get-api-template-v1by-id-versions.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/template/get-api-template-v1by-id.mdx
Source: https://reloop.sh/docs/api/template/get-api-template-v1by-id
Markdown: https://reloop.sh/docs/api/template/get-api-template-v1by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/template/get-api-template-v1list.mdx
Source: https://reloop.sh/docs/api/template/get-api-template-v1list
Markdown: https://reloop.sh/docs/api/template/get-api-template-v1list.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/template/post-api-template-v1by-id-duplicate.mdx
Source: https://reloop.sh/docs/api/template/post-api-template-v1by-id-duplicate
Markdown: https://reloop.sh/docs/api/template/post-api-template-v1by-id-duplicate.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/template/post-api-template-v1by-id-test.mdx
Source: https://reloop.sh/docs/api/template/post-api-template-v1by-id-test
Markdown: https://reloop.sh/docs/api/template/post-api-template-v1by-id-test.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/template/post-api-template-v1by-id-versions-by-version-id-restore.mdx
Source: https://reloop.sh/docs/api/template/post-api-template-v1by-id-versions-by-version-id-restore
Markdown: https://reloop.sh/docs/api/template/post-api-template-v1by-id-versions-by-version-id-restore.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/template/post-api-template-v1by-id-versions.mdx
Source: https://reloop.sh/docs/api/template/post-api-template-v1by-id-versions
Markdown: https://reloop.sh/docs/api/template/post-api-template-v1by-id-versions.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/template/post-api-template-v1create.mdx
Source: https://reloop.sh/docs/api/template/post-api-template-v1create
Markdown: https://reloop.sh/docs/api/template/post-api-template-v1create.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/template/put-api-template-v1by-id.mdx
Source: https://reloop.sh/docs/api/template/put-api-template-v1by-id
Markdown: https://reloop.sh/docs/api/template/put-api-template-v1by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/get-api-tools-v1deliverability-test-by-token.mdx
Source: https://reloop.sh/docs/api/tools/get-api-tools-v1deliverability-test-by-token
Markdown: https://reloop.sh/docs/api/tools/get-api-tools-v1deliverability-test-by-token.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/get-api-tools-v1email-health-check-batch-by-token.mdx
Source: https://reloop.sh/docs/api/tools/get-api-tools-v1email-health-check-batch-by-token
Markdown: https://reloop.sh/docs/api/tools/get-api-tools-v1email-health-check-batch-by-token.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1auth-checker.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1auth-checker
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1auth-checker.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1bimi-check.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1bimi-check
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1bimi-check.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1blocklist-check.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1blocklist-check
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1blocklist-check.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1deliverability-test-inject.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1deliverability-test-inject
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1deliverability-test-inject.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1deliverability-test.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1deliverability-test
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1deliverability-test.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1dns-lookup.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1dns-lookup
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1dns-lookup.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1domain-age.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1domain-age
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1domain-age.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1email-health-check-batch.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1email-health-check-batch
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1email-health-check-batch.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1email-health-check.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1email-health-check
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1email-health-check.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1lookalike-watch.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1lookalike-watch
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1lookalike-watch.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1spam-check-rewrite.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1spam-check-rewrite
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1spam-check-rewrite.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1spam-check.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1spam-check
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1spam-check.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1spoof-checker.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1spoof-checker
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1spoof-checker.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1temp-email-checker.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1temp-email-checker
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1temp-email-checker.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/tools/post-api-tools-v1who-sends.mdx
Source: https://reloop.sh/docs/api/tools/post-api-tools-v1who-sends
Markdown: https://reloop.sh/docs/api/tools/post-api-tools-v1who-sends.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/upload/delete-api-upload-v1files-by-file-id.mdx
Source: https://reloop.sh/docs/api/upload/delete-api-upload-v1files-by-file-id
Markdown: https://reloop.sh/docs/api/upload/delete-api-upload-v1files-by-file-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/upload/post-api-upload-v1upload.mdx
Source: https://reloop.sh/docs/api/upload/post-api-upload-v1upload
Markdown: https://reloop.sh/docs/api/upload/post-api-upload-v1upload.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/usage-limits.mdx
Source: https://reloop.sh/docs/api/usage-limits
Markdown: https://reloop.sh/docs/api/usage-limits.md
Reloop enforces rate limits on API requests to ensure stability and fair usage across all organizations. Limits are tracked with fixed-window counters backed by Redis.
If a limit is exceeded, the API returns a `429 Too Many Requests` response with a `Retry-After` header indicating how many seconds to wait.
## Baseline Limit
Every API endpoint shares a baseline of **600 requests per 60 seconds per client**. A client is identified by its API key, then its dashboard session, then its IP address for unauthenticated requests. The budget is shared across all services, so calls to `/api/domain`, `/api/contacts` and `/api/logs` draw from the same bucket.
The endpoint-specific limits below apply on top of the baseline, and the stricter one wins. Self-hosted deployments can change the baseline with the `API_RATE_LIMIT_MAX` and `API_RATE_LIMIT_WINDOW_SECONDS` environment variables.
## Management Endpoint Limits
Management endpoints cover everything outside of email sending: domains, contacts, channels, groups, properties, API keys, and templates. Each endpoint category has its own independent limit.
### Domain Service
| Operation | Limit | Window |
|-----------|-------|--------|
| Create Domain | 10 requests | 60s |
| Update Domain | 10 requests | 60s |
| Delete Domain | 10 requests | 60s |
| Verify DNS | 10 requests | 60s |
### Contacts Service
| Operation | Limit | Window |
|-----------|-------|--------|
| List Contacts | 60 requests | 60s |
| Get Contact | 60 requests | 60s |
| Create Contact | 200 requests | 60s |
| Update Contact | 30 requests | 60s |
| Delete Contact | 30 requests | 60s |
### Channels Service
| Operation | Limit | Window |
|-----------|-------|--------|
| List Channels | 60 requests | 60s |
| Get Channel | 60 requests | 60s |
| Create Channel | 30 requests | 60s |
| Update Channel | 30 requests | 60s |
| Delete Channel | 30 requests | 60s |
### Groups Service
| Operation | Limit | Window |
|-----------|-------|--------|
| List Groups | 60 requests | 60s |
| Get Group | 60 requests | 60s |
| Create Group | 30 requests | 60s |
| Update Group | 30 requests | 60s |
| Delete Group | 30 requests | 60s |
| Add Contact to Group | 30 requests | 60s |
| Remove Contact from Group | 30 requests | 60s |
### Contact Properties Service
| Operation | Limit | Window |
|-----------|-------|--------|
| List Properties | 60 requests | 60s |
| Create Property | 30 requests | 60s |
| Update Property | 30 requests | 60s |
| Delete Property | 30 requests | 60s |
### API Keys Service
| Operation | Limit | Window |
|-----------|-------|--------|
| Create API Key | 10 requests | 60s |
| Update API Key | 30 requests | 60s |
| Delete API Key | 20 requests | 60s |
| Rotate API Key | 20 requests | 60s |
| Enable / Disable API Key | 30 requests | 60s |
### Preferences Service
| Operation | Limit | Window |
|-----------|-------|--------|
| Generate Token | 30 requests | 60s |
| Update Preference | 30 requests | 60s |
---
## Email Sending Limits
The email sending endpoint (`/api/mail/v1/send`) uses a **multi-layer** rate limiting strategy. All five layers are checked in parallel. If any single layer is exceeded, the request is rejected. The organization daily layer applies only to plans with a daily sending limit (Free); Pay as you go, Pro and higher plans have no daily cap.
| Layer | Limit | Window | What It Protects |
|-------|-------|--------|-----------------|
| IP | 20 requests | 60s | Prevents brute-force from a single source. |
| User | 50 requests | 60s | Stops abuse from a single user within an organization. |
| Organization | 100 requests | 60s | Prevents runaway integrations per tenant. |
| Organization Daily | 5,000 requests | 24h | Daily cap per organization on the Free plan. Not applied to Pay as you go, Pro and higher. |
| Global | 500 requests | 60s | Protects infrastructure from DDoS. |
When a sending request is rate limited, the error response tells you which layer was exceeded:
```json
{
"message": "Too Many Requests",
"why": "Rate limit exceeded on the organization layer. You have sent too many requests in the current time window.",
"fix": "Please wait 42 seconds before retrying, or contact support to increase your limits."
}
```
---
## Rate Limit Headers
Every API response, including errors such as `401` and `404`, includes rate limit headers. When an endpoint has its own stricter limit, the headers describe that limit.
| Header | Description | Example |
|--------|-------------|---------|
| `ratelimit-limit` | Maximum requests allowed in the current window. | `600` |
| `ratelimit-remaining` | Requests remaining in the current window. | `587` |
| `ratelimit-reset` | Seconds until the window resets (a countdown, not a timestamp). | `42` |
| `retry-after` | Seconds to wait before retrying (only on `429`). | `42` |
`X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` carry the same values for older clients. Email sending responses also include `x-reloop-daily-quota` and `x-reloop-monthly-quota` with your used send counts.
---
## Best Practices
- **Monitor headers proactively.** Check `ratelimit-remaining` before it reaches zero to avoid disruptions.
- **Implement exponential backoff.** When you receive a `429`, wait for the duration specified in `Retry-After`, then retry with increasing delays if the limit is still exceeded.
- **Batch where possible.** Use list endpoints with pagination instead of fetching resources one at a time.
- **Separate read and write traffic.** Read operations (list, get) generally have higher limits than write operations (create, update, delete).
---
# api/versioning.mdx
Source: https://reloop.sh/docs/api/versioning
Markdown: https://reloop.sh/docs/api/versioning.md
The Reloop API is versioned in the URL. The current version is `v1`, as in `https://reloop.sh/api/mail/v1/send`. Within a version, Reloop does not make breaking changes. When an endpoint must change in a breaking way, the new behavior ships as a new endpoint or version, and the old one is deprecated with at least **180 days** of notice before it is removed.
## Non-breaking changes
These can ship at any time without a new version. Build your client to tolerate them:
- New endpoints.
- New optional request fields, query parameters and headers.
- New fields in responses, and new values in fields documented as open-ended.
- New error `code` values. Handle an unknown `code` by its HTTP status. See [Errors](/docs/api/errors).
- New webhook event types.
- Changes to human-readable text such as `message`, `why` and `fix`.
- Changes to rate limits. See [Usage Limits](/docs/api/usage-limits).
## Breaking changes
These only happen in a new version, after the deprecation process below:
- Removing or renaming an endpoint, request field or response field.
- Making an optional request field required.
- Changing a field's type or format.
- Changing authentication.
- Changing the meaning of an existing status code or error `code`.
Fixes for security issues are the one exception. When a vulnerability requires it, Reloop may change behavior with shorter notice and will explain why in the [changelog](https://reloop.sh/changelog).
## Deprecation process
1. **Announce.** The deprecation is published in the [changelog](https://reloop.sh/changelog) ([RSS](https://reloop.sh/changelog/feed.xml)) with the replacement and the removal date.
2. **Signal.** From that day, every response from the deprecated endpoint carries headers you can monitor:
```http
Deprecation: @1793491200
Sunset: Sat, 01 May 2027 00:00:00 GMT
Link: ; rel="deprecation", ; rel="successor-version"
```
| Header | Meaning |
|--------|---------|
| `Deprecation` | When the endpoint was deprecated, as `@` plus Unix seconds ([RFC 9745](https://www.rfc-editor.org/rfc/rfc9745)). |
| `Sunset` | The earliest date the endpoint may be removed, as an HTTP date ([RFC 8594](https://www.rfc-editor.org/rfc/rfc8594)). |
| `Link` | This policy (`rel="deprecation"`) and the replacement endpoint, if there is one (`rel="successor-version"`). |
The [OpenAPI document](https://reloop.sh/openapi.json) marks the operation `deprecated: true`, with the removal date in `x-sunset` and the replacement in `x-successor`.
3. **Remind.** Organizations still calling the endpoint are contacted before the sunset date.
4. **Remove.** On or after the `Sunset` date, the endpoint may be removed and return `404` (`code: not_found`).
`Sunset` is always at least 180 days after `Deprecation`.
## Staying ahead of changes
- Log a warning whenever a response includes a `Deprecation` header.
- Subscribe to the [changelog feed](https://reloop.sh/changelog/feed.xml).
- Ignore unknown response fields instead of failing on them.
- Branch on error `code`, not on `message` text.
## Self-hosted deployments
Self-hosted Reloop follows the same API versions. Deprecated endpoints are removed in a release after their `Sunset` date, and the release notes list them.
---
# api/webhook/delete-api-webhook-v1by-id.mdx
Source: https://reloop.sh/docs/api/webhook/delete-api-webhook-v1by-id
Markdown: https://reloop.sh/docs/api/webhook/delete-api-webhook-v1by-id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/webhook/get-api-webhook-v1.mdx
Source: https://reloop.sh/docs/api/webhook/get-api-webhook-v1
Markdown: https://reloop.sh/docs/api/webhook/get-api-webhook-v1.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/webhook/get-api-webhook-v1by-webhook_id-deliveries.mdx
Source: https://reloop.sh/docs/api/webhook/get-api-webhook-v1by-webhook_id-deliveries
Markdown: https://reloop.sh/docs/api/webhook/get-api-webhook-v1by-webhook_id-deliveries.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/webhook/get-api-webhook-v1by-webhook_id.mdx
Source: https://reloop.sh/docs/api/webhook/get-api-webhook-v1by-webhook_id
Markdown: https://reloop.sh/docs/api/webhook/get-api-webhook-v1by-webhook_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/webhook/patch-api-webhook-v1by-webhook_id.mdx
Source: https://reloop.sh/docs/api/webhook/patch-api-webhook-v1by-webhook_id
Markdown: https://reloop.sh/docs/api/webhook/patch-api-webhook-v1by-webhook_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/webhook/post-api-webhook-deliveries-by-delivery_id-retry.mdx
Source: https://reloop.sh/docs/api/webhook/post-api-webhook-deliveries-by-delivery_id-retry
Markdown: https://reloop.sh/docs/api/webhook/post-api-webhook-deliveries-by-delivery_id-retry.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/webhook/post-api-webhook-v1.mdx
Source: https://reloop.sh/docs/api/webhook/post-api-webhook-v1
Markdown: https://reloop.sh/docs/api/webhook/post-api-webhook-v1.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/webhook/post-api-webhook-v1trigger.mdx
Source: https://reloop.sh/docs/api/webhook/post-api-webhook-v1trigger
Markdown: https://reloop.sh/docs/api/webhook/post-api-webhook-v1trigger.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/automations/delete-api-workflow-v1automations-by-automation_id.mdx
Source: https://reloop.sh/docs/api/workflow/automations/delete-api-workflow-v1automations-by-automation_id
Markdown: https://reloop.sh/docs/api/workflow/automations/delete-api-workflow-v1automations-by-automation_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/automations/get-api-workflow-v1automations-by-automation_id-enrollments-by-enrollment_id.mdx
Source: https://reloop.sh/docs/api/workflow/automations/get-api-workflow-v1automations-by-automation_id-enrollments-by-enrollment_id
Markdown: https://reloop.sh/docs/api/workflow/automations/get-api-workflow-v1automations-by-automation_id-enrollments-by-enrollment_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/automations/get-api-workflow-v1automations-by-automation_id-enrollments.mdx
Source: https://reloop.sh/docs/api/workflow/automations/get-api-workflow-v1automations-by-automation_id-enrollments
Markdown: https://reloop.sh/docs/api/workflow/automations/get-api-workflow-v1automations-by-automation_id-enrollments.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/automations/get-api-workflow-v1automations-by-automation_id.mdx
Source: https://reloop.sh/docs/api/workflow/automations/get-api-workflow-v1automations-by-automation_id
Markdown: https://reloop.sh/docs/api/workflow/automations/get-api-workflow-v1automations-by-automation_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/automations/get-api-workflow-v1automations.mdx
Source: https://reloop.sh/docs/api/workflow/automations/get-api-workflow-v1automations
Markdown: https://reloop.sh/docs/api/workflow/automations/get-api-workflow-v1automations.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/automations/patch-api-workflow-v1automations-by-automation_id.mdx
Source: https://reloop.sh/docs/api/workflow/automations/patch-api-workflow-v1automations-by-automation_id
Markdown: https://reloop.sh/docs/api/workflow/automations/patch-api-workflow-v1automations-by-automation_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/automations/post-api-workflow-v1automations-by-automation_id-activate.mdx
Source: https://reloop.sh/docs/api/workflow/automations/post-api-workflow-v1automations-by-automation_id-activate
Markdown: https://reloop.sh/docs/api/workflow/automations/post-api-workflow-v1automations-by-automation_id-activate.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/automations/post-api-workflow-v1automations-by-automation_id-enroll.mdx
Source: https://reloop.sh/docs/api/workflow/automations/post-api-workflow-v1automations-by-automation_id-enroll
Markdown: https://reloop.sh/docs/api/workflow/automations/post-api-workflow-v1automations-by-automation_id-enroll.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/automations/post-api-workflow-v1automations-by-automation_id-pause.mdx
Source: https://reloop.sh/docs/api/workflow/automations/post-api-workflow-v1automations-by-automation_id-pause
Markdown: https://reloop.sh/docs/api/workflow/automations/post-api-workflow-v1automations-by-automation_id-pause.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/automations/post-api-workflow-v1automations.mdx
Source: https://reloop.sh/docs/api/workflow/automations/post-api-workflow-v1automations
Markdown: https://reloop.sh/docs/api/workflow/automations/post-api-workflow-v1automations.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/events/delete-api-workflow-v1events-by-event_id.mdx
Source: https://reloop.sh/docs/api/workflow/events/delete-api-workflow-v1events-by-event_id
Markdown: https://reloop.sh/docs/api/workflow/events/delete-api-workflow-v1events-by-event_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/events/get-api-workflow-v1events-by-event_id.mdx
Source: https://reloop.sh/docs/api/workflow/events/get-api-workflow-v1events-by-event_id
Markdown: https://reloop.sh/docs/api/workflow/events/get-api-workflow-v1events-by-event_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/events/get-api-workflow-v1events.mdx
Source: https://reloop.sh/docs/api/workflow/events/get-api-workflow-v1events
Markdown: https://reloop.sh/docs/api/workflow/events/get-api-workflow-v1events.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/events/patch-api-workflow-v1events-by-event_id.mdx
Source: https://reloop.sh/docs/api/workflow/events/patch-api-workflow-v1events-by-event_id
Markdown: https://reloop.sh/docs/api/workflow/events/patch-api-workflow-v1events-by-event_id.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/events/post-api-workflow-v1events-track.mdx
Source: https://reloop.sh/docs/api/workflow/events/post-api-workflow-v1events-track
Markdown: https://reloop.sh/docs/api/workflow/events/post-api-workflow-v1events-track.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# api/workflow/events/post-api-workflow-v1events.mdx
Source: https://reloop.sh/docs/api/workflow/events/post-api-workflow-v1events
Markdown: https://reloop.sh/docs/api/workflow/events/post-api-workflow-v1events.md
{/* This file was generated by the OpenAPI doc generator. Do not edit this file directly. Run `bun run generate:api-docs` to regenerate. */}
---
# webhooks/contacts/created.mdx
Source: https://reloop.sh/docs/webhooks/contacts/created
Markdown: https://reloop.sh/docs/webhooks/contacts/created.md
The `contact.create` event is triggered when a new contact is added (via API or auto-capture from sends).
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`contact.create`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Contact identity and status fields (`id`, `email`, `first_name`, `last_name`, `status`).
## Handling this event
When your endpoint receives a `contact.create` event, you might want to:
- Sync the contact with your primary database.
- Trigger a welcome email sequence.
- Tag the contact in your CRM.
```json
{
"id": "whev_01h…",
"type": "contact.create",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"id": "con_123456789",
"email": "contact@example.com",
"first_name": "Ada",
"last_name": "Lovelace",
"status": "subscribed"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/contacts/deleted.mdx
Source: https://reloop.sh/docs/webhooks/contacts/deleted
Markdown: https://reloop.sh/docs/webhooks/contacts/deleted.md
The `contact.delete` event is triggered when a contact is permanently removed.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`contact.delete`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
The deleted contact identity and last known fields.
## Handling this event
When your endpoint receives a `contact.delete` event, you might want to:
- Purge the contact from your local cache or database.
- Stop any active communication threads.
- Update your audience metrics.
```json
{
"id": "whev_01h…",
"type": "contact.delete",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"id": "con_123456789",
"email": "contact@example.com",
"first_name": null,
"last_name": null,
"status": "subscribed"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/contacts/updated.mdx
Source: https://reloop.sh/docs/webhooks/contacts/updated
Markdown: https://reloop.sh/docs/webhooks/contacts/updated.md
The `contact.update` event is triggered when a contact's details change. Status-only transitions also emit dedicated events (`contact.subscribed`, `contact.unsubscribed`, `contact.blocked`).
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`contact.update`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Updated contact fields (`id`, `email`, `first_name`, `last_name`, `status`).
## Handling this event
When your endpoint receives a `contact.update` event, you might want to:
- Update the contact's record in your system.
- Synchronize subscription preferences.
- Log the change for audit purposes.
```json
{
"id": "whev_01h…",
"type": "contact.update",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"id": "con_123456789",
"email": "contact@example.com",
"first_name": "Ada",
"last_name": "Lovelace",
"status": "unsubscribed"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/domains/created.mdx
Source: https://reloop.sh/docs/webhooks/domains/created
Markdown: https://reloop.sh/docs/webhooks/domains/created.md
The `domain.create` event is triggered when a new domain is successfully added to your Reloop account.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`domain.create`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Contains the details of the newly created domain (`id`, `name`, `status`).
## Handling this event
When your endpoint receives a `domain.create` event, you might want to:
- Sync the domain status with your internal system.
- Trigger DNS verification workflows.
- Log the activity for auditing.
```json
{
"id": "whev_01h…",
"type": "domain.create",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"id": "dom_123456789",
"name": "example.com",
"status": "pending"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/domains/deleted.mdx
Source: https://reloop.sh/docs/webhooks/domains/deleted
Markdown: https://reloop.sh/docs/webhooks/domains/deleted.md
The `domain.delete` event is triggered when a domain is removed from your organization.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`domain.delete`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Contains the deleted domain identity (`id`, `name`, `status`).
## Handling this event
When your endpoint receives a `domain.delete` event, you might want to:
- Stop sending from that domain in your application.
- Remove DNS setup reminders.
- Clean up local domain caches.
```json
{
"id": "whev_01h…",
"type": "domain.delete",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"id": "dom_123456789",
"name": "example.com",
"status": "pending"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/domains/updated.mdx
Source: https://reloop.sh/docs/webhooks/domains/updated
Markdown: https://reloop.sh/docs/webhooks/domains/updated.md
The `domain.update` event is triggered when a domain's settings change (tracking, TLS, send/receive flags, etc.).
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`domain.update`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Contains the updated domain identity and status (`id`, `name`, `status`).
## Handling this event
When your endpoint receives a `domain.update` event, you might want to:
- Refresh cached domain configuration.
- Re-run internal validation against the new settings.
```json
{
"id": "whev_01h…",
"type": "domain.update",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"id": "dom_123456789",
"name": "example.com",
"status": "active"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/bounced.mdx
Source: https://reloop.sh/docs/webhooks/emails/bounced
Markdown: https://reloop.sh/docs/webhooks/emails/bounced.md
The `email.bounced` event is triggered when delivery permanently fails (hard bounce, admin bounce, or OOB).
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`email.bounced`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Includes standard email fields plus `error` with SMTP details when available.
## Handling this event
When your endpoint receives an `email.bounced` event, you might want to:
- Suppress the recipient from future sends.
- Notify the sender or update CRM status.
```json
{
"id": "whev_01h…",
"type": "email.bounced",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_123456789",
"from": "sender@example.com",
"to": ["bad@example.com"],
"subject": "Hello World",
"status": "bounced",
"error": {
"code": 550,
"message": "User unknown"
}
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/clicked.mdx
Source: https://reloop.sh/docs/webhooks/emails/clicked
Markdown: https://reloop.sh/docs/webhooks/emails/clicked.md
The `email.clicked` event is triggered when click tracking records a link click.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`email.clicked`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Standard outbound email fields plus `url` for the clicked destination.
## Handling this event
When your endpoint receives an `email.clicked` event, you might want to:
- Track link engagement.
- Personalize follow-up based on which URL was clicked.
```json
{
"id": "whev_01h…",
"type": "email.clicked",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_123456789",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello World",
"status": "delivered",
"url": "https://example.com/pricing"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/complained.mdx
Source: https://reloop.sh/docs/webhooks/emails/complained
Markdown: https://reloop.sh/docs/webhooks/emails/complained.md
The `email.complained` event is triggered when a feedback loop reports spam.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`email.complained`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Includes standard email fields; `status` is `spam` and `error` may include FBL details.
## Handling this event
When your endpoint receives an `email.complained` event, you might want to:
- Immediately suppress the recipient.
- Review content or list hygiene.
```json
{
"id": "whev_01h…",
"type": "email.complained",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_123456789",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello World",
"status": "spam",
"error": {
"message": "Feedback loop: spam complaint"
}
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/delivered.mdx
Source: https://reloop.sh/docs/webhooks/emails/delivered
Markdown: https://reloop.sh/docs/webhooks/emails/delivered.md
The `email.delivered` event is triggered when an email is delivered to the recipient MTA.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`email.delivered`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Outbound email fields: `email_id`, `from`, `to`, `subject`, `status`.
## Handling this event
When your endpoint receives an `email.delivered` event, you might want to:
- Update the status in your internal database.
- Trigger an automated confirmation email.
- Log the event for analytics purposes.
```json
{
"id": "whev_01h…",
"type": "email.delivered",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_123456789",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello World",
"status": "delivered"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/delivery-delayed.mdx
Source: https://reloop.sh/docs/webhooks/emails/delivery-delayed
Markdown: https://reloop.sh/docs/webhooks/emails/delivery-delayed.md
The `email.delivery_delayed` event is triggered on a transient delivery failure (soft bounce / deferral).
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`email.delivery_delayed`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Includes standard email fields plus `error` describing the temporary failure.
## Handling this event
When your endpoint receives an `email.delivery_delayed` event, you might want to:
- Track deliverability health without treating it as a permanent failure.
- Alert if delays spike for a domain.
```json
{
"id": "whev_01h…",
"type": "email.delivery_delayed",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_123456789",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello World",
"status": "sent",
"error": {
"code": 450,
"message": "Mailbox temporarily unavailable"
}
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/failed.mdx
Source: https://reloop.sh/docs/webhooks/emails/failed
Markdown: https://reloop.sh/docs/webhooks/emails/failed.md
The `email.failed` event is triggered when an email fails permanently before delivery (provider error at send time) or expires in the queue.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`email.failed`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Includes standard email fields plus `error`.
## Handling this event
When your endpoint receives an `email.failed` event, you might want to:
- Notify the user about the failure.
- Check the error details for troubleshooting.
- Update the status in your internal database.
```json
{
"id": "whev_01h…",
"type": "email.failed",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_123456789",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello World",
"status": "failed",
"error": {
"message": "Upstream provider rejected the message"
}
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/opened.mdx
Source: https://reloop.sh/docs/webhooks/emails/opened
Markdown: https://reloop.sh/docs/webhooks/emails/opened.md
The `email.opened` event is triggered when open tracking records a pixel load.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`email.opened`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Standard outbound email fields. `status` reflects the email log status (usually `delivered` or `sent`), not a separate open status.
## Handling this event
When your endpoint receives an `email.opened` event, you might want to:
- Track email open rates.
- Update user engagement scores.
- Trigger follow-up workflows.
```json
{
"id": "whev_01h…",
"type": "email.opened",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_123456789",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello World",
"status": "delivered"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/received.mdx
Source: https://reloop.sh/docs/webhooks/emails/received
Markdown: https://reloop.sh/docs/webhooks/emails/received.md
The `email.received` event is triggered when an inbound email is successfully received by Reloop.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`email.received`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Inbound fields: `email_id`, `mailbox_id`, `from`, `from_name`, `to`, `cc`, `subject`, `thread_id`, `has_attachments`, `is_spam`, `status`, `message_id`.
## Handling this event
When your endpoint receives an `email.received` event, you might want to:
- Process inbound messages for your application.
- Fetch the full body via the inbox API using `email_id`.
- Store attachments in your cloud storage.
```json
{
"id": "whev_01h…",
"type": "email.received",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "in_123456789",
"mailbox_id": "mb_123456789",
"from": "sender@example.com",
"from_name": "Sender",
"to": ["inbound@yourdomain.com"],
"cc": [],
"subject": "Inbound Message",
"thread_id": "thr_123456789",
"has_attachments": false,
"is_spam": false,
"status": "received",
"message_id": ""
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/scheduled.mdx
Source: https://reloop.sh/docs/webhooks/emails/scheduled
Markdown: https://reloop.sh/docs/webhooks/emails/scheduled.md
The `email.scheduled` event is triggered when an email is accepted with a future `scheduled_at` time. It replaces `email.sent` for that message; later lifecycle events (`email.delivered`, `email.bounced`, …) still fire when the message is actually processed.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`email.scheduled`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Standard outbound email fields with `status: "scheduled"` and `scheduled_at` (ISO 8601).
## Handling this event
When your endpoint receives an `email.scheduled` event, you might want to:
- Show the message as queued for a future send in your UI.
- Cancel or reschedule downstream automations until delivery.
```json
{
"id": "whev_01h…",
"type": "email.scheduled",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_123456789",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello World",
"status": "scheduled",
"scheduled_at": "2026-07-23T09:00:00.000Z"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/sent.mdx
Source: https://reloop.sh/docs/webhooks/emails/sent
Markdown: https://reloop.sh/docs/webhooks/emails/sent.md
The `email.sent` event is triggered when an email is accepted for delivery by Reloop.
## Event Details
All webhook payloads follow a consistent top-level structure with event-specific data nested within the `data` object.
### type `string`
The event type that triggered the webhook (`email.sent`).
### created_at `string`
ISO 8601 timestamp when the webhook event was created.
### data `object`
Outbound email fields: `email_id`, `from`, `to`, `subject`, `status`.
## Handling this event
When your endpoint receives an `email.sent` event, you might want to:
- Update the status in your internal database.
- Trigger automated workflows or notifications.
- Log the event for analytics purposes.
```json
{
"id": "whev_01h…",
"type": "email.sent",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_123456789",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello World",
"status": "sent"
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/emails/suppressed.mdx
Source: https://reloop.sh/docs/webhooks/emails/suppressed
Markdown: https://reloop.sh/docs/webhooks/emails/suppressed.md
The `email.suppressed` event ID is reserved for when Reloop rejects a send because the recipient is on a suppression list.
**Status:** inactive. Not available for new webhook subscriptions until pre-send suppression publishes this event. Contact suppressions currently surface as `contact.blocked` when auto-capture blocks a recipient.
## Planned `data` shape
```json
{
"id": "whev_01h…",
"type": "email.suppressed",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_123456789",
"from": "sender@example.com",
"to": ["blocked@example.com"],
"subject": "Hello World",
"status": "failed",
"error": {
"message": "Recipient is on the suppression list"
}
}
}
```
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [All Event Types](/docs/webhooks/event-types)
---
# webhooks/event-types.mdx
Source: https://reloop.sh/docs/webhooks/event-types
Markdown: https://reloop.sh/docs/webhooks/event-types.md
Reloop supports the following **active** event types. Each delivery uses a consistent top-level envelope; event-specific fields live under `data` and use **snake_case**.
## Email lifecycle
| Event | Description |
| --- | --- |
| `email.sent` | Email accepted for delivery (immediate send) |
| `email.scheduled` | Email accepted with a future `scheduled_at` (replaces `email.sent` for that message) |
| `email.delivered` | Delivered to the recipient MTA |
| `email.bounced` | Permanent delivery failure |
| `email.delivery_delayed` | Transient failure / delay |
| `email.complained` | Recipient marked as spam (feedback loop) |
| `email.failed` | Permanent send failure before delivery (or message expired) |
| `email.opened` | Recipient opened the email |
| `email.clicked` | Recipient clicked a link in the email |
| `email.received` | Inbound email successfully received |
### Email `data` shape (outbound)
```json
{
"email_id": "em_…",
"from": "Acme ",
"to": ["user@example.com"],
"subject": "Hello",
"status": "delivered",
"error": {
"code": 550,
"message": "User unknown"
},
"url": "https://example.com/path"
}
```
- `error` is present on bounce, delay, complaint, and failure events.
- `url` is present on `email.clicked`.
- `scheduled_at` is present on `email.scheduled`.
### Inbound `email.received` `data` shape
```json
{
"email_id": "in_…",
"mailbox_id": "mb_…",
"from": "sender@example.com",
"from_name": "Sender",
"to": ["inbox@yourdomain.com"],
"cc": [],
"subject": "Inbound Message",
"thread_id": "thr_…",
"has_attachments": false,
"is_spam": false,
"status": "received",
"message_id": ""
}
```
## Domain lifecycle
| Event | Description |
| --- | --- |
| `domain.create` | Domain created |
| `domain.update` | Domain updated |
| `domain.delete` | Domain deleted |
| `domain.undelete` | Domain restored |
| `domain.verify` | Domain DNS verified |
### Domain `data` shape
```json
{
"id": "dom_…",
"name": "example.com",
"status": "active"
}
```
## API key lifecycle
| Event | Description |
| --- | --- |
| `api-key.create` | API key created |
| `api-key.update` | API key updated / enabled / rotated |
| `api-key.delete` | API key deleted |
| `api-key.revoke` | API key revoked (disabled) |
### API key `data` shape
```json
{
"api_key_id": "key_…",
"status": "disabled",
"action": "revoked"
}
```
`status` and `action` are optional and only set for some transitions.
## Contact lifecycle
| Event | Description |
| --- | --- |
| `contact.create` | Contact created |
| `contact.update` | Contact updated |
| `contact.delete` | Contact deleted |
| `contact.subscribed` | Contact status set to subscribed |
| `contact.unsubscribed` | Contact status set to unsubscribed |
| `contact.blocked` | Contact status set to blocked |
| `contact.group.create` | Contact group created |
| `contact.group.update` | Contact group updated |
| `contact.group.delete` | Contact group deleted |
### Contact `data` shape
```json
{
"id": "con_…",
"email": "contact@example.com",
"first_name": "Ada",
"last_name": "Lovelace",
"status": "subscribed"
}
```
### Contact group `data` shape
```json
{
"id": "grp_…",
"name": "VIP"
}
```
## Envelope
```json
{
"id": "whev_…",
"type": "email.delivered",
"created_at": "2026-07-22T12:00:00.000Z",
"data": { }
}
```
## Coming soon (not subscribable yet)
These event IDs exist in the catalog but are **inactive** until the pipeline emits them:
| Event | Notes |
| --- | --- |
| `email.suppressed` | Pre-send suppression event not wired yet |
| `api-key.rate_limited` | Rate-limit publisher not wired yet |
---
## Learn More
- [Introduction](/docs/webhooks)
- [Verify Webhooks Requests](/docs/webhooks/verify-webhooks-requests)
- [Retries and Replays](/docs/webhooks/retries-and-replays)
---
# webhooks/index.mdx
Source: https://reloop.sh/docs/webhooks
Markdown: https://reloop.sh/docs/webhooks.md
Webhooks allow you to build or set up integrations which subscribe to certain events on Reloop. When one of those events is triggered, we'll send a HTTP POST payload to the webhook's configured URL.
## What is a webhook?
Webhooks are "user-defined HTTP callbacks". They are usually triggered by some event, such as an email being delivered or a domain being verified. When that event occurs, the source site makes an HTTP request to the URL configured for the webhook. Users can configure them to cause events on one site to invoke behavior on another.
## Why use webhooks?
- Automatically remove bounced email addresses from mailing lists
- Create alerts in your messaging or incident tools based on event types
- Store all send events in your own database for custom reporting/retention
- Receive emails using [Inbound](/docs/webhooks/ingester)
## How to receive webhooks
### 1. Create a dev endpoint to receive requests.
Set up a URL on your server that can receive HTTP POST requests.
```javascript
if (req.method === 'POST') {
const event = req.body;
console.log(event);
res.status(200).send('OK');
}
};
```
To test it while it runs on localhost, use [`reloop webhooks listen`](/docs/webhooks/testing-webhooks#test-on-localhost-with-the-cli). It forwards test events to your machine without exposing it to the internet.
### 2. Add a webhook in Reloop.
Go to the [Webhooks page](https://reloop.sh/dashboard/webhooks) in your dashboard.
1. Add your publicly accessible HTTPS URL.
2. Select all events you want to observe.
### 3. Test your local endpoint.
Open the webhook's **Test** page and send a test event to your URL or to a local listener. See [Test Webhooks](/docs/webhooks/testing-webhooks).
When you send an email or trigger an event, Reloop will send a payload like this to your endpoint:
```json
{
"id": "whev_01h…",
"type": "email.bounced",
"created_at": "2026-07-22T23:41:12.126Z",
"data": {
"email_id": "em_123456789",
"from": "Acme ",
"to": ["bounced@reloop.sh"],
"subject": "Sending this example",
"status": "bounced",
"error": {
"code": 550,
"message": "User unknown"
}
}
}
```
All `data` fields use **snake_case**. See [Event Types](/docs/webhooks/event-types) for the full catalog and per-category shapes.
Headers include `Reloop-Id`, `Reloop-Timestamp`, and `Reloop-Signature` for verification.
### 4. Update and deploy your production endpoint.
Once you've tested your logic, update your endpoint to handle specific event types and deploy it to your production server.
```javascript
if (req.method === 'POST') {
const event = req.body;
if (event.type === "email.bounced") {
// Handle bounce logic
}
res.status(200).send('OK');
}
};
```
### 5. Register your production webhook endpoint
Update your webhook URL in the [Reloop Dashboard](https://reloop.sh/dashboard/webhooks) with your production URL.
## FAQ
If your server returns a non-2xx response, we retry up to 7 attempts:
- Immediate
- 5 seconds
- 5 minutes
- 30 minutes
- 2 hours
- 5 hours
- 10 hours
Use your endpoint secret (`whsec_…`) to verify `Reloop-Signature`. See [Verify Webhooks Requests](/docs/webhooks/verify-webhooks-requests).
- `54.148.139.208`
- `2600:1f24:64:8000::/52`
Reloop guarantees "at-least-once" delivery of webhooks. Each webhook has a unique `reloop-id` (or Svix ID) header that you can use for idempotency to ensure you don't process the same event twice.
We do not guarantee that events will arrive in the order they occurred. For example, an `email.opened` event might arrive before an `email.delivered` event due to network latency or retries. You should use the `created_at` timestamp in the payload to order them.
Yes, you can manually replay any failed or successful webhook event from the [Reloop Dashboard](https://reloop.sh/dashboard/webhooks).
---
## Learn More
- [Event Types](/docs/webhooks/event-types)
- [Webhook Ingester](/docs/webhooks/ingester)
- [Verifying Requests](/docs/webhooks/verify-webhooks-requests)
---
# webhooks/ingester.mdx
Source: https://reloop.sh/docs/webhooks/ingester
Markdown: https://reloop.sh/docs/webhooks/ingester.md
The Webhook Ingester is a self-hosted tool that allows you to store all your Reloop webhook events directly in your own database. This is ideal for long-term data retention, custom analytics, and building robust event-driven workflows without worrying about webhook delivery order or duplicate handling.
## Why use the Webhook Ingester?
- **Signature Verification**: Built-in verification to ensure webhook authenticity.
- **Idempotency**: Safely handles duplicate webhook deliveries using unique event IDs.
- **Multiple Databases**: Support for PostgreSQL, MySQL, MongoDB, and data warehouses.
- **Easy Deployment**: One-click deployment to Vercel, Railway, or Render.
## Deploy
You can run the ingester using our official Docker image:
```bash
docker pull ghcr.io/reloop-labs/reloop-webhooks-ingester
```
## Supported Databases
We support a wide range of databases to store your webhook events:
- **PostgreSQL** (including Supabase and Neon)
- **MySQL** (including PlanetScale)
- **MongoDB**
- **Data Warehouses** (Snowflake, BigQuery, ClickHouse)
## Quick Start
Clone the repository and install dependencies:
```bash
git clone https://github.com/reloop-labs/reloop-webhooks-ingester.git
cd reloop-webhooks-ingester
pnpm install
```
Copy the example environment file and add your credentials:
```bash
cp .env.example .env.local
```
Required variables:
```bash
# Your Reloop webhook signing secret from the dashboard
RELOOP_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxx
# Database connection string
POSTGRESQL_URL=postgresql://user:password@host:5432/database
```
Run the setup command to create the necessary tables/collections:
```bash
pnpm db:setup --postgresql # or --mysql, --mongodb
```
Deploy your instance and add the endpoint in the [Reloop Dashboard](https://reloop.sh/dashboard/webhooks).
Your endpoint URL will be: `https://your-app.com/postgresql` (or your chosen connector).
## Database Schemas
The ingester creates tables with a standardized schema for different event categories:
- `reloop_wh_emails`: Stores all email-related events.
- `reloop_wh_contacts`: Stores audience and contact events.
- `reloop_wh_domains`: Stores domain verification events.
### Core Fields
Every event includes these essential fields:
- `event_id`: Unique webhook event ID for idempotency.
- `event_type`: The type of event (e.g., `email.delivered`).
- `event_created_at`: When the event occurred in Reloop.
- `webhook_received_at`: When the webhook was stored in your database.
- `data`: JSON object containing the full event payload.
## Idempotency
The ingester uses the unique event ID provided by Reloop to ensure that even if a webhook is delivered multiple times, it is only stored once in your database.
## Configuration Reference
### Required Environment Variables
| Variable | Description |
|----------|-------------|
| `RELOOP_WEBHOOK_SECRET` | The signing secret used to verify webhook authenticity. |
### Database-Specific Variables
#### Supabase / PostgreSQL
```bash
POSTGRESQL_URL=postgresql://user:password@host:5432/database
```
#### MongoDB
```bash
MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/
MONGODB_DATABASE=reloop_webhooks
```
## Example Queries
Once your data is in your database, you can run powerful queries:
**Count daily events by type:**
```sql
SELECT
DATE(event_created_at) AS day,
event_type,
COUNT(*) AS count
FROM reloop_wh_emails
GROUP BY DATE(event_created_at), event_type
ORDER BY day DESC;
```
## Data Retention
Since the data is in your own database, you have full control over retention. You can set up TTL indexes in MongoDB or use a cron job to purge old data:
```sql
DELETE FROM reloop_wh_emails
WHERE event_created_at < NOW() - INTERVAL '90 days';
```
## Troubleshooting
- **Signature failing**: Ensure `RELOOP_WEBHOOK_SECRET` matches your dashboard secret.
- **Connection errors**: Check your database firewall rules and connection string.
- **Webhooks not received**: Verify your endpoint is publicly accessible and returning a 200 OK.
---
## Learn More
- [Verifying Webhooks](/docs/webhooks/verify-webhooks-requests)
- [Event Types Reference](/docs/webhooks/event-types)
---
# webhooks/retries-and-replays.mdx
Source: https://reloop.sh/docs/webhooks/retries-and-replays
Markdown: https://reloop.sh/docs/webhooks/retries-and-replays.md
Reloop delivers webhooks with at-least-once semantics. If your server is down or returns a non-2xx status, we automatically retry the delivery.
## Automatic Retries
Each delivery is attempted up to **`maxRetries` times** (default **7**, maximum **7**) with a fixed schedule. `maxRetries` is total attempts including the first try. Delays are measured from the previous failure:
| Attempt | Delay after previous failure |
| --- | --- |
| 1 | Immediate |
| 2 | 5 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 5 hours |
| 7 | 10 hours |
If you set a lower `maxRetries` on the endpoint (for example `3`), only the first rows of the schedule apply.
SSRF-blocked or non-HTTPS endpoints are **not** retried (terminal failure).
After many consecutive **terminal** failures on an endpoint, Reloop may disable the webhook (`status: failed`). Re-enable it from the dashboard after fixing your endpoint.
## Outbound rate limiting
When `rateLimitEnabled` is true (default), Reloop caps outbound POSTs per endpoint at `maxRequestsPerMinute` (default **60**). Extra deliveries are delayed into the next minute window rather than dropped.
## Filtering
Optional `filteringOptions` on an endpoint:
- **`matchConditions`**: only deliver when every listed top-level `data` field equals the expected value (shallow equality).
- **`excludeFields`**: strip listed top-level keys from `data` before POST (and in the stored delivery payload).
Example:
```json
{
"filteringOptions": {
"matchConditions": { "status": "bounced" },
"excludeFields": ["subject"]
}
}
```
## Manual Replays
You can manually replay any delivery from the dashboard or API. Replay creates a **new** delivery row (linked to the original) so history stays intact, then enqueues it on the delivery queue using the endpoint's current `maxRetries`.
Navigate to the Webhooks page in your dashboard.
Open the webhook endpoint you are using.
Locate the delivery in the delivery logs.
Trigger a manual redelivery.
---
## Learn More
- [Webhooks Introduction](/docs/webhooks)
- [Event Types](/docs/webhooks/event-types)
- [Verify Webhooks Requests](/docs/webhooks/verify-webhooks-requests)
---
# webhooks/testing-webhooks.mdx
Source: https://reloop.sh/docs/webhooks/testing-webhooks
Markdown: https://reloop.sh/docs/webhooks/testing-webhooks.md
You can test a webhook before any real email goes out. There are two ways to do it:
- **Send a test event to your webhook URL** from the dashboard. Use this when your endpoint is already deployed.
- **Forward test events to localhost** with `reloop webhooks listen`. Use this while you build your handler on your own machine. You don't need ngrok or a public URL.
Either way, Reloop signs the request and sends it **from its own servers**, exactly like a real delivery. Your endpoint doesn't need CORS headers, and your [signature check](/docs/webhooks/verify-webhooks-requests) runs unchanged.
Test events are not stored. They don't show up in delivery logs, and they don't count toward retries or auto-disable.
## Send a test event to your webhook URL
Go to [Webhooks](https://reloop.sh/dashboard/webhooks), open your webhook, and choose **Test**.
Under **Send to**, keep **Webhook URL** selected. Pick one of the webhook's subscribed events. The right-hand panel shows the exact envelope that will be sent.
Click **Send test event** (or press ⌘/Ctrl + Enter). The result shows the HTTP status, how long the request took, your endpoint's response body, and the signed headers that were sent.
Paused or disabled webhooks can't receive tests at their URL. Resume the webhook first, or send to a local listener instead.
## Test on localhost with the CLI
`reloop webhooks listen` opens a connection from your machine to Reloop. When you send a test event to the listener, Reloop pushes it down that connection, and the CLI POSTs it to your local server. Your server's response then travels back to the dashboard.
```
Dashboard ──► Reloop ══ connection opened by the CLI ══► reloop CLI ──► localhost:3000
▲ │
└────────────── status · response body · timing ◄─────────┘
```
The connection starts on your machine, so this works behind NAT, firewalls, and corporate networks.
```bash
npm install -g reloop-cli
reloop login
```
In CI or a container, set `RELOOP_API_KEY` instead of running `reloop login`.
Run the app that handles webhooks, for example on `http://localhost:3000/webhooks`.
```bash
reloop webhooks listen --forward-to localhost:3000/webhooks
```
```
Ready. Forwarding webhook events to http://localhost:3000/webhooks
Listener wls_o3g9g7vk7k82ule1s4d4at8x. Pick it as the target on a webhook's Test page. Ctrl-C to stop.
```
On the webhook's **Test** page, a **Local listener** option appears under **Send to** within a few seconds. Select it, pick an event, and click **Send test event**.
The CLI prints one line per event:
```
14:02:11 email.sent → POST http://localhost:3000/webhooks [200] 12ms
```
The dashboard shows the same status and your server's response body.
Press Ctrl + C to stop. The listener disappears from the dashboard straight away.
### Signatures
Requests forwarded to a listener are signed with the secret of **the webhook whose Test page you sent from**. Configure your local server with that webhook's signing secret (`whsec_…`) and verify requests as usual. See [Verify Webhooks Requests](/docs/webhooks/verify-webhooks-requests).
### Options
| Flag | Description |
| --- | --- |
| `--forward-to`, `-f` | Local URL to POST events to. Accepts a full URL, `localhost:3000/webhooks`, or just a port (`3000` means `http://localhost:3000/`). |
| `--json` | Print one JSON object per event, for scripts and agents. |
| `--endpoint` | Use a self-hosted Reloop instead of Reloop Cloud. |
| `--debug` | Log the connection to the relay. |
```bash
# Forward to a port on localhost
reloop webhooks listen -f 8080
# Pipe results into jq
reloop webhooks listen -f localhost:3000/webhooks --json | jq '{type: .eventType, status}'
```
If the connection drops, for example when your laptop sleeps, the CLI reconnects with backoff and prints a warning. Its listener ID changes, so select the new listener in the dashboard.
## Troubleshooting
**The listener doesn't appear in the dashboard.**
Check that the CLI printed `Ready.` and is signed in to the same organization you're viewing (`reloop auth status`). The list refreshes every few seconds.
**`Could not reach http://localhost:3000/webhooks`**
The CLI is connected, but nothing is listening at `--forward-to`. Check that your server is running, and check the port and path.
**`Local endpoint returned HTTP 4xx/5xx`**
Your server received the request and rejected it. The response body is shown in the dashboard and the CLI. A `401` or `400` is usually a signature check that uses the wrong secret, or that re-serializes the JSON before verifying.
**`The listener did not report a result within 15s`**
Your server took longer than 10 seconds to respond. Return `2xx` quickly, and do slow work after responding.
**`Outbound request to private/local IP address … is blocked`**
You sent a test to a webhook URL that points at a private or local address. Reloop's servers can't reach your machine. Use a local listener instead.
---
# webhooks/verify-webhooks-requests.mdx
Source: https://reloop.sh/docs/webhooks/verify-webhooks-requests
Markdown: https://reloop.sh/docs/webhooks/verify-webhooks-requests.md
To ensure that webhook requests are authentic and coming from Reloop, verify the signature included in the request headers. This prevents unauthorized requests from reaching your application logic.
## Headers
Every delivery includes:
| Header | Description |
| --- | --- |
| `Reloop-Id` | Event id (`whev_…`), same as body `id` |
| `Reloop-Timestamp` | Unix timestamp (seconds) used in the signature |
| `Reloop-Signature` | `t=,v1=` |
## Signature algorithm
1. Read the **raw** request body bytes (do not re-serialize JSON).
2. Read `Reloop-Timestamp` (or parse `t=` from `Reloop-Signature`).
3. Compute HMAC-SHA256 over the string `` `${timestamp}.${rawBody}` `` using your endpoint secret (`whsec_…`).
4. Compare the hex digest to the `v1=` value with a constant-time compare.
5. Reject if the timestamp is older than ~5 minutes (replay protection).
```typescript
secret: string;
rawBody: string;
signatureHeader: string;
toleranceSeconds?: number;
}): boolean {
const parts = Object.fromEntries(
input.signatureHeader.split(",").map((p) => {
const [k, ...rest] = p.trim().split("=");
return [k, rest.join("=")];
}),
);
const timestamp = Number(parts.t);
const v1 = parts.v1;
if (!Number.isFinite(timestamp) || !v1) return false;
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > (input.toleranceSeconds ?? 300)) return false;
const expected = createHmac("sha256", input.secret)
.update(`${timestamp}.${input.rawBody}`)
.digest("hex");
try {
const a = Buffer.from(expected, "hex");
const b = Buffer.from(v1, "hex");
return a.length === b.length && timingSafeEqual(a, b);
} catch {
return false;
}
}
```
## Payload shape
```json
{
"id": "whev_01h…",
"type": "email.delivered",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_…",
"from": "hello@example.com",
"to": ["user@example.com"],
"subject": "Welcome",
"status": "delivered"
}
}
```
## Why verify webhooks?
Verification is critical because it allows you to:
- Confirm the request was signed with your endpoint secret
- Reject forged events from third parties
- Detect and drop replayed requests outside the time window
---
# integrations/ai-tools/cli-agents.mdx
Source: https://reloop.sh/docs/integrations/ai-tools/cli-agents
Markdown: https://reloop.sh/docs/integrations/ai-tools/cli-agents.md
The [Reloop CLI](/docs/resources/cli) is purpose-built for AI agent workflows: every command supports machine-readable JSON output, deterministic exit codes, and stdin piping. See the full [CLI reference](/docs/resources/cli) for all available commands.
## Agent Skills
Install the CLI agent skill so your agent knows how to use the Reloop CLI effectively:
```bash
npx skills add reloop/reloop-cli
```
## Non-interactive mode
Every CLI command supports `--json` for machine-readable output:
- **Output**: JSON to stdout, no progress indicators
- **Exit codes**: `0` for success, `1` for errors
- **Errors**: Always include `message` and `code` fields
```json
{
"error": {
"message": "No API key found",
"code": "auth_error"
}
}
```
## Piping from stdin
Use `-` as the value of any file flag to read from stdin:
```bash
echo "Your order has shipped." | reloop emails send \
--from "Acme " \
--to delivered@example.com \
--subject "Order update" \
--text-file -
```
This also works with `--html-file -` and `--file -` for [batch commands](#batch-sending).
## Batch sending
Use `emails batch` to send up to 100 emails in a single request:
```bash
cat emails.json | reloop emails batch --file -
```
## Safe retries
Use `--idempotency-key` to safely retry failed sends without risking duplicates:
```bash
reloop emails send \
--from "Acme " \
--to delivered@example.com \
--subject "Welcome" \
--text "Hello!" \
--idempotency-key "welcome-user-123"
```
Idempotency keys are supported on both `emails send` and `emails batch` commands.
## Scheduling
Use `--scheduled-at` to schedule emails for future delivery:
```bash
reloop emails send \
--from "Acme " \
--to delivered@example.com \
--subject "Your trial ends soon" \
--text "Your free trial expires in 3 days." \
--scheduled-at "tomorrow at 9am ET"
```
You can cancel or reschedule with:
```bash
reloop emails cancel
reloop emails update --scheduled-at
```
## Reading inbound emails
The `emails receiving listen` command starts a long-running process that watches for new inbound emails:
```bash
reloop emails receiving listen --json
```
To fetch a specific received email:
```bash
reloop emails receiving get
```
Make sure you have a [verified domain](/docs/learn/domain) with receiving enabled.
## Closing the loop with webhooks
The `webhooks listen` command subscribes to [webhook events](/docs/webhooks) and streams them to your terminal:
```bash
reloop webhooks listen \
--url https://hostname.tailnet-name.ts.net \
--events email.delivered email.bounced email.received
```
Press `Ctrl+C` to stop listening. The webhook is automatically removed when you disconnect.
To forward webhooks to a local development server, use `--forward-to`:
```bash
reloop webhooks listen \
--url https://hostname.tailnet-name.ts.net \
--forward-to http://localhost:4321/api/webhook
```
The `--url` flag requires a publicly reachable URL. We recommend [Tailscale Funnel](https://tailscale.com/kb/1223/funnel), [ngrok](https://ngrok.com/), or [localtunnel](https://theboringtech.io/) for local development.
If you prefer to set up webhooks manually, use `reloop webhooks create`.
---
# integrations/ai-tools/mcp-prompts.mdx
Source: https://reloop.sh/docs/integrations/ai-tools/mcp-prompts
Markdown: https://reloop.sh/docs/integrations/ai-tools/mcp-prompts.md
Once your client is connected to the [Reloop MCP server](/docs/integrations/ai-tools/mcp-server), you can ask for things in plain language. Here are prompts to start with, grouped by area. Swap in your own addresses, names, and details.
Your assistant shows each tool call before it runs. Read sends and deletes before you approve them: email that's been sent can't be recalled.
## Emails
> "Send a welcome email to user@example.com with a personalized greeting."
Writes a welcome email and sends it from a verified domain in your organization.
> "List all emails I sent today and show their delivery status."
Pulls today's email logs and reports whether each one was delivered, bounced, or is still queued.
> "Send a batch of onboarding emails to these five new users."
Sends one onboarding email to each address you list, in a single batch.
## Contacts
> "Add a new contact with their name and email."
Creates a contact with the name and email you give it. Creating a contact doesn't send anything.
> "Create a contact group for users who signed up this month."
Creates a group and adds the contacts that signed up this month, so you can target them later.
## Campaigns
> "Create a campaign for our product launch announcement."
Drafts a launch campaign with a subject line and content, ready for you to review, schedule, or send.
## Domains
> "Verify my domain and check the DNS configuration status."
Checks your domain's DNS records, runs verification, and tells you which records are missing or wrong.
## Inbound
> "Show me all inbound emails I received this week."
Lists the emails your receiving domains got this week, with senders and subjects.
## Webhooks
> "Set up a webhook to notify me when an email bounces."
Creates a webhook subscribed to bounce events and sends them to the URL you choose.
## Templates
> "Create a password reset email template that matches our brand."
Builds a reusable password reset template with a variable for the reset link.
## Metrics
> "Why did my bounce rate go up yesterday?"
Compares yesterday's bounces with earlier days and points out the domains, recipients, or messages behind the change.
## Automation
> "Build an automation that sends a welcome series when someone signs up."
Sets up a flow that starts when a contact signs up and sends a sequence of welcome emails.
## Tips for better results
- **Name the sender.** Say which address to send from, for example "from hello@example.com". It has to be on a verified domain.
- **Ask before you act.** For bulk changes, ask for a preview first: "Show me who would get this before sending."
- **Be specific about time.** "Today", "this week", and "yesterday" work, and so do exact dates.
- **Mention Reloop.** If your assistant has several tools, saying "in Reloop" helps it pick the right one.
---
# integrations/ai-tools/mcp-server.mdx
Source: https://reloop.sh/docs/integrations/ai-tools/mcp-server
Markdown: https://reloop.sh/docs/integrations/ai-tools/mcp-server.md
The [Model Context Protocol](https://modelcontextprotocol.io) (MCP) is an open standard that lets AI assistants call external tools. The Reloop MCP server connects your assistant to your Reloop organization, so you can send email, manage contacts, check deliverability, and more by describing what you want in plain language.
Reloop runs a hosted MCP server for you. Add one URL to your client, sign in with your Reloop account, and you're done. There's no API key to copy and nothing to install.
| Setting | Value |
|---|---|
| **Server URL** | `https://reloop.sh/mcp` |
| **Transport** | Streamable HTTP |
| **Authentication** | Sign in with Reloop (OAuth) |
The [MCP page in your dashboard](https://reloop.sh/dashboard/integrations/mcp) has setup steps, one-click install buttons, and copyable config for each client.
## What you can do
The MCP server is built to do everything you can do in the Reloop dashboard:
- **Emails**: Send transactional email with HTML or plain text, CC/BCC, reply-to, tags, templates, and scheduled delivery. Check what you sent and its delivery status.
- **Contacts**: Look up, create, update, unsubscribe, and delete contacts. Manage custom properties, groups, and channel subscriptions.
- **Campaigns**: Draft, schedule, and send campaigns to your audience.
- **Templates**: Create and edit email templates and reuse them when sending.
- **Automation**: Build flows such as a welcome series that runs when someone signs up.
- **Inbox**: Read inbound email and attachments on your receiving domains.
- **Metrics & logs**: Inspect email logs, bounces, and engagement, and ask why a number changed.
- **Domains**: Add sending domains, check DNS records, and verify them.
- **Webhooks & API keys**: Create and manage webhooks for email events, and manage API keys.
See [example prompts](/docs/integrations/ai-tools/mcp-prompts) for ideas.
Sending email is real and can't be undone. Most clients ask you to approve each tool call; read the sender, recipients, and content before you approve a send or a delete.
## Quick setup
Pick your client for full steps, including how to verify the connection and fix common problems.
Add Reloop with one `claude mcp add` command.
Add a custom connector on the web or in the desktop app.
One-click install or `.cursor/mcp.json`.
GitHub Copilot agent mode with `.vscode/mcp.json`.
Add Reloop to `~/.codex/config.toml`.
The short version for each:
```bash
claude mcp add --transport http reloop https://reloop.sh/mcp
```
Then run `/mcp` in Claude Code, pick **reloop**, and choose **Authenticate**.
In Claude, go to **Settings → Connectors**, click **Add custom connector**, name it `Reloop`, and use this URL:
```text
https://reloop.sh/mcp
```
Click **Connect** and sign in to Reloop.
Add to `.cursor/mcp.json` (or `~/.cursor/mcp.json` for every project):
```json
{
"mcpServers": {
"reloop": {
"url": "https://reloop.sh/mcp"
}
}
}
```
Then click **Connect** next to reloop in **Cursor Settings → MCP & Integrations**.
Add to `.vscode/mcp.json`:
```json
{
"servers": {
"reloop": {
"type": "http",
"url": "https://reloop.sh/mcp"
}
}
}
```
Click **Start** above the server and sign in to Reloop when asked.
Add to `~/.codex/config.toml`:
```toml
[mcp_servers.reloop]
url = "https://reloop.sh/mcp"
```
Then sign in:
```bash
codex mcp login reloop
```
Point any client that supports Streamable HTTP at this URL:
```text
https://reloop.sh/mcp
```
It discovers Reloop's OAuth server and signs you in through the browser. If your client can only launch local servers, use the [local server](#local-server-stdio) instead.
## How sign-in works
The hosted server is an OAuth protected resource. You don't need an API key.
1. Your client connects to `https://reloop.sh/mcp` and gets a `401` with a `WWW-Authenticate` challenge.
2. It reads `https://reloop.sh/.well-known/oauth-protected-resource/mcp` to find Reloop's authorization server, and registers itself automatically.
3. Your browser opens the Reloop sign-in page. You sign in, pick the organization the client can access, and approve.
4. The client sends the access token as `Authorization: Bearer ` on every request.
Tokens are short-lived, scoped to the MCP server, and tied to the organization you approved. Reloop checks every call against your permissions, so the assistant can only do what your account can do.
### Choosing an organization
If you belong to several organizations, you pick one on the consent screen. To switch, sign in again from your client and pick a different one. Clients that support custom headers can also send an `x-organization-id` header to select an organization per request.
## Local server (stdio)
Use the local server when your client can't connect to a remote URL, or when you prefer an API key over signing in. Your client starts `reloop-mcp` as a child process and talks to it over stdin/stdout.
You'll need Node.js 20 or later (for `npx`) and a [Reloop API key](https://reloop.sh/dashboard/api-keys). Replace `rl_prod_your_api_key` with your key.
```bash
claude mcp add reloop --env RELOOP_API_KEY=rl_prod_your_api_key -- npx -y reloop-mcp
```
Add to `claude_desktop_config.json` (Claude Desktop) or `.cursor/mcp.json` (Cursor):
```json
{
"mcpServers": {
"reloop": {
"command": "npx",
"args": ["-y", "reloop-mcp"],
"env": {
"RELOOP_API_KEY": "rl_prod_your_api_key"
}
}
}
}
```
Add to `.vscode/mcp.json`:
```json
{
"servers": {
"reloop": {
"type": "stdio",
"command": "npx",
"args": ["-y", "reloop-mcp"],
"env": {
"RELOOP_API_KEY": "rl_prod_your_api_key"
}
}
}
}
```
Add to `~/.codex/config.toml`:
```toml
[mcp_servers.reloop]
command = "npx"
args = ["-y", "reloop-mcp"]
env = { RELOOP_API_KEY = "rl_prod_your_api_key" }
```
Add to `~/.gemini/settings.json`:
```json
{
"mcpServers": {
"reloop": {
"command": "npx",
"args": ["-y", "reloop-mcp"],
"env": {
"RELOOP_API_KEY": "rl_prod_your_api_key"
}
}
}
}
```
Add to `opencode.json`:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"reloop": {
"type": "local",
"command": ["npx", "-y", "reloop-mcp"],
"enabled": true,
"environment": {
"RELOOP_API_KEY": "rl_prod_your_api_key"
}
}
}
}
```
For any other stdio client, run `npx -y reloop-mcp` (or `bunx reloop-mcp`) with `RELOOP_API_KEY` in the environment.
An API key is scoped to the organization it was created in, so the local server always acts in that organization.
## Self-hosted Reloop
If you run your own Reloop instance, set `RELOOP_BASE_URL` to its origin. Nothing is tied to `reloop.sh` beyond the default.
For the hosted-style setup, your instance serves the MCP endpoint at `https:///mcp` and every remote setup above works with that URL in place of `https://reloop.sh/mcp`.
For the local server, add `RELOOP_BASE_URL` next to your key:
```json
{
"mcpServers": {
"reloop": {
"command": "npx",
"args": ["-y", "reloop-mcp"],
"env": {
"RELOOP_API_KEY": "rl_prod_your_api_key",
"RELOOP_BASE_URL": "https://mail.example.com"
}
}
}
}
```
## Run the HTTP server yourself
You can run the Streamable HTTP server on your own infrastructure. It's stateless: there are no sessions, so you can run several instances behind any load balancer.
```bash
bunx reloop-mcp --transport http --host 0.0.0.0 --port 3000
```
The standalone HTTP server needs [Bun](https://bun.sh). On Node.js, use the Docker image instead:
```bash
docker build -t reloop/mcp .
docker run --rm -p 3000:3000 -e RELOOP_BASE_URL=https://mail.example.com reloop/mcp
```
Clients connect to `http://:3000/mcp` and authenticate on every request with one of:
- An OAuth access token: `Authorization: Bearer ` (recommended; clients get this by signing in)
- A Reloop API key: `Authorization: Bearer rl_prod_...` or `x-api-key: rl_prod_...`
`RELOOP_API_KEY` is not used by the HTTP transport. For example, to connect Claude Code with an API key:
```bash
claude mcp add --transport http reloop http://127.0.0.1:3000/mcp \
--header "Authorization: Bearer rl_prod_your_api_key"
```
Check that the server is up:
```bash
curl -s http://127.0.0.1:3000/mcp/healthz
```
```json
{ "status": "ok", "name": "reloop", "version": "0.1.0" }
```
`GET /healthz` returns the same response.
## Options
**CLI flags**
| Flag | Default | Description |
|---|---|---|
| `--transport ` | `stdio` | Transport to serve |
| `--host ` | `127.0.0.1` | HTTP bind hostname |
| `--port ` | `3000` | HTTP bind port |
| `--base-url ` | `https://reloop.sh` | Origin of the Reloop instance |
| `--version` | | Print the version and exit |
| `--help` | | Print help and exit |
**Environment variables**
| Variable | Default | Description |
|---|---|---|
| `RELOOP_API_KEY` | | Reloop API key. Required for stdio, ignored by HTTP |
| `RELOOP_BASE_URL` | `https://reloop.sh` | Origin of the Reloop instance |
| `RELOOP_MCP_RESOURCE` | `/mcp` | Canonical MCP resource identifier. Must match the auth server's `MCP_RESOURCE` |
| `RELOOP_AUTH_ISSUER` | `/api/auth/v1` | OAuth issuer advertised in the protected resource metadata |
| `RELOOP_TIMEOUT_MS` | `30000` | Per-request timeout to Reloop, from 1000 to 600000 |
| `HOST` | `127.0.0.1` | HTTP bind hostname |
| `PORT` | `3000` | HTTP bind port |
| `MCP_ALLOWED_HOSTS` | `*` | Comma-separated `Host` header allowlist. `*` or unset accepts any host |
| `MCP_ALLOWED_ORIGINS` | loopback origins | Comma-separated `Origin` header allowlist |
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error`, or `silent` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | unset | OTLP/HTTP endpoint (for example SigNoz). When set, every log line is also exported there |
| `OTEL_EXPORTER_OTLP_HEADERS` | unset | Headers for the OTLP endpoint as `key=value,key2=value2`, for example `signoz-ingestion-key=` |
Even with `MCP_ALLOWED_HOSTS` unset, every request still needs an OAuth token or API key. Set it to restrict which hostnames the server answers on.
## Security
- Credentials are per request. The server forwards the caller's token or key to Reloop and never stores, caches, or shares it.
- API keys and tokens are redacted from logs, and email content is never logged.
- `Host` and `Origin` headers are validated to block DNS-rebinding attacks from a browser.
- `RELOOP_BASE_URL` is server configuration, never a tool parameter, so a model can't redirect traffic to another host.
- Tools that send email or delete data are marked destructive, so clients can ask you to confirm before they run.
## Local development
The server's source is at [github.com/reloop-labs/reloop-mcp](https://github.com/reloop-labs/reloop-mcp).
```bash
git clone https://github.com/reloop-labs/reloop-mcp.git
cd reloop-mcp
bun install
bun run dev:stdio # stdio transport
bun run dev:http # HTTP transport on http://127.0.0.1:3000/mcp, reloads on change
bun run build # builds dist/cli.js
```
To point a stdio client at your local build, replace `npx -y reloop-mcp` with `node`:
```json
{
"mcpServers": {
"reloop": {
"command": "node",
"args": ["ABSOLUTE_PATH_TO_PROJECT/dist/cli.js"],
"env": {
"RELOOP_API_KEY": "rl_prod_your_api_key"
}
}
}
}
```
Before opening a pull request, run `bun run lint`, `bun run typecheck`, and `bun test`.
## Test with MCP Inspector
[MCP Inspector](https://github.com/modelcontextprotocol/inspector) is a browser UI for calling MCP tools by hand.
### Stdio
From the `reloop-mcp` folder:
```bash
npx @modelcontextprotocol/inspector -e RELOOP_API_KEY=rl_prod_your_api_key bun src/cli.ts
```
Inspector opens in your browser with the server already configured. Click **Connect**, then **List Tools**.
### HTTP
1. Start the server:
```bash
bun run dev:http
```
2. Start Inspector:
```bash
npx @modelcontextprotocol/inspector
```
3. In the browser, choose **Streamable HTTP** and set the URL to `http://127.0.0.1:3000/mcp`.
4. Add a header `Authorization: Bearer rl_prod_your_api_key` and turn it on.
5. Click **Connect**, then **List Tools**.
To test against the hosted server, use `https://reloop.sh/mcp` without a header. Inspector walks you through the OAuth sign-in.
## Troubleshooting
Remove the saved sign-in from your client and authenticate again. Each client page above has the exact steps. If you're on a self-hosted instance, check that `RELOOP_MCP_RESOURCE` matches the auth server's `MCP_RESOURCE`.
Sign in again and pick the right organization on the consent screen, or send an `x-organization-id` header if your client supports custom headers. With an API key, create the key in the organization you want.
The `from` address must use a sending domain that's verified in your organization. Ask your assistant to list your domains, or check [Domains](https://reloop.sh/dashboard/domain).
The stdio transport needs `RELOOP_API_KEY`, and the key has to look like a Reloop key (for example `rl_prod_...`). Copy it again from the [API keys page](https://reloop.sh/dashboard/api-keys).
The server reached something that isn't a Reloop API, which usually means `RELOOP_BASE_URL` is wrong. Set it to the origin of your instance, without a path.
Retry reads. Don't blindly retry a send: the first one may have gone out. Check your email logs first. You can raise `RELOOP_TIMEOUT_MS` if you run the server yourself.
---
# integrations/ai-tools/mcp/claude-code.mdx
Source: https://reloop.sh/docs/integrations/ai-tools/mcp/claude-code
Markdown: https://reloop.sh/docs/integrations/ai-tools/mcp/claude-code.md
[Claude Code](https://docs.anthropic.com/en/docs/claude-code) is Anthropic's coding agent for the terminal. Once you add Reloop, Claude Code can send email, manage contacts, check domains, and more from your terminal session.
## Prerequisites
- Claude Code installed and signed in
- A [Reloop account](https://reloop.sh/dashboard)
- A [verified domain](https://reloop.sh/dashboard/domain) if you want to send email
## Setup
Run this in your terminal:
```bash
claude mcp add --transport http reloop https://reloop.sh/mcp
```
This adds Reloop to the current project. Add `--scope user` to make it available in every project:
```bash
claude mcp add --transport http --scope user reloop https://reloop.sh/mcp
```
Start Claude Code and run:
```text
/mcp
```
Pick **reloop** and choose **Authenticate**. Your browser opens the Reloop sign-in page. Sign in, choose the organization Claude Code can access, and approve.
## Verify it works
Run `/mcp` again. **reloop** should show as connected, with its tools listed. From your shell, `claude mcp list` also shows the server and its status.
Then ask something read-only to confirm it reaches your organization:
> "List my Reloop domains and tell me which ones are verified."
## Example prompts
- "Send a welcome email to user@example.com with a personalized greeting."
- "Add a new contact for jane@example.com named Jane Doe."
- "List all emails I sent today and show their delivery status."
See [more example prompts](/docs/integrations/ai-tools/mcp-prompts).
## Troubleshooting
Run `/mcp`, pick **reloop**, and choose **Authenticate** again. If sign-in keeps failing, choose **Clear authentication** first, then authenticate.
Clear authentication from `/mcp`, authenticate again, and pick the right organization on the consent screen. You can also pin an organization with a header:
```bash
claude mcp remove reloop
claude mcp add --transport http reloop https://reloop.sh/mcp \
--header "x-organization-id: YOUR_ORGANIZATION_ID"
```
You may have added it in a different project. Run `claude mcp list` in the project you're working in, or add it again with `--scope user`.
Use the [local server](/docs/integrations/ai-tools/mcp-server#local-server-stdio):
```bash
claude mcp add reloop --env RELOOP_API_KEY=rl_prod_your_api_key -- npx -y reloop-mcp
```
---
# integrations/ai-tools/mcp/claude.mdx
Source: https://reloop.sh/docs/integrations/ai-tools/mcp/claude
Markdown: https://reloop.sh/docs/integrations/ai-tools/mcp/claude.md
Add Reloop to [Claude](https://claude.ai) as a custom connector to work with your email from any chat. The connector works on the web and in the Claude desktop app, and you only set it up once.
## Prerequisites
- A Claude plan that supports custom connectors
- On Team and Enterprise plans, an owner has to add the connector under **Organization settings** before members can connect
- A [Reloop account](https://reloop.sh/dashboard)
- A [verified domain](https://reloop.sh/dashboard/domain) if you want to send email
## Setup
In Claude, go to **Settings → Connectors** and click **Add custom connector**.
Name it `Reloop` and use this URL:
```text
https://reloop.sh/mcp
```
Leave the advanced OAuth fields empty. Reloop registers the client automatically.
Click **Connect**. Sign in to Reloop, choose the organization Claude can access, and approve.
Open the tools menu in a chat and turn on **Reloop**.
## Verify it works
In **Settings → Connectors**, Reloop shows as connected. In a new chat with Reloop turned on, ask something read-only:
> "List my Reloop domains and tell me which ones are verified."
Claude asks permission before it calls a Reloop tool the first time.
## Example prompts
- "Show me all inbound emails I received this week."
- "Why did my bounce rate go up yesterday?"
- "Create a password reset email template that matches our brand."
See [more example prompts](/docs/integrations/ai-tools/mcp-prompts).
## Troubleshooting
In **Settings → Connectors**, open Reloop, disconnect it, and click **Connect** again.
Disconnect Reloop, connect again, and pick the right organization on the consent screen. Custom connectors can't send extra headers, so the consent screen is how you choose.
Your plan may not include custom connectors, or on Team and Enterprise plans an owner has to add Reloop first.
Check that Reloop is turned on in the tools menu for that chat, and mention Reloop in your prompt.
---
# integrations/ai-tools/mcp/codex.mdx
Source: https://reloop.sh/docs/integrations/ai-tools/mcp/codex
Markdown: https://reloop.sh/docs/integrations/ai-tools/mcp/codex.md
[Codex](https://developers.openai.com/codex) is OpenAI's coding agent for the terminal and IDE. The CLI and the IDE extension share one config file, so you set up Reloop once for both.
## Prerequisites
- The Codex CLI or IDE extension installed
- A [Reloop account](https://reloop.sh/dashboard)
- A [verified domain](https://reloop.sh/dashboard/domain) if you want to send email
## Setup
Add this to `~/.codex/config.toml`:
```toml
[mcp_servers.reloop]
url = "https://reloop.sh/mcp"
```
Run the login command:
```bash
codex mcp login reloop
```
Your browser opens the Reloop sign-in page. Sign in, choose the organization Codex can access, and approve.
## Verify it works
Run `codex mcp list` and check that reloop is listed. In a Codex session, run `/mcp` to see its tools. Then ask something read-only:
> "List my Reloop domains and tell me which ones are verified."
## Example prompts
- "Create a campaign for our product launch announcement."
- "Create a contact group for users who signed up this month."
- "Build an automation that sends a welcome series when someone signs up."
See [more example prompts](/docs/integrations/ai-tools/mcp-prompts).
## Troubleshooting
Log out and back in:
```bash
codex mcp logout reloop
codex mcp login reloop
```
Update Codex to the latest version. Remote servers with OAuth need a recent release.
Log out, log in again, and pick the right organization on the consent screen. Or pin one with a header:
```toml
[mcp_servers.reloop]
url = "https://reloop.sh/mcp"
http_headers = { "x-organization-id" = "YOUR_ORGANIZATION_ID" }
```
Use the [local server](/docs/integrations/ai-tools/mcp-server#local-server-stdio):
```toml
[mcp_servers.reloop]
command = "npx"
args = ["-y", "reloop-mcp"]
env = { RELOOP_API_KEY = "rl_prod_your_api_key" }
```
---
# integrations/ai-tools/mcp/cursor.mdx
Source: https://reloop.sh/docs/integrations/ai-tools/mcp/cursor
Markdown: https://reloop.sh/docs/integrations/ai-tools/mcp/cursor.md
Add Reloop to [Cursor](https://cursor.com) so the agent can send test emails, look up contacts, check domains, and manage templates while you build.
## Prerequisites
- Cursor installed
- A [Reloop account](https://reloop.sh/dashboard)
- A [verified domain](https://reloop.sh/dashboard/domain) if you want to send email
## Setup
The quickest way is the **Add to Cursor** button on the [Cursor page in your dashboard](https://reloop.sh/dashboard/integrations/mcp/cursor). It opens Cursor with Reloop filled in.
Or add this to `.cursor/mcp.json` in your project. Use `~/.cursor/mcp.json` to make it available in every project.
```json
{
"mcpServers": {
"reloop": {
"url": "https://reloop.sh/mcp"
}
}
}
```
Open **Cursor Settings → MCP & Integrations** and click **Connect** next to **reloop**. Your browser opens the Reloop sign-in page. Sign in, choose the organization Cursor can access, and approve.
## Verify it works
In **Cursor Settings → MCP & Integrations**, reloop shows a green status dot and lists its tools. Then ask the agent something read-only:
> "List my Reloop domains and tell me which ones are verified."
## Example prompts
- "Send a test email to me@example.com with the HTML from emails/welcome.html."
- "Create a webhook that notifies https://example.com/hooks/reloop when an email bounces."
- "Verify my domain and check the DNS configuration status."
See [more example prompts](/docs/integrations/ai-tools/mcp-prompts).
## Troubleshooting
In **Cursor Settings → MCP & Integrations**, turn reloop off and on, then click **Connect**. If that doesn't help, remove the server, add it again, and sign in.
Sign in again and pick the right organization on the consent screen, or pin one with a header:
```json
{
"mcpServers": {
"reloop": {
"url": "https://reloop.sh/mcp",
"headers": {
"x-organization-id": "YOUR_ORGANIZATION_ID"
}
}
}
}
```
Check that reloop is turned on in settings, and mention Reloop in your prompt.
Use the [local server](/docs/integrations/ai-tools/mcp-server#local-server-stdio) with `npx -y reloop-mcp` and `RELOOP_API_KEY`.
---
# integrations/ai-tools/mcp/vscode.mdx
Source: https://reloop.sh/docs/integrations/ai-tools/mcp/vscode
Markdown: https://reloop.sh/docs/integrations/ai-tools/mcp/vscode.md
Add Reloop to [Visual Studio Code](https://code.visualstudio.com) so GitHub Copilot in agent mode can send email, manage contacts, and check your Reloop setup from the editor.
## Prerequisites
- VS Code with GitHub Copilot and agent mode available
- A [Reloop account](https://reloop.sh/dashboard)
- A [verified domain](https://reloop.sh/dashboard/domain) if you want to send email
## Setup
The quickest way is the **Install in VS Code** button on the [VS Code page in your dashboard](https://reloop.sh/dashboard/integrations/mcp/vscode).
Or add this to `.vscode/mcp.json` in your workspace:
```json
{
"servers": {
"reloop": {
"type": "http",
"url": "https://reloop.sh/mcp"
}
}
}
```
Click **Start** above the server in `mcp.json`, or run **MCP: List Servers** from the Command Palette and start **reloop**. VS Code asks you to sign in to Reloop in the browser. Sign in, choose the organization VS Code can access, and approve.
## Verify it works
Run **MCP: List Servers**. reloop shows as running. Open Copilot Chat in **Agent** mode and click the tools icon; the Reloop tools are listed. Then ask something read-only:
> "List my Reloop domains and tell me which ones are verified."
## Example prompts
- "Add a new contact with their name and email."
- "Send a batch of onboarding emails to these five new users."
- "List all emails I sent today and show their delivery status."
See [more example prompts](/docs/integrations/ai-tools/mcp-prompts).
## Troubleshooting
Run **MCP: List Servers**, pick reloop, and restart it. To sign in from scratch, sign out of Reloop from the **Accounts** menu in the activity bar, then start the server again.
Sign out, start the server again, and pick the right organization on the consent screen. Or pin one with a header:
```json
{
"servers": {
"reloop": {
"type": "http",
"url": "https://reloop.sh/mcp",
"headers": {
"x-organization-id": "YOUR_ORGANIZATION_ID"
}
}
}
}
```
Make sure Chat is in **Agent** mode and the Reloop tools are checked in the tools picker.
Use the [local server](/docs/integrations/ai-tools/mcp-server#local-server-stdio) with `npx -y reloop-mcp` and `RELOOP_API_KEY`.
---
# integrations/ai-tools/openclaw-guide.mdx
Source: https://reloop.sh/docs/integrations/ai-tools/openclaw-guide
Markdown: https://reloop.sh/docs/integrations/ai-tools/openclaw-guide.md
Learn how to give your AI agent a dedicated email inbox using Reloop, so it can send and receive emails autonomously.
## Why should I give my agent an inbox?
An AI agent with its own email address can:
- Sign up for its own accounts to GitHub, hosting platforms, and more, so you don't need to share your own credentials.
- Process attachments (like receipts, invoices, etc.) and act accordingly
- Receive newsletters, parse them, and send the most important information to you
- Send you daily reports and digests
- Send and receive emails on your behalf
## How to set up an inbox for OpenClaw
### Step 1: Install the skill
Tell your agent:
```
Let's get you set up with an email inbox! Install the reloop-skills from https://github.com/reloop-labs/reloop-skills and review them before continuing.
```
Alternatively, you can install the skills using the [Reloop CLI](/docs/resources/cli):
```bash
npx skills add reloop/reloop-skills
```
### Step 2: Get an API key
1. Open the [API Keys](https://reloop.sh/dashboard/api-keys) page
2. Choose a name and permission scope
3. Create the API key
Store the key securely:
1. SSH into your agent's machine and store the API key in an `.env` file.
2. Store the API key in a password manager like 1Password, and give your agent access to its own vault. This can be done using a [1Password Service Account](https://developer.1password.com/docs/service-accounts/) on team plans.
### Step 3: Verify a domain
We [strongly recommend](/docs/learn/domain) using a subdomain like `agent.example.com` rather than your root `example.com` domain. This keeps your agent's email reputation separate.
You can set up a domain using the [Reloop CLI](/docs/resources/cli) or through the dashboard:
1. Go to the [Domains tab](https://reloop.sh/dashboard/domain) and add a domain
2. Select a subdomain and region
3. Configure DNS:
- **Auto Configure**: Automatically configure DNS records if your provider supports it.
- **Go to (provider)**: Open your provider's website to add records manually.
- **I've added the records**: Manually add DNS records via your DNS provider.
4. Enable receiving on the domain
5. Add the DNS records and wait for verification
See our [guide on verifying a domain](/docs/learn/domain) for detailed instructions.
### Step 4: Use webhooks to receive emails
**Ask your agent to set up a webhook server:**
```
I want you to be able to receive emails, too. Let's set up a webhook to receive emails at my domain using the agent email inbox agent skill.
```
**Set up a tunnel:**
Use [Tailscale Funnel](https://tailscale.com/kb/1223/funnel) to expose your agent's webhook server:
```bash
tailscale funnel 3000
```
This gives you a public URL like `https://hostname.tailnet-name.ts.net`.
**Give your agent access to secrets securely** (see [Step 2](#step-2-get-an-api-key)).
**Add the webhook to Reloop:**
```
Let's add a webhook to Reloop using the server you just built. Use the email.received event, as instructed by the reloop-skills agent skill, and securely save the returned webhook signing secret.
```
**Test receiving an email:**
```typescript
const reloop = new Reloop(process.env.RELOOP_API_KEY);
// Security: strict allowlist
const ALLOWED_SENDERS = ['your@email.com'];
async function handler(req) {
const payload = await req.text();
const id = req.headers.get('svix-id');
const timestamp = req.headers.get('svix-timestamp');
const signature = req.headers.get('svix-signature');
if (!id || !timestamp || !signature) {
return new Response('Missing headers', { status: 400 });
}
const event = reloop.webhooks.verify({
payload,
headers: { id, timestamp, signature },
webhookSecret: process.env.RELOOP_WEBHOOK_SECRET,
});
if (event.type === 'email.received') {
// Security check
if (!ALLOWED_SENDERS.includes(event.data.from.toLowerCase())) {
return new Response('OK', { status: 200 });
}
// Get full email
const { data: email } = await reloop.emails.receiving.get(
event.data.email_id,
);
// Notify user instantly
await notifyUser(email);
}
return new Response('OK', { status: 200 });
}
```
See our [guide on receiving emails with Reloop](/docs/guides/receiving-emails) for more details.
### Step 5: Hook into OpenClaw's APIs for instant notifications
**Ask your agent to hook into the Gateway API:**
```
Use the OpenClaw Gateway API to notify me instantly when you receive an email webhook call from Reloop. Use the reloop-skills agent skill for guidance.
```
**Test instant notifications** by sending an email to your agent's address and verifying it receives it.
## Security considerations
The [Agent Email Inbox skill](https://github.com/reloop-labs/reloop-skills/blob/main/skills/agent-email-inbox/SKILL.md#security-levels) includes security guidelines with five levels:
1. **Strict Allowlist**: Only allow emails from specific senders. Recommended for most use cases.
2. **Domain Allowlist**: Allow emails from any sender from a given domain (e.g. anyone from `example.com`).
3. **Content Filtering with Sanitization**: Accept emails from anyone, but sanitize content to remove potential injection attempts.
4. **Sandboxed Processing**: Process all emails but in a restricted context where the agent has limited capabilities.
5. **Human-in-the-Loop**: Process all emails but require human approval for each email.
Review the [Reloop Skill](https://github.com/reloop-labs/reloop-skills/blob/main/skills/agent-email-inbox/SKILL.md#security-levels) for detailed security guidance. If you have questions, [contact support](https://reloop.sh/help).
---
# integrations/agent-skills/agent-email-inbox.mdx
Source: https://reloop.sh/docs/integrations/agent-skills/agent-email-inbox
Markdown: https://reloop.sh/docs/integrations/agent-skills/agent-email-inbox.md
Give your AI agent a secure email inbox to receive and act on inbound emails. This skill provides your agent with the knowledge to set up, secure, and manage an email inbox for receiving messages.
## Installation
The Agent Email Inbox skill is part of the [reloop-skills](https://github.com/reloop-labs/reloop-skills) collection:
```bash
npx skills add reloop/reloop-skills
```
## Features
- **Webhook-based receiving**: Set up webhooks to receive inbound emails in real-time
- **Email parsing**: Extract structured data from incoming emails (sender, subject, body, attachments)
- **Attachment handling**: Download and process email attachments securely
- **Security guidelines**: Built-in security levels from strict allowlists to sandboxed processing
- **Event-driven architecture**: React to incoming emails with automated workflows
## Security Levels
The skill includes five security levels you can implement based on your needs:
1. **Strict Allowlist**: Only process emails from pre-approved senders. Recommended for most use cases.
2. **Domain Allowlist**: Allow emails from any sender at approved domains.
3. **Content Filtering**: Accept all emails but sanitize content before processing.
4. **Sandboxed Processing**: Process in a restricted context with limited agent capabilities.
5. **Human-in-the-Loop**: Queue all emails for human approval before agent processing.
## How it works
Your agent needs a verified domain with receiving enabled. See [Domains](/docs/learn/domain).
Your agent creates an HTTP endpoint to receive webhook events from Reloop.
Subscribe to `email.received` events via the Reloop API or CLI.
When an email arrives, Reloop sends a webhook to your endpoint. Your agent verifies the signature, fetches the full email, and processes it.
## Learn More
- [OpenClaw Guide](/docs/integrations/ai-tools/openclaw-guide): Step-by-step guide for setting up an agent inbox
- [Receiving Emails](/docs/guides/receiving-emails): General guide on receiving emails with Reloop
- [Webhooks](/docs/webhooks): Webhook setup and event types
- [View on GitHub](https://github.com/reloop-labs/reloop-skills/tree/main/skills/agent-email-inbox)
---
# integrations/agent-skills/email-best-practices.mdx
Source: https://reloop.sh/docs/integrations/agent-skills/email-best-practices
Markdown: https://reloop.sh/docs/integrations/agent-skills/email-best-practices.md
A comprehensive skill that teaches your AI agent best practices for building production-ready email systems. This skill covers deliverability, authentication, content guidelines, and compliance.
## Installation
```bash
npx skills add reloop/email-best-practices
```
## What's covered
### Deliverability
- SPF, DKIM, and DMARC authentication setup
- Domain warmup strategies for new sending domains
- Reputation management and monitoring
- Bounce handling and suppression lists
- Throttling and rate limiting best practices
### Content Guidelines
- Subject line optimization for engagement
- Plain text vs HTML email best practices
- Image-to-text ratio recommendations
- Call-to-action (CTA) best practices
- Mobile-responsive email design
### Compliance
- CAN-SPAM Act requirements
- GDPR considerations for email
- Unsubscribe link requirements
- Physical address requirements
- Consent management (opt-in/opt-out)
### Transactional vs Marketing
- When to use transactional emails vs marketing emails
- Separate sending domains and IP pools
- Appropriate content for each type
- Legal distinctions and requirements
### Error Handling
- Retry strategies for failed sends
- Handling bounces (hard vs soft)
- Managing complaint feedback loops
- Monitoring delivery rates and engagement metrics
## Rules
The skill provides your agent with actionable rules such as:
- Always include a plain text version alongside HTML
- Never use a no-reply sender address for marketing emails
- Always verify your sending domain before sending at scale
- Use dedicated subdomains for transactional and marketing email
- Implement exponential backoff for API retries
- Always include an unsubscribe mechanism in marketing emails
## Learn More
- [Sending Emails](/docs/learn/emails): Guide to sending emails with Reloop
- [Domains](/docs/learn/domain): Domain verification and DNS configuration
- [View on GitHub](https://github.com/reloop-labs/reloop-skills/tree/main/skills/email-best-practices)
---
# integrations/agent-skills/reloop-skill.mdx
Source: https://reloop.sh/docs/integrations/agent-skills/reloop-skill
Markdown: https://reloop.sh/docs/integrations/agent-skills/reloop-skill.md
Send emails through the Reloop API with AI agents. This skill gives your agent comprehensive knowledge of the Reloop API, including sending, receiving, and managing emails.
## Installation
The Reloop skill is part of the [reloop-skills](https://github.com/reloop-labs/reloop-skills) collection. It also includes the [Agent Email Inbox](/docs/integrations/agent-skills/agent-email-inbox) skill.
```bash
npx skills add reloop/reloop-skills
```
## Advantages
- **Single and batch email sending**: Send individual emails or batch up to 100 emails per request.
- **Built-in error handling and retry logic**: Automatic retries with exponential backoff for transient failures.
- **Idempotency key support**: Prevent duplicate sends with idempotency keys for safe retries.
- **Multi-language SDK support**: Works with Node.js, Python, Ruby, Go, and other supported SDKs.
- **Automatic activation for email tasks**: AI agents automatically use this skill when email sending is needed.
## What's included
The skill teaches your agent:
- How to authenticate with the Reloop API
- Sending emails (single and batch)
- Managing contacts and audiences
- Working with templates
- Handling webhooks and events
- Domain verification and configuration
- Best practices for deliverability
## Learn More
- [Reloop API Reference](/docs/api)
- [SDKs](/docs/resources/sdks)
- [View on GitHub](https://github.com/reloop-labs/reloop-skills/tree/main/skills/reloop)
---
# resources/cli.mdx
Source: https://reloop.sh/docs/resources/cli
Markdown: https://reloop.sh/docs/resources/cli.md
# CLI
Coming soon.
---
# resources/sdks.mdx
Source: https://reloop.sh/docs/resources/sdks
Markdown: https://reloop.sh/docs/resources/sdks.md
Official Node.js SDK for server-side JavaScript and TypeScript.
Official Python client library for the Reloop API.
Official Go client library for the Reloop API.
Official Java SDK for JVM-based applications.
Official PHP library for integrating with Reloop.
Official Ruby gem for integrating with Reloop.
Official Rust crate for integrating with Reloop.
Official Elixir library for integrating with Reloop.
Official .NET library for C# and other JVM/.NET applications.
## Missing your language?
We're actively expanding our SDK coverage. If your language or framework isn't listed above, you can:
Integrate directly with our raw REST API endpoints.
Open an issue on GitHub to request or upvote a new SDK.
All SDKs are open-source and contributions are welcome.
---
# resources/security.mdx
Source: https://reloop.sh/docs/resources/security
Markdown: https://reloop.sh/docs/resources/security.md
# Security
Coming soon.
---
# examples/go/gin.mdx
Source: https://reloop.sh/docs/examples/go/gin
Markdown: https://reloop.sh/docs/examples/go/gin.md
## Installation
Install the official Go SDK:
```bash
go get github.com/reloop-labs/reloop-go
```
## Sending an Email
```go
package main
"context"
"net/http"
"os"
"github.com/gin-gonic/gin"
"github.com/reloop-labs/reloop-go"
)
func main() {
r := gin.Default()
client := reloop.NewClient(os.Getenv("RELOOP_API_KEY"))
r.POST("/send-email", func(c *gin.Context) {
params := &reloop.SendEmailParams{
From: "onboarding@reloop.sh",
To: []string{"delivered@resend.dev"},
Subject: "Hello from Gin",
HTML: "Congrats on sending your first email via Reloop from Go Gin!
",
}
res, err := client.Emails.Send(context.Background(), params)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, res)
})
r.Run(":8080")
}
```
---
# examples/nodejs/express.mdx
Source: https://reloop.sh/docs/examples/nodejs/express
Markdown: https://reloop.sh/docs/examples/nodejs/express.md
## Installation
Install the official Node.js SDK:
```bash
npm install reloop-email
```
## Setup Environment Variables
Add your Reloop API key to your environment variables or a `.env` file:
```env
RELOOP_API_KEY=rl_your_api_key_here
```
## Sending an Email
Here is an example of an Express route that sends an email:
```typescript
const app = express();
const reloop = new Reloop({ apiKey: process.env.RELOOP_API_KEY });
app.post('/send-email', async (req, res) => {
try {
const data = await reloop.mail.send({
from: 'onboarding@reloop.sh',
to: 'delivered@resend.dev',
subject: 'Hello from Express',
html: 'Congrats on sending your first email via Reloop from an Express app!
',
});
res.json(data);
} catch (error) {
res.status(500).json({ error });
}
});
app.listen(3000, () => {
console.log('App listening on port 3000');
});
```
---
# examples/nodejs/nextjs.mdx
Source: https://reloop.sh/docs/examples/nodejs/nextjs
Markdown: https://reloop.sh/docs/examples/nodejs/nextjs.md
## Installation
```bash
npm install reloop-email
```
## Setup environment variables
Add your Reloop API key to `.env.local` (server-only; do not use `NEXT_PUBLIC_`):
```env
RELOOP_API_KEY=rl_your_api_key_here
```
## Shared client
```typescript
// lib/reloop.ts
apiKey: process.env.RELOOP_API_KEY!,
});
```
## Route Handler
```typescript
// app/api/send-email/route.ts
try {
const { to, subject, html, text } = await request.json();
const { response, emailError } = await reloop.mail.send({
from: "Acme ",
to,
subject: subject ?? "Hello from Next.js",
html: html ?? "Congrats on sending your first email via Reloop!
",
text: text ?? "Congrats on sending your first email via Reloop!",
});
if (emailError) {
return NextResponse.json({ error: emailError }, { status: 502 });
}
return NextResponse.json(response);
} catch (error) {
return NextResponse.json({ error }, { status: 500 });
}
}
```
## Server Action
```typescript
// app/actions/send-email.ts
"use server";
const { response, emailError } = await reloop.mail.send({
from: "Acme ",
to: email,
subject: "Welcome",
html: "Thanks for signing up.
",
text: "Thanks for signing up.",
});
if (emailError) {
return { ok: false as const, error: "Failed to send" };
}
return { ok: true as const, messageId: response?.messageId };
}
```
## Full guide
For forms, auth, Edge runtime, Vercel deploy, and a full comparison of Server Actions vs Route Handlers, see the blog post: [How to Send Email in Next.js App Router](/blog/send-email-nextjs-app-router).
---
# examples/php/laravel.mdx
Source: https://reloop.sh/docs/examples/php/laravel
Markdown: https://reloop.sh/docs/examples/php/laravel.md
## Installation
Install the official PHP SDK via Composer:
```bash
composer require reloop-labs/reloop-php
```
## Setup Configuration
Add the API key to your `.env` file:
```env
RELOOP_API_KEY=rl_your_api_key_here
```
## Sending an Email
Here is an example of sending an email from a controller:
```php
mail->send([
'from' => 'onboarding@reloop.sh',
'to' => 'delivered@resend.dev',
'subject' => 'Hello from Laravel',
'html' => 'Congrats on sending your first email via Reloop from Laravel!
',
]);
return response()->json($response);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 500);
}
}
}
```
---
# examples/python/django.mdx
Source: https://reloop.sh/docs/examples/python/django
Markdown: https://reloop.sh/docs/examples/python/django.md
## Installation
```bash
pip install reloop-email
```
## Setup settings.py
Add your API key to your settings:
```python
# settings.py
RELOOP_API_KEY = os.environ.get("RELOOP_API_KEY", "rl_your_api_key_here")
```
## Sending an Email
Use the client within your Django views:
```python
from django.http import JsonResponse
from django.conf import settings
from reloop_email import Reloop
reloop = Reloop(api_key=settings.RELOOP_API_KEY)
def send_email_view(request):
if request.method == 'POST':
try:
result = reloop.mail.send({
"from": "onboarding@reloop.sh",
"to": "delivered@resend.dev",
"subject": "Hello from Django",
"html": "Congrats on sending your first email via Reloop from Django!
",
})
if result.email_error:
raise result.email_error
return JsonResponse(result.response)
except Exception as e:
return JsonResponse({"error": str(e)}, status=500)
```
---
# examples/python/fastapi.mdx
Source: https://reloop.sh/docs/examples/python/fastapi
Markdown: https://reloop.sh/docs/examples/python/fastapi.md
## Installation
Install the official Python SDK:
```bash
pip install reloop-email
```
## Setup Environment Variables
Add your API key to your environment:
```bash
```
## Sending an Email
Here is a simple FastAPI route that sends an email:
```python
from fastapi import FastAPI, HTTPException
from reloop_email import Reloop
app = FastAPI()
reloop = Reloop(api_key=os.environ.get("RELOOP_API_KEY"))
# The SDK is synchronous. Use `def`, not `async def`, so FastAPI runs the
# route in its thread pool instead of blocking the event loop.
@app.post("/send-email")
def send_email():
try:
result = reloop.mail.send({
"from": "onboarding@reloop.sh",
"to": "delivered@resend.dev",
"subject": "Hello from FastAPI",
"html": "Congrats on sending your first email via Reloop from FastAPI!
",
})
if result.email_error:
raise result.email_error
return result.response
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
```
---
# examples/python/flask.mdx
Source: https://reloop.sh/docs/examples/python/flask
Markdown: https://reloop.sh/docs/examples/python/flask.md
## Installation
```bash
pip install reloop-email
```
## Sending an Email
```python
from flask import Flask, jsonify
from reloop_email import Reloop
app = Flask(__name__)
reloop = Reloop(api_key=os.environ.get("RELOOP_API_KEY"))
@app.route("/send-email", methods=["POST"])
def send_email():
try:
result = reloop.mail.send({
"from": "onboarding@reloop.sh",
"to": "delivered@resend.dev",
"subject": "Hello from Flask",
"html": "Congrats on sending your first email via Reloop from Flask!
",
})
if result.email_error:
raise result.email_error
return jsonify(result.response)
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == "__main__":
app.run(port=5000)
```
---
# examples/ruby/rails.mdx
Source: https://reloop.sh/docs/examples/ruby/rails
Markdown: https://reloop.sh/docs/examples/ruby/rails.md
## Installation
Add the official Ruby SDK to your Gemfile:
```ruby
gem 'reloop-ruby'
```
And run:
```bash
bundle install
```
## Setup Credentials
Set your API key in your environment variables:
```bash
```
## Sending an Email
Define a controller or a mailer to send emails:
```ruby
class EmailsController < ApplicationController
def create
reloop = Reloop::Client.new(api_key: ENV['RELOOP_API_KEY'])
begin
response = reloop.mail.send(
from: 'onboarding@reloop.sh',
to: 'delivered@resend.dev',
subject: 'Hello from Rails',
html: 'Congrats on sending your first email via Reloop from Ruby on Rails!
'
)
render json: response
rescue => e
render json: { error: e.message }, status: :internal_server_error
end
end
end
```
---
# examples/rust/axum.mdx
Source: https://reloop.sh/docs/examples/rust/axum
Markdown: https://reloop.sh/docs/examples/rust/axum.md
## Installation
Add the official Rust SDK and standard tokio dependencies to your `Cargo.toml`:
```toml
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
reloop = "0.1"
serde_json = "1"
```
## Sending an Email
```rust
use axum::{routing::post, Json, Router};
use reloop::ReloopClient;
use serde_json::json;
use std::env;
#[tokio::main]
async fn main() {
let app = Router::new().route("/send-email", post(send_email));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn send_email() -> Result, (axum::http::StatusCode, String)> {
let api_key = env::var("RELOOP_API_KEY").map_err(|_| {
(axum::http::StatusCode::INTERNAL_SERVER_ERROR, "Missing API Key".to_string())
})?;
let client = ReloopClient::new(api_key, None);
match client.mail().send(json!({
"from": "onboarding@reloop.sh",
"to": "delivered@resend.dev",
"subject": "Hello from Axum",
"html": "Congrats on sending your first email via Reloop from Rust Axum!
",
})).await {
Ok(res) => Ok(Json(res)),
Err(e) => Err((axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string())),
}
}
```
---
# examples/smtp/go.mdx
Source: https://reloop.sh/docs/examples/smtp/go
Markdown: https://reloop.sh/docs/examples/smtp/go.md
[View complete runnable example on GitHub ↗](https://github.com/reloop-labs/reloop-examples/tree/main/smtp-go)
## Code Example
```go
package main
"crypto/tls"
"fmt"
"net/smtp"
"os"
)
func main() {
apiKey := os.Getenv("RELOOP_API_KEY")
// SMTP server configuration
smtpHost := "smtp.reloop.sh"
smtpPort := "587"
auth := smtp.PlainAuth("", apiKey, apiKey, smtpHost)
to := []string{"delivered@resend.dev"}
msg := []byte("To: delivered@resend.dev\r\n" +
"Subject: Hello from Go net/smtp\r\n" +
"Content-Type: text/html; charset=UTF-8\r\n" +
"\r\n" +
"Congrats on sending your first email via Reloop SMTP using net/smtp!
\r\n")
err := smtp.SendMail(smtpHost+":"+smtpPort, auth, "onboarding@reloop.sh", to, msg)
if err != nil {
fmt.Println("Error connecting:", err)
return
}
defer conn.Close()
client, err := smtp.NewClient(conn, host)
if err != nil {
fmt.Println("Error creating client:", err)
return
}
defer client.Close()
if err = client.Auth(auth); err != nil {
fmt.Println("Auth failed:", err)
return
}
if err = client.Mail(from); err != nil {
fmt.Println("MAIL FROM failed:", err)
return
}
for _, addr := range to {
if err = client.Rcpt(addr); err != nil {
fmt.Println("RCPT TO failed:", err)
return
}
}
w, err := client.Data()
if err != nil {
fmt.Println("DATA failed:", err)
return
}
if _, err = w.Write(msg); err != nil {
fmt.Println("Write failed:", err)
return
}
if err = w.Close(); err != nil {
fmt.Println("Close failed:", err)
return
}
_ = client.Quit()
fmt.Println("Email sent successfully!")
}
```
---
# examples/smtp/introduction.mdx
Source: https://reloop.sh/docs/examples/smtp/introduction
Markdown: https://reloop.sh/docs/examples/smtp/introduction.md
## SMTP Credentials
To connect to the Reloop SMTP server, use the following configuration settings:
| Setting | Value |
| :--- | :--- |
| **Host** | `smtp.reloop.sh` |
| **Port** | `587`, or `2587` / `2025` if 587 is blocked |
| **Username** | Any string, for example `reloop` |
| **Password** | Your Reloop API Key |
| **Encryption** | STARTTLS on every port. Implicit TLS (port `465`) is not offered. |
## Code Examples
Configure standard SMTP library examples or explore step-by-step guides for your language:
Standard Node SMTP using Nodemailer.
Read Guide →
GitHub ↗
Built-in smtplib Python SMTP.
Read Guide →
GitHub ↗
Built-in net/smtp Go implementation.
Read Guide →
GitHub ↗
Standard PHP SMTP connection.
Read Guide →
GitHub ↗
Standard Ruby Net::SMTP integration.
Read Guide →
GitHub ↗
Lettre SMTP library for Rust.
Read Guide →
GitHub ↗
---
# examples/smtp/nodemailer.mdx
Source: https://reloop.sh/docs/examples/smtp/nodemailer
Markdown: https://reloop.sh/docs/examples/smtp/nodemailer.md
[View complete runnable example on GitHub ↗](https://github.com/reloop-labs/reloop-examples/tree/main/smtp-nodemailer)
## Installation
Install Nodemailer:
```bash
npm install nodemailer
```
## Code Example
```javascript
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: 'smtp.reloop.sh',
port: 587,
secure: false, // Reloop uses STARTTLS; keep false on every port
auth: {
user: 'reloop',
pass: process.env.RELOOP_API_KEY,
},
});
async function main() {
const info = await transporter.sendMail({
from: '"Onboarding" ',
to: 'delivered@resend.dev',
subject: 'Hello from Nodemailer',
html: 'Congrats on sending your first email via Reloop SMTP using Nodemailer!
',
});
console.log('Message sent: %s', info.messageId);
}
main().catch(console.error);
```
---
# examples/smtp/php.mdx
Source: https://reloop.sh/docs/examples/smtp/php
Markdown: https://reloop.sh/docs/examples/smtp/php.md
[View complete runnable example on GitHub ↗](https://github.com/reloop-labs/reloop-examples/tree/main/smtp-php)
## Installation
Install PHPMailer via Composer:
```bash
composer require phpmailer/phpmailer
```
## Code Example
```php
isSMTP();
$mail->Host = 'smtp.reloop.sh';
$mail->SMTPAuth = true;
$mail->Username = 'reloop';
$mail->Password = getenv('RELOOP_API_KEY');
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// Recipients
$mail->setFrom('onboarding@reloop.sh', 'Onboarding');
$mail->addAddress('delivered@resend.dev');
// Content
$mail->isHTML(true);
$mail->Subject = 'Hello from PHPMailer';
$mail->Body = 'Congrats on sending your first email via Reloop SMTP using PHPMailer!
';
$mail->send();
echo 'Message has been sent';
} catch (Exception $e) {
echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}
```
---
# examples/smtp/python.mdx
Source: https://reloop.sh/docs/examples/smtp/python
Markdown: https://reloop.sh/docs/examples/smtp/python.md
[View complete runnable example on GitHub ↗](https://github.com/reloop-labs/reloop-examples/tree/main/smtp-python)
## Code Example
```python
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
api_key = os.environ.get("RELOOP_API_KEY")
# Create message
msg = MIMEMultipart()
msg['From'] = 'onboarding@reloop.sh'
msg['To'] = 'delivered@resend.dev'
msg['Subject'] = 'Hello from Python smtplib'
body = 'Congrats on sending your first email via Reloop SMTP using smtplib!
'
msg.attach(MIMEText(body, 'html'))
try:
# Connect to the server
server = smtplib.SMTP('smtp.reloop.sh', 587)
server.starttls() # Upgrade connection to secure
server.login(api_key, api_key)
# Send email
server.sendmail(msg['From'], msg['To'], msg.as_string())
server.quit()
print("Email sent successfully!")
except Exception as e:
print(f"Error: {e}")
```
---
# examples/smtp/ruby.mdx
Source: https://reloop.sh/docs/examples/smtp/ruby
Markdown: https://reloop.sh/docs/examples/smtp/ruby.md
[View complete runnable example on GitHub ↗](https://github.com/reloop-labs/reloop-examples/tree/main/smtp-ruby)
## Code Example
```ruby
require 'net/smtp'
require 'openssl'
api_key = ENV['RELOOP_API_KEY']
message = <Congrats on sending your first email via Reloop SMTP using net/smtp!
MESSAGE_END
begin
Net::SMTP.start('smtp.reloop.sh', 587, 'localhost', api_key, api_key, :plain) do |smtp|
smtp.send_message message, 'onboarding@reloop.sh', 'delivered@resend.dev'
end
puts "Email sent successfully!"
rescue => e
puts "Error: #{e.message}"
end
```
---
# examples/smtp/rust.mdx
Source: https://reloop.sh/docs/examples/smtp/rust
Markdown: https://reloop.sh/docs/examples/smtp/rust.md
[View complete runnable example on GitHub ↗](https://github.com/reloop-labs/reloop-examples/tree/main/smtp-rust)
## Installation
Add `lettre` to your `Cargo.toml`:
```toml
[dependencies]
lettre = "0.11"
```
## Code Example
```rust
use lettre::transport::smtp::authentication::Credentials;
use lettre::{Message, SmtpTransport, Transport};
use std::env;
fn main() -> Result<(), Box> {
let api_key = env::var("RELOOP_API_KEY")?;
let email = Message::builder()
.from("onboarding@reloop.sh".parse()?)
.to("delivered@resend.dev".parse()?)
.subject("Hello from Lettre")
.header(lettre::message::header::ContentType::TEXT_HTML)
.body(String::from("Congrats on sending your first email via Reloop SMTP using Lettre!
"))?;
let creds = Credentials::new("reloop".to_string(), api_key);
// Open a local connection on port 587
let mailer = SmtpTransport::starttls_relay("smtp.reloop.sh")?
.credentials(creds)
.build();
// Send the email
match mailer.send(&email) {
Ok(_) => println!("Email sent successfully!"),
Err(e) => panic!("Could not send email: {:?}", e),
}
Ok(())
}
```
---
# guides/account-quotas-limits.mdx
Source: https://reloop.sh/docs/guides/account-quotas-limits
Markdown: https://reloop.sh/docs/guides/account-quotas-limits.md
Understanding Reloop sending quotas and rate limits.
## Overview
This guide helps you understand and configure this sending feature in Reloop.
## Details
Understanding your sending configuration is key to successful email delivery. Review the [Reloop dashboard](https://reloop.sh/dashboard) for your current settings and limits.
## Configuration
Check your current plan and usage in the [Reloop dashboard](https://reloop.sh/dashboard/settings).
Adjust your sending configuration based on your requirements and plan limits.
Keep track of your sending volume and adjust as needed to stay within limits.
## Learn More
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
- [Sending Emails](/docs/learn/emails)
- [Email Best Practices](/docs/integrations/agent-skills/email-best-practices)
---
# guides/apple-branded-mail.mdx
Source: https://reloop.sh/docs/guides/apple-branded-mail
Markdown: https://reloop.sh/docs/guides/apple-branded-mail.md
Apple Mail supports Brand Indicators for Message Identification (BIMI), which lets you display your brand logo next to your emails in the recipient's inbox.
## Prerequisites
- A verified domain with DMARC set to `quarantine` or `reject`
- A square SVG logo in Tiny PS format
- A VMC (Verified Mark Certificate) from a trusted authority
## Setup
Your DMARC policy must be set to at least `p=quarantine`:
```
v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com
```
Create a square SVG logo that meets BIMI requirements. The logo must be in Tiny PS format.
Add a TXT record at `default._bimi.yourdomain.com`:
```
v=BIMI1; l=https://example.com/logo.svg; a=https://example.com/vmc.pem
```
BIMI adoption is growing but not yet universal. Apple Mail, Gmail, and Yahoo support it. Outlook does not yet.
---
# guides/apple-private-relay.mdx
Source: https://reloop.sh/docs/guides/apple-private-relay
Markdown: https://reloop.sh/docs/guides/apple-private-relay.md
How to send emails to Apple Private Relay addresses.
## Overview
Understanding this topic is important for maintaining good email deliverability with Reloop.
## Key Points
- Follow email sending best practices to maintain a strong sender reputation
- Monitor your delivery rates in the [Reloop dashboard](https://reloop.sh/dashboard)
- Keep your email lists clean and up-to-date
## Best Practices
Regularly check your delivery, bounce, and complaint rates in the Reloop dashboard.
Ensure SPF, DKIM, and DMARC are properly configured for your domain.
Always include an unsubscribe mechanism in marketing emails and honor opt-out requests promptly.
For detailed guidance on email deliverability, see our [Email Best Practices](/docs/integrations/agent-skills/email-best-practices) skill documentation.
## Learn More
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Warm-up Guide](/docs/guides/warmup-guide)
- [Audience Hygiene](/docs/guides/audience-hygiene)
- [Domains Guide](/docs/learn/domain)
---
# guides/audience-hygiene.mdx
Source: https://reloop.sh/docs/guides/audience-hygiene
Markdown: https://reloop.sh/docs/guides/audience-hygiene.md
Best practices for maintaining a clean email list.
## Overview
Understanding this topic is important for maintaining good email deliverability with Reloop.
## Key Points
- Follow email sending best practices to maintain a strong sender reputation
- Monitor your delivery rates in the [Reloop dashboard](https://reloop.sh/dashboard)
- Keep your email lists clean and up-to-date
## Best Practices
Regularly check your delivery, bounce, and complaint rates in the Reloop dashboard.
Ensure SPF, DKIM, and DMARC are properly configured for your domain.
Always include an unsubscribe mechanism in marketing emails and honor opt-out requests promptly.
For detailed guidance on email deliverability, see our [Email Best Practices](/docs/integrations/agent-skills/email-best-practices) skill documentation.
## Learn More
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Warm-up Guide](/docs/guides/warmup-guide)
- [Audience Hygiene](/docs/guides/audience-hygiene)
- [Domains Guide](/docs/learn/domain)
---
# guides/avoid-gmail-spam.mdx
Source: https://reloop.sh/docs/guides/avoid-gmail-spam
Markdown: https://reloop.sh/docs/guides/avoid-gmail-spam.md
Tips to prevent your emails from landing in Gmail spam.
## Overview
This article covers common causes and solutions for this issue when using Reloop.
## Common Causes
- Misconfigured DNS records
- API key permissions not matching the domain
- Rate limiting or account restrictions
## Resolution Steps
Review your domain settings in the [Reloop dashboard](https://reloop.sh/dashboard/domain) and ensure all DNS records are correctly configured.
Ensure your API key has the correct permissions for the operation you're performing. Go to [API Keys](https://reloop.sh/dashboard/api-keys) to review.
If the issue persists, reach out to [Reloop support](https://reloop.sh/help) with your error details and domain information.
## Learn More
- [Domain Not Verifying](/docs/guides/domain-not-verifying)
- [Avoid MX Conflicts](/docs/guides/avoid-mx-conflicts)
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
---
# guides/avoid-mx-conflicts.mdx
Source: https://reloop.sh/docs/guides/avoid-mx-conflicts
Markdown: https://reloop.sh/docs/guides/avoid-mx-conflicts.md
This guide moved to **[MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts)** under Connect a domain.
Also see [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving) and [Root vs subdomain](/docs/guides/connect-domain/what-is-a-domain).
---
# guides/avoid-outlook-spam.mdx
Source: https://reloop.sh/docs/guides/avoid-outlook-spam
Markdown: https://reloop.sh/docs/guides/avoid-outlook-spam.md
Tips to prevent your emails from landing in Outlook spam.
## Overview
This article covers common causes and solutions for this issue when using Reloop.
## Common Causes
- Misconfigured DNS records
- API key permissions not matching the domain
- Rate limiting or account restrictions
## Resolution Steps
Review your domain settings in the [Reloop dashboard](https://reloop.sh/dashboard/domain) and ensure all DNS records are correctly configured.
Ensure your API key has the correct permissions for the operation you're performing. Go to [API Keys](https://reloop.sh/dashboard/api-keys) to review.
If the issue persists, reach out to [Reloop support](https://reloop.sh/help) with your error details and domain information.
## Learn More
- [Domain Not Verifying](/docs/guides/domain-not-verifying)
- [Avoid MX Conflicts](/docs/guides/avoid-mx-conflicts)
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
---
# guides/change-email-address.mdx
Source: https://reloop.sh/docs/guides/change-email-address
Markdown: https://reloop.sh/docs/guides/change-email-address.md
How to change the email address associated with your Reloop account.
## Overview
Manage your Reloop account settings effectively to ensure smooth operation.
## Steps
Navigate to the [Account Settings](https://reloop.sh/dashboard/settings) page in your Reloop dashboard.
Follow the on-screen instructions to update your account configuration.
Review and confirm your changes. Some changes may require email verification.
If you need assistance with account changes, contact [Reloop support](https://reloop.sh/help).
## Learn More
- [Handling API Keys](/docs/guides/handling-api-keys)
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
---
# guides/configuring-tls.mdx
Source: https://reloop.sh/docs/guides/configuring-tls
Markdown: https://reloop.sh/docs/guides/configuring-tls.md
The difference between opportunistic and enforced TLS.
## Overview
Manage your Reloop account settings effectively to ensure smooth operation.
## Steps
Navigate to the [Account Settings](https://reloop.sh/dashboard/settings) page in your Reloop dashboard.
Follow the on-screen instructions to update your account configuration.
Review and confirm your changes. Some changes may require email verification.
If you need assistance with account changes, contact [Reloop support](https://reloop.sh/help).
## Learn More
- [Handling API Keys](/docs/guides/handling-api-keys)
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
---
# guides/connect-domain/add-domain.mdx
Source: https://reloop.sh/docs/guides/connect-domain/add-domain
Markdown: https://reloop.sh/docs/guides/connect-domain/add-domain.md
This is the main path from “I have a domain” to “I can send as `@mydomain.com`.”
## Before you start
- You can log in to the place that manages **nameservers** for the domain ([find your DNS provider](/docs/guides/connect-domain/find-dns-provider)).
- Decide [root vs subdomain](/docs/guides/connect-domain/what-is-a-domain). Reloop supports either, or both.
- Decide if you need [receiving](/docs/guides/connect-domain/sending-vs-receiving) or only sending. New domains start with Email Receiving on. Turn it off before publishing DNS if this hostname must keep Google Workspace / Microsoft 365.
## Steps

Go to [Domains](https://reloop.sh/dashboard/domain) in the Reloop dashboard (or finish the onboarding domain step). Click **Add domain**.
Enter the hostname you want to send from, for example:
- `acme.com` (root)
- `mail.acme.com` or `notifications.acme.com` (subdomain)
Confirm organization and create the domain. Reloop generates DNS records and DKIM keys. Email Sending, Email Receiving, and click/open tracking start on by default.
On the domain page, Reloop shows a provider when nameservers are recognized.
- **Auto-populate available** → continue with [Auto-populate](/docs/guides/connect-domain/auto-populate)
- **Provider known, manual only** → [Manual setup](/docs/guides/connect-domain/manual-setup) + that host’s [guide](/docs/guides/connect-domain/providers)
- **Unknown** → [Unknown provider](/docs/guides/connect-domain/providers/unknown-provider)
Either complete the Domain Connect consent flow, or copy every required record into your DNS panel. Do not skip DKIM, SPF, or DMARC when Email Sending is on.
Click **Verify & finish** on the setup page, or **Verify Domain** on the domain detail page, to start checks. Reloop may retry that verification job; it does not verify on its own while status stays pending. Status moves through [pending → verifying → active](/docs/guides/connect-domain/verification/statuses) when public DNS matches.
Use [Test your domain](/docs/guides/connect-domain/after/test-domain) to confirm SPF/DKIM/DMARC in a real inbox.
## Domain already in use
If another Reloop organization already verified the domain, see [Domain already registered](/docs/guides/connect-domain/troubleshoot/domain-already-registered).
---
# guides/connect-domain/after/change-dns.mdx
Source: https://reloop.sh/docs/guides/connect-domain/after/change-dns
Markdown: https://reloop.sh/docs/guides/connect-domain/after/change-dns.md
## Safe migration checklist
From the old DNS host (or Reloop dashboard), note SPF, DKIM, DMARC, MX, and tracking values.
Add the domain/zone at Cloudflare, Route 53, etc., and recreate **all** important records (website + mail), including Reloop’s.
A day before, set TTL to 300s on old NS-related records if your registrar allows.
Point NS to the new host. Keep the old zone until dig shows the new NS everywhere.
After NS propagate, open the domain → **Verify**. Auto populate may appear if the new host supports Reloop’s Domain Connect template.
## Avoid
- Deleting Reloop records on the old host **before** the new host is live
- Switching NS without recreating MX if you still need inbound mail
## Related
- [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers)
- [Find your DNS provider](/docs/guides/connect-domain/find-dns-provider)
---
# guides/connect-domain/after/index.mdx
Source: https://reloop.sh/docs/guides/connect-domain/after
Markdown: https://reloop.sh/docs/guides/connect-domain/after.md
Once status is **active**:
- [Test your domain](/docs/guides/connect-domain/after/test-domain)
- [Change DNS later](/docs/guides/connect-domain/after/change-dns)
- [Remove a domain](/docs/guides/connect-domain/after/remove-domain)
For inbox placement and reputation, continue with deliverability guides under [Guides](/docs/guides) (warmup, spam folders, suppression list).
---
# guides/connect-domain/after/remove-domain.mdx
Source: https://reloop.sh/docs/guides/connect-domain/after/remove-domain
Markdown: https://reloop.sh/docs/guides/connect-domain/after/remove-domain.md
## In Reloop
From [Domains](https://reloop.sh/dashboard/domain), open the domain and use the delete/remove action. After removal:
- You can no longer send with that domain’s identities in Reloop
- API/SMTP From addresses on that domain will fail authentication checks
## DNS cleanup (optional but recommended)
At your DNS host, remove Reloop-specific rows you no longer need:
- `reloop._domainkey` TXT
- Reloop SPF include (or whole SPF if unused)
- Reloop DMARC if you added it only for Reloop
- Reloop MX / tracking CNAME
Do **not** remove Google/Microsoft records if you still use those services.
## Re-adding later
You can add the same domain again later and re-verify, subject to [already registered](/docs/guides/connect-domain/troubleshoot/domain-already-registered) rules if another org holds it.
---
# guides/connect-domain/after/test-domain.mdx
Source: https://reloop.sh/docs/guides/connect-domain/after/test-domain
Markdown: https://reloop.sh/docs/guides/connect-domain/after/test-domain.md
## In Reloop
1. Confirm domain status is **active**.
2. Send a test message using the dashboard send flow or your API/SMTP integration with a `From` on the verified domain.
3. Open the message in Gmail/Outlook and check authentication results.
## What “good” looks like
In Gmail → Show original (or equivalent):
- `SPF: PASS`
- `DKIM: PASS` (selector often `reloop`)
- `DMARC: PASS` (when aligned)
## If authentication fails but Reloop says active
- Wait for propagation on the receiver’s resolvers
- Re-check public DNS with `dig`
- Confirm you did not remove records after verify
- See [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) if Reloop flipped back to failed
## Related
- [Warm-up guide](/docs/guides/warmup-guide)
- [Emails going to spam](/docs/guides/emails-going-to-spam)
---
# guides/connect-domain/auto-populate.mdx
Source: https://reloop.sh/docs/guides/connect-domain/auto-populate
Markdown: https://reloop.sh/docs/guides/connect-domain/auto-populate.md
**Auto-populate** uses the open [Domain Connect](https://www.domainconnect.org/) standard. Reloop opens your DNS provider, you approve a template, and the provider writes SPF, DKIM, DMARC, MX, and tracking records for you.
Auto-populate only works on Reloop Cloud. The Domain Connect template is registered to `reloop.sh` and its records point at Reloop Cloud, so a [self-hosted](/docs/self-host) instance can't use it. Add the records by hand with [manual setup](/docs/guides/connect-domain/manual-setup).
## When you see the button
Reloop shows **Auto populate** when nameserver detection matches a host where Reloop’s Domain Connect template is known to work today:
- [Cloudflare](/docs/guides/connect-domain/providers/cloudflare)
- [Vercel](/docs/guides/connect-domain/providers/vercel)
- [Domain Chief](/docs/guides/connect-domain/providers/domain-chief)
- [NameSilo](/docs/guides/connect-domain/providers/namesilo)
On the domain page (and during onboarding DNS step):
- **Auto populate** → use this flow
- **Open DNS settings** (or no Auto populate) → use [manual setup](/docs/guides/connect-domain/manual-setup)
When you click **Auto populate**, Reloop also runs live Domain Connect discovery as a safety check (`_domainconnect` DNS + provider API) to confirm the Reloop template (`reloop.sh` / `email-setup`) is still available. A host that supports Domain Connect in general may still fail that check if Reloop’s template is not onboarded there.
Trust the dashboard for your domain: if the button is missing, use manual setup even if the provider advertises Domain Connect elsewhere.
## Defaults before you approve
New domains start with **Email Receiving** and **click/open tracking** on. Auto-populate therefore typically includes inbound MX (`inbound.reloop.sh`) and the tracking CNAME unless you turn those off first.
If this hostname already uses Google Workspace, Microsoft 365, or another inbox, turn **Email Receiving** off on the Reloop domain page before Auto-populate, or use a [separate subdomain](/docs/guides/connect-domain/what-is-a-domain) for Reloop. See [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## How the flow works
Reloop runs Domain Connect discovery and builds a signed apply URL when the Reloop template is available.
A new tab opens on your DNS host. Sign in if needed, review the records, and confirm.
After success you are redirected back (or you can return manually). Click **Verify & finish** on the setup page, or **Verify Domain** on the domain detail page, if status is still pending.
If discovery fails or the provider dropped the template, you get an error toast and should continue manually.
## What gets applied
The Reloop Domain Connect template typically applies groups based on your domain toggles:
- DKIM TXT on `reloop._domainkey`
- SPF via SPFM / merge (`include:reloop.sh`)
- DMARC on `_dmarc` (required for verification when Email Sending is on)
- **Receiving MX** → `inbound.reloop.sh` when Email Receiving is on (default for new domains)
- Tracking CNAME → `link.reloop.sh` when tracking is on (host is usually `link` on apex, or `link.` when the Reloop domain is a subdomain)
Exact groups depend on your toggles and template version; the consent screen lists what will change. Always confirm hosts and values in the Reloop dashboard.
## If Auto-populate fails
See [Auto-populate failed](/docs/guides/connect-domain/troubleshoot/auto-populate-failed) for:
- Popup blocked
- User cancelled consent
- Template not found at provider
- Domain Connect not supported on that zone
You can always fall back to [manual setup](/docs/guides/connect-domain/manual-setup) using the same dashboard records.
## Security notes
- Only approve the flow while logged into **your** DNS account for that domain.
- Reloop signs Domain Connect apply URLs; do not reuse apply links from untrusted sources.
- After you approve, check the records in your DNS panel, then click **Verify Domain** (or **Verify & finish**) in Reloop.
---
# guides/connect-domain/dns-records-explained.mdx
Source: https://reloop.sh/docs/guides/connect-domain/dns-records-explained
Markdown: https://reloop.sh/docs/guides/connect-domain/dns-records-explained.md
When you add a domain, Reloop generates the exact records to publish. Always prefer the dashboard values over examples here. Your **DKIM key is unique**.
## Quick map
| Purpose | Typical type | Example host | What it does |
| --- | --- | --- | --- |
| **SPF** | TXT | `@` | Authorizes Reloop to send for your domain |
| **DKIM** | TXT | `reloop._domainkey` | Cryptographic signature so receivers trust the message |
| **DMARC** | TXT | `_dmarc` | Policy if SPF/DKIM fail; required for verification when Email Sending is on |
| **Receiving MX** | MX | `@` or subdomain host | Routes **inbound** mail to Reloop (`inbound.reloop.sh`) when Email Receiving is on (default for new domains) |
| **Tracking** | CNAME | `link` or `link.` | Click/open tracking → `link.reloop.sh` (on by default for new domains) |
Hosts and whether MX/tracking appear depend on your domain settings (apex vs subdomain, sending/receiving/tracking toggles). Always copy the host from the Reloop dashboard.
## SPF (Sender Policy Framework)
**Type:** TXT
**Example value:**
```
v=spf1 include:reloop.sh -all
```
SPF lists which servers may send mail as your domain. Reloop’s include is `include:reloop.sh` (hosted product).
You may only have **one SPF TXT** on a given host. If you already have SPF (Google, Microsoft, Mailchimp, …), **merge** Reloop’s `include:reloop.sh` into that record. Do not add a second SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## DKIM (DomainKeys Identified Mail)
**Type:** TXT
**Host:** usually `reloop._domainkey` (selector `reloop`)
**Value:** looks like:
```
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
```
The long `p=` string is your public key. Reloop signs outbound mail with the matching private key. If the TXT is truncated, quoted wrongly, or split incorrectly, verification fails. See [DKIM / DMARC typos](/docs/guides/connect-domain/troubleshoot/dkim-dmarc-typos).
## DMARC
**Type:** TXT
**Host:** `_dmarc`
**Example value:**
```
v=DMARC1; p=reject;
```
DMARC tells receivers what to do when authentication fails. Reloop’s default template uses `p=reject`. When **Email Sending** is on, a matching DMARC record is required for Reloop verification. If you already have DMARC for Google/Microsoft, you can often keep your existing policy. Reloop checks that a valid DMARC record is present; review carefully before overwriting a production policy.
## MX (mail exchange)
MX is **not** required to send mail. Reloop only shows MX when **Email Receiving** is on:
- **Receiving MX** → typically `inbound.reloop.sh`. Used when Email Receiving is on (default for new domains). On the same hostname as Google Workspace / Microsoft 365, this can divert or split inbound delivery. Turn Receiving off for send-only, or use a [separate hostname](/docs/guides/connect-domain/what-is-a-domain). See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts).
## Tracking CNAME
**Type:** CNAME
**Host:** `link` on an apex domain; for a Reloop subdomain such as `mail.example.com`, the host is often `link.mail` (always copy from the dashboard)
**Value:** `link.reloop.sh`
Used for branded click/open tracking links. Click and open tracking are **on by default** for new domains; disable tracking in Reloop if you do not want this CNAME.
Conflicts if that host already points elsewhere, or if your provider disallows CNAMEs in that spot. See [Tracking CNAME issues](/docs/guides/connect-domain/troubleshoot/tracking-cname).
## How to read a Reloop dashboard row
For each row, match three things in your DNS UI:
1. **Type** (TXT / MX / CNAME)
2. **Name / Host**: some UIs want `@` for the root; others want a blank host or the bare domain
3. **Value**: paste exactly; for MX also set **Priority**
Provider-specific quirks (Cloudflare proxy, GoDaddy host field, Route 53 hosted zone) are in each [provider guide](/docs/guides/connect-domain/providers).
## Related
- [Manual setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate](/docs/guides/connect-domain/auto-populate)
- [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving)
---
# guides/connect-domain/find-dns-provider.mdx
Source: https://reloop.sh/docs/guides/connect-domain/find-dns-provider
Markdown: https://reloop.sh/docs/guides/connect-domain/find-dns-provider.md
You need to know **who hosts your nameservers** before you can publish Reloop records (or before Auto-populate can work).
## Method 1: Reloop dashboard (easiest)
1. Add the domain at [Domains](https://reloop.sh/dashboard/domain) if you have not already.
2. Open the domain detail page.
3. Reloop reads public nameservers and shows a **Provider** chip when it recognizes the host (Cloudflare, GoDaddy, Vercel, Namecheap, …).
If Reloop shows a provider:
- **Auto populate** on the domain page → use [Auto-populate](/docs/guides/connect-domain/auto-populate)
- **Otherwise** → open that host’s guide under [Provider guides](/docs/guides/connect-domain/providers) and add records manually
If Reloop cannot detect a provider, use Method 2 or 3.
## Method 2: Registrar control panel
1. Log in where you **bought / renew** the domain.
2. Open the domain → **Nameservers** / **DNS** / **Manage DNS**.
3. Note the nameserver hostnames, for example:
| Nameserver contains | Likely DNS host |
| --- | --- |
| `cloudflare.com` | Cloudflare |
| `vercel-dns.com` | Vercel |
| `domaincontrol.com` | GoDaddy |
| `registrar-servers.com` | Namecheap |
| `awsdns-` | AWS Route 53 |
| `domainchief.` | Domain Chief |
| `dnsowl.com` / `hostsilo.com` | NameSilo |
| `ui-dns.` | IONOS |
| `squarespace.com` / `googledomains.com` | Squarespace |
| `hetzner.com` / `hetzner.de` | Hetzner |
| `hostinger.` / `dns-parking.com` | Hostinger |
| `gandi.net` | Gandi |
| `porkbun.com` | Porkbun |
| `strato.de` / `strato-hosting.eu` | Strato |
| `dreamhost.com` / `dreamhosters.com` | DreamHost |
| `as207960.net` / `glauca.digital` | Glauca Digital |
| `wordpress.com` / `wp.com` | WordPress.com |
| `plesk` | Plesk (or your host’s Plesk panel) |
Then open the matching [provider guide](/docs/guides/connect-domain/providers).
## Method 3: Public lookup
### In the browser
Use any “DNS lookup” / “whois” tool and query **NS** records for your domain (e.g. `acme.com`).
### On your computer
```bash
dig NS acme.com +short
# or
nslookup -type=NS acme.com
```
Example output:
```
ada.ns.cloudflare.com.
bob.ns.cloudflare.com.
```
→ DNS is on **Cloudflare**.
## Bought at A, DNS at B
If the registrar is GoDaddy but NS are Cloudflare, **ignore GoDaddy’s DNS record editor** for Reloop. Edit Cloudflare. Full explanation: [Registrar vs DNS host](/docs/guides/connect-domain/registrar-vs-dns-host).
## Still stuck?
Use the [Unknown provider](/docs/guides/connect-domain/providers/unknown-provider) guide: open whatever panel controls the NS hosts you found, and paste the records from Reloop.
---
# guides/connect-domain/index.mdx
Source: https://reloop.sh/docs/guides/connect-domain
Markdown: https://reloop.sh/docs/guides/connect-domain.md
Reloop can send and receive email for **any domain you control** (receiving and tracking are on by default for new domains; turn receiving off if you keep another inbox on that hostname). You do not need to buy the domain from Reloop. Register it anywhere, point DNS at the records Reloop shows you, and verify.
The [Domains](https://reloop.sh/dashboard/domain) page lists every sending domain in your organization, with status and shortcuts to DNS guides.

## What you will do
Open [Domains](https://reloop.sh/dashboard/domain) and click **Add domain**. Enter `example.com` or a subdomain like `mail.example.com`.
Reloop detects your nameservers when possible. If you are unsure who hosts DNS, see [Find your DNS provider](/docs/guides/connect-domain/find-dns-provider).
On the domain page, follow the banner Reloop shows for your DNS host (see below).
Click **Verify & finish** (setup) or **Verify Domain** (domain detail) to start checks. When status is **active**, you can send from that domain.
## Configuring DNS from the banner
After you add a domain, Reloop shows a banner based on the nameservers it detected.
- If the banner names your DNS host and offers **Auto populate**, click it. Approve the consent screen at your provider and Reloop’s records are written for you. More detail: [Auto populate](/docs/guides/connect-domain/auto-populate).
- If the banner does not offer Auto populate (or you prefer not to use it), copy the Type / Host / Value rows from the dashboard into your DNS panel. More detail: [Manual setup](/docs/guides/connect-domain/manual-setup) and the [DNS guides](/docs/guides/connect-domain/providers).
Either way, Reloop checks public DNS afterward and marks the domain verified when the records match.
## New to DNS?
Start here. You do not need to be a network engineer:
1. [What is a domain?](/docs/guides/connect-domain/what-is-a-domain)
2. [What is DNS?](/docs/guides/connect-domain/what-is-dns)
3. [Registrar vs DNS host](/docs/guides/connect-domain/registrar-vs-dns-host)
4. [Find your DNS provider](/docs/guides/connect-domain/find-dns-provider)
5. [DNS records Reloop needs](/docs/guides/connect-domain/dns-records-explained)
## Full map
- **Setup**: [Add a domain](/docs/guides/connect-domain/add-domain) · [What is a domain?](/docs/guides/connect-domain/what-is-a-domain) · [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving)
- **DNS guides**: [All DNS hosts](/docs/guides/connect-domain/providers)
- **Verification**: [Statuses](/docs/guides/connect-domain/verification/statuses) · [Propagation](/docs/guides/connect-domain/verification/propagation)
- **Troubleshoot**: [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and more
- **After connect**: [Test](/docs/guides/connect-domain/after/test-domain) · [Change DNS](/docs/guides/connect-domain/after/change-dns) · [Remove](/docs/guides/connect-domain/after/remove-domain)
Always copy values from your domain’s page in the Reloop dashboard. Examples in docs use placeholders; your DKIM key and hosts are unique to your domain.
---
# guides/connect-domain/manual-setup.mdx
Source: https://reloop.sh/docs/guides/connect-domain/manual-setup
Markdown: https://reloop.sh/docs/guides/connect-domain/manual-setup.md
## Prerequisites
- Domain added in [Reloop](https://reloop.sh/dashboard/domain)
- Access to the DNS panel for the **nameserver** host ([find your provider](/docs/guides/connect-domain/find-dns-provider))
## Steps
Go to [Domains](https://reloop.sh/dashboard/domain) → select the domain. Keep this tab open; you will copy each row.
Use **Open DNS settings** in Reloop if shown, or jump to the matching [provider guide](/docs/guides/connect-domain/providers).
For every required row in Reloop:
1. Choose the same **type** (TXT, MX, CNAME)
2. Set **name/host** as shown (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …)
3. Paste **value** exactly
4. For MX, set **priority** (often `10`); add **Receiving MX** (`inbound.reloop.sh`) only when Email Receiving is on
5. Save
Provider UIs differ:
- Some want blank host instead of `@`
- Some auto-append your domain, so do not enter `reloop._domainkey.acme.com` if the UI already adds `acme.com`
- Cloudflare: keep records **DNS only** (grey cloud) unless you know you need a proxy (almost never for mail TXT/MX)
If an SPF TXT already exists, merge `include:reloop.sh` into it. Do not create a second SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
Return to Reloop → **Verify & finish** (setup) or **Verify Domain** (detail). Allow time for [propagation](/docs/guides/connect-domain/verification/propagation) if checks fail immediately.
## Checklist before you verify
- [ ] Edited the DNS host that matches your **NS** records (not only the registrar invoice)
- [ ] DKIM TXT is complete (long `p=` value not cut off)
- [ ] Only one SPF record on that host
- [ ] DMARC published when Email Sending is on
- [ ] Email Receiving toggled to match intent ([sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving)); default is on for new domains
- [ ] Tracking CNAME host matches the dashboard (`link` or `link.`) and is free or intentionally replaced
## Provider-specific guides
See the full list: [Provider guides](/docs/guides/connect-domain/providers).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying)
---
# guides/connect-domain/providers/cloudflare.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/cloudflare
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/cloudflare.md
## Add domain to Reloop
First, log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
It is [best practice to use a subdomain](/docs/guides/connect-domain/what-is-a-domain) (for example `mail.example.com` or `team.example.com`) instead of the root domain (`example.com`). A subdomain keeps sending reputation separate and is especially important if you use Reloop receiving (on by default for new domains).
## Automatic setup (recommended)
The fastest way to verify your domain on Cloudflare is **Auto populate** in Reloop. This uses [Domain Connect](https://www.domainconnect.org/) to configure your DNS records.
Auto populate is only on Reloop Cloud. On a [self-hosted](/docs/self-host) instance, skip to [Manual setup](#manual-setup).
1. Go to your [Domains](https://reloop.sh/dashboard/domain) page in Reloop and open the domain.
2. Check **Email Receiving** before Auto-populate. New domains start with Receiving on, so inbound MX (`inbound.reloop.sh`) is usually included. Turn **Email Receiving** off first if this hostname must keep Google Workspace / Microsoft 365, or use a [subdomain](/docs/guides/connect-domain/what-is-a-domain). [Learn more below](#receiving-emails).
3. Click **Auto populate**.
4. Authorize Reloop in Cloudflare and approve the records.
5. Return to Reloop and click **Verify & finish** (setup) or **Verify Domain** (detail) to start checks.
Verification often finishes within a few minutes after DNS publishes; allow longer if resolvers are slow ([propagation](/docs/guides/connect-domain/verification/propagation)).
If Auto-populate fails, use **Manual setup** below. See also [Auto-populate DNS](/docs/guides/connect-domain/auto-populate) and [Auto-populate failed](/docs/guides/connect-domain/troubleshoot/auto-populate-failed).
## Manual setup
If you prefer to add DNS records manually, follow these steps. Always copy Type / Name / Value from your domain page in Reloop. The examples below use placeholders.
### Log in to Cloudflare
Log in to your [Cloudflare account](https://dash.cloudflare.com) and open **DNS → Records** for the domain whose nameservers match `*.ns.cloudflare.com`.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Add TXT SPF record
Click **Add record** on Cloudflare:
1. Set the Type to `TXT`.
2. Set the **Name** to the host Reloop shows (e.g. `team` or `@`).
3. Copy the TXT value from Reloop into **Content** (typically `v=spf1 include:reloop.sh -all`).
4. Use `Auto` for **TTL**.
5. Select **Save**.
| Cloudflare | Reloop | Example value |
| --- | --- | --- |
| Type | Type | `TXT` |
| Name | Name | `team` (or `@` for the root) |
| Content | Value | `v=spf1 include:reloop.sh -all` |
| TTL | TTL | `Auto` |
Omit your domain from the Name field when Cloudflare appends it. Paste `team`, not `team.example.com`.
Never create a second SPF TXT on the same host. If SPF already exists, merge `include:reloop.sh` into that record. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
### Add TXT DKIM record
Click **Add record** on Cloudflare:
1. Set the Type to `TXT`.
2. Set the **Name** from Reloop (selector is usually `reloop._domainkey`). For a subdomain zone at the apex, Cloudflare often needs `reloop._domainkey.team`.
3. Copy the full TXT value from Reloop into **Content** (`v=DKIM1; k=rsa; p=…`).
4. Use `Auto` for **TTL**.
5. Select **Save**.
| Cloudflare | Reloop | Example value |
| --- | --- | --- |
| Type | Type | `TXT` |
| Name | Name | `reloop._domainkey` (or `reloop._domainkey.team` under the apex zone) |
| Content | Value | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| TTL | TTL | `Auto` |
Omit your domain from the Name field when Cloudflare appends it. Paste `reloop._domainkey` or `reloop._domainkey.team`, not the full FQDN ending in `.example.com`.
### Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, add the inbound MX in Cloudflare:
1. Set the Type to `MX`.
2. Set the **Name** to the host Reloop shows (e.g. `team` or `@`).
3. Copy the MX value from Reloop into **Mail server** (typically `inbound.reloop.sh`).
4. Use `Auto` for **TTL**.
5. Set **Priority** as shown in Reloop (often `10`).
6. Select **Save**.
| Cloudflare | Reloop | Example value |
| --- | --- | --- |
| Type | Type | `MX` |
| Name | Name | `team` (or `@` for the root) |
| Mail server | Value | `inbound.reloop.sh` |
| Priority | Priority | `10` |
| TTL | TTL | `Auto` |
Publishing Reloop’s inbound MX on a hostname that already has Google/Microsoft MX can divert or split delivery depending on priority. Prefer a [subdomain](/docs/guides/connect-domain/what-is-a-domain), or turn Email Receiving off for send-only. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
### Complete verification
Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop. On the setup page click **Verify & finish**; on the domain detail page click **Verify Domain** (menu: **Re-verify DNS**). It may take a few minutes (rarely up to 48–72 hours). See [propagation](/docs/guides/connect-domain/verification/propagation).
When verification succeeds, Reloop shows **You're all set!** (Domain added → Verified → Ready to send). Your domain is verified and you are good to go.
## Cloudflare-specific notes
- Keep Reloop mail/auth records on **DNS only** (grey cloud). Orange-cloud proxy often breaks verification.
- **DMARC** is required for verification when Email Sending is on (keep an existing policy if you already have one). **Tracking** CNAME is on by default for new domains. The host is usually `link` on apex, or `link.` for subdomains; copy from the dashboard.
## Troubleshooting
If your domain is not successfully verified, try these common fixes.
Confirm Reloop records use **DNS only** (grey cloud), not Proxied (orange cloud).
Re-open the Reloop domain page and compare every Type / Name / Value to Cloudflare. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/domain-chief.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/domain-chief
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/domain-chief.md
Domain Chief can use Reloop **Auto-populate** when nameservers match. Manual steps are always available as a fallback.
First, log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
It is [best practice to use a subdomain](/docs/guides/connect-domain/what-is-a-domain) (for example `mail.example.com` or `team.example.com`) instead of the root domain (`example.com`). A subdomain keeps sending reputation separate and is especially important if you use Reloop receiving (on by default for new domains).
## Automatic setup (recommended)
The fastest way to verify your domain on Domain Chief is **Auto populate** in Reloop. This uses [Domain Connect](https://www.domainconnect.org/); Domain Chief indexes public templates automatically ([Domain Chief Domain Connect](https://aka.chief.app/domainconnect)).
1. Go to your [Domains](https://reloop.sh/dashboard/domain) page in Reloop and open the domain.
2. Check **Email Receiving** before Auto-populate. New domains start with Receiving on, so inbound MX (`inbound.reloop.sh`) is usually included. Turn **Email Receiving** off first if this hostname must keep Google Workspace / Microsoft 365, or use a [subdomain](/docs/guides/connect-domain/what-is-a-domain). [Learn more below](#receiving-emails).
3. Click **Auto populate** (shown when Reloop detects Domain Chief nameservers).
4. Authorize Reloop in Domain Chief and approve the records.
5. Return to Reloop and click **Verify & finish** (setup) or **Verify Domain** (detail) to start checks.
Verification often finishes within a few minutes after DNS publishes; allow longer if resolvers are slow ([propagation](/docs/guides/connect-domain/verification/propagation)).
If Auto-populate fails or Reloop does not offer it, use **Manual setup** below. See also [Auto-populate DNS](/docs/guides/connect-domain/auto-populate) and [Auto-populate failed](/docs/guides/connect-domain/troubleshoot/auto-populate-failed).
## Manual setup
If you prefer to add DNS records manually, follow these steps. Always copy Type / Name / Value from your domain page in Reloop. The examples below use placeholders.
### Log in to Domain Chief
Log in to [Domain Chief](https://domain.chief.app) and open DNS for the domain whose nameservers match `ns.domainchief.app` / `.eu` / `.nl` / `.net`.
Domain Chief → select domain → DNS.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Add TXT SPF record
Add a record in Domain Chief:
1. Set the Type to `TXT`.
2. Set the **Name** to the host Reloop shows (e.g. `team` or `@`).
3. Copy the TXT value from Reloop (typically `v=spf1 include:reloop.sh -all`).
4. Use `Auto` or the provider default for **TTL**.
5. Save.
| Domain Chief | Reloop | Example value |
| --- | --- | --- |
| Type | Type | `TXT` |
| Name / Host | Name | `team` (or `@` for the root) |
| Value / Content | Value | `v=spf1 include:reloop.sh -all` |
| TTL | TTL | `Auto` |
Omit your domain from the Name field when Domain Chief appends it. Paste `team`, not `team.example.com`.
Never create a second SPF TXT on the same host. If SPF already exists, merge `include:reloop.sh` into that record. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
### Add TXT DKIM record
Add a record in Domain Chief:
1. Set the Type to `TXT`.
2. Set the **Name** from Reloop (selector is usually `reloop._domainkey`). Under an apex zone this may be `reloop._domainkey.team`.
3. Copy the full TXT value from Reloop into the value field (`v=DKIM1; k=rsa; p=…`).
4. Use `Auto` or the provider default for **TTL**.
5. Save.
| Domain Chief | Reloop | Example value |
| --- | --- | --- |
| Type | Type | `TXT` |
| Name / Host | Name | `reloop._domainkey` (or `reloop._domainkey.team` under the apex zone) |
| Value / Content | Value | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| TTL | TTL | `Auto` |
Omit your domain from the Name field when Domain Chief appends it. Paste `reloop._domainkey` or `reloop._domainkey.team`, not the full FQDN ending in `.example.com`.
### Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, add the inbound MX in Domain Chief:
1. Set the Type to `MX`.
2. Set the **Name** to the host Reloop shows (e.g. `team` or `@`).
3. Copy the MX value from Reloop (typically `inbound.reloop.sh`).
4. Use `Auto` or the provider default for **TTL**.
5. Set **Priority** as shown in Reloop (often `10`).
6. Save.
| Domain Chief | Reloop | Example value |
| --- | --- | --- |
| Type | Type | `MX` |
| Name / Host | Name | `team` (or `@` for the root) |
| Value / Mail server | Value | `inbound.reloop.sh` |
| Priority | Priority | `10` |
| TTL | TTL | `Auto` |
Publishing Reloop’s inbound MX on a hostname that already has Google/Microsoft MX can divert or split delivery depending on priority. Prefer a [subdomain](/docs/guides/connect-domain/what-is-a-domain), or turn Email Receiving off for send-only. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
### Complete verification
Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop. On the setup page click **Verify & finish**; on the domain detail page click **Verify Domain** (menu: **Re-verify DNS**). It may take a few minutes (rarely up to 48–72 hours). See [propagation](/docs/guides/connect-domain/verification/propagation).
When verification succeeds, Reloop shows **You're all set!** (Domain added → Verified → Ready to send). Your domain is verified and you are good to go.
## Domain Chief-specific notes
- Domain Chief auto-indexes public Domain Connect templates, so prefer **Auto populate** when Reloop offers it.
- Auto-populate only works when public nameservers point at Domain Chief.
- **DMARC** is required for verification when Email Sending is on (keep an existing policy if you already have one). **Tracking** CNAME is on by default for new domains. The host is usually `link` on apex, or `link.` for subdomains; copy from the dashboard.
## Troubleshooting
If your domain is not successfully verified, try these common fixes.
Confirm nameservers are Domain Chief (`dig NS yourdomain.com`). Auto-populate needs Domain Chief hosted DNS. Otherwise use manual setup above.
Re-open the Reloop domain page and compare every Type / Name / Value to Domain Chief. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/dreamhost.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/dreamhost
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/dreamhost.md
Add Reloop’s DNS records in DreamHost using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to DreamHost
Open [DreamHost](https://panel.dreamhost.com) and go to DNS for the domain whose **nameservers** match `dreamhost.com / dreamhosters.com`.
DreamHost Panel → Domains → DNS.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | DreamHost field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## DreamHost-specific notes
- DreamHost uses `@` or blank for the root host depending on the screen.
- Confirm you are editing the zone that matches public nameservers.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to DreamHost. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/gandi.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/gandi
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/gandi.md
Add Reloop’s DNS records in Gandi using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to Gandi
Open [Gandi](https://admin.gandi.net/domain) and go to DNS for the domain whose **nameservers** match `gandi.net`.
Gandi Admin → Domain → DNS Records.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | Gandi field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Gandi-specific notes
- Gandi LiveDNS uses relative hostnames; omit the domain suffix.
- If the domain uses external nameservers, edit that host instead.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to Gandi. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/glauca-digital.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/glauca-digital
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/glauca-digital.md
Add Reloop’s DNS records in Glauca Digital using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to Glauca Digital
Open [Glauca Digital](https://domains.glauca.digital) and go to DNS for the domain whose **nameservers** match `as207960.net / glauca.digital`.
Glauca HexDNS → zone DNS.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | Glauca Digital field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Glauca Digital-specific notes
- Glauca Digital supports Domain Connect ([HexDNS](https://docs.glauca.digital/hexdns/domain-connect/)). Reloop’s template may become available after public registry sync. If Reloop does not show **Auto populate**, use manual setup.
- Default HexDNS nameservers are `ns1`–`ns4.as207960.net`.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to Glauca Digital. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/godaddy.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/godaddy
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/godaddy.md
Add Reloop’s DNS records in GoDaddy using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to GoDaddy
Open [GoDaddy](https://dcc.godaddy.com/dns) and go to DNS for the domain whose **nameservers** match `domaincontrol.com`.
GoDaddy Domain Control Center → DNS.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | GoDaddy field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## GoDaddy-specific notes
- GoDaddy supports Domain Connect for some products, but Reloop’s template is **not** onboarded there yet. Use manual setup.
- If nameservers point to Cloudflare (or another host), edit that host instead of GoDaddy DNS.
- For apex hosts GoDaddy uses `@`.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to GoDaddy. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/hetzner.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/hetzner
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/hetzner.md
Add Reloop’s DNS records in Hetzner using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to Hetzner
Open [Hetzner](https://dns.hetzner.com) and go to DNS for the domain whose **nameservers** match `hetzner.com / hetzner.de`.
Hetzner DNS Console → zone records.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | Hetzner field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Hetzner-specific notes
- Use the Hetzner DNS Console for zones on Hetzner nameservers.
- Merge SPF carefully: Hetzner may already have a default TXT.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to Hetzner. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/hostinger.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/hostinger
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/hostinger.md
Add Reloop’s DNS records in Hostinger using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to Hostinger
Open [Hostinger](https://hpanel.hostinger.com/domains) and go to DNS for the domain whose **nameservers** match `hostinger. / dns-parking.com`.
hPanel → Domains → DNS / Name Servers.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | Hostinger field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Hostinger-specific notes
- Hostinger hPanel is the editor when nameservers point at Hostinger.
- If you pointed NS at Cloudflare (or another host), edit that host instead.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to Hostinger. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/index.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers
Markdown: https://reloop.sh/docs/guides/connect-domain/providers.md
Pick the company that hosts your **nameservers** (not always where you bought the domain). Unsure? [Find your DNS provider](/docs/guides/connect-domain/find-dns-provider).
Each guide covers adding a domain in Reloop, provider login, field mapping, record-by-record setup, receiving mail, verification, and troubleshooting. If Reloop shows **Auto populate** for a host, that guide includes an **Automatic setup** section first. Otherwise, follow the manual steps. See [Auto-populate DNS](/docs/guides/connect-domain/auto-populate).
This set includes common registrars/DNS hosts plus every DNS provider with a live [Domain Connect](https://www.domainconnect.org/dns-providers/) implementation.
## All DNS guides
Includes Auto-populate when Reloop detects this host.
Includes Auto-populate when Reloop detects this host.
Step-by-step manual DNS setup.
Step-by-step manual DNS setup.
Step-by-step manual DNS setup (Auto-populate only if the dashboard shows it).
Step-by-step manual DNS setup.
Step-by-step manual DNS setup.
Step-by-step manual DNS setup.
Step-by-step manual DNS setup.
Step-by-step manual DNS setup.
Includes Auto-populate when Reloop detects this host.
Step-by-step manual DNS setup.
Step-by-step manual DNS setup.
Step-by-step manual DNS setup.
Step-by-step manual DNS setup.
Step-by-step manual DNS setup.
Includes Auto-populate when Reloop detects this host.
Step-by-step manual DNS setup.
## Not listed?
Use [Unknown or other DNS provider](/docs/guides/connect-domain/providers/unknown-provider).
## Related
- [Connect a domain](/docs/guides/connect-domain)
- [Auto-populate](/docs/guides/connect-domain/auto-populate)
- [Manual setup](/docs/guides/connect-domain/manual-setup)
- [Domain Connect DNS providers](https://www.domainconnect.org/dns-providers/)
---
# guides/connect-domain/providers/ionos.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/ionos
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/ionos.md
Add Reloop’s DNS records in IONOS using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to IONOS
Open [IONOS](https://my.ionos.com/domains) and go to DNS for the domain whose **nameservers** match `ui-dns.com / .de / .org / .biz`.
IONOS → Domains → DNS.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | IONOS field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Host Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Points to / Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## IONOS-specific notes
- IONOS Domain Connect has not finished Reloop template onboarding. Use manual setup.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to IONOS. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/namecheap.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/namecheap
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/namecheap.md
Add Reloop’s DNS records in Namecheap using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to Namecheap
Open [Namecheap](https://ap.www.namecheap.com/domains/list) and go to DNS for the domain whose **nameservers** match `registrar-servers.com`.
Domain List → Manage → Advanced DNS.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | Namecheap field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Namecheap-specific notes
- Use Advanced DNS when Namecheap nameservers are active.
- For apex hosts Namecheap often uses `@`.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to Namecheap. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/namesilo.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/namesilo
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/namesilo.md
NameSilo can use Reloop **Auto-populate** when nameservers match. Manual steps are always available as a fallback.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Automatic setup (recommended)
NameSilo supports Reloop **Auto-populate** (Domain Connect). This is usually the fastest path.
Go to [Domains](https://reloop.sh/dashboard/domain) and open the domain you added.
New domains start with **Email Receiving** on, so Auto-populate usually includes inbound MX (`inbound.reloop.sh`). If this hostname must keep Google Workspace / Microsoft 365, turn **Email Receiving** off first, or use a [separate subdomain](/docs/guides/connect-domain/what-is-a-domain). See [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
Reloop opens NameSilo for consent. Sign in if asked, review the records, and approve.
Return to Reloop and click **Verify & finish** or **Verify Domain** to start checks. Status should move to **active** within a few minutes (see [propagation](/docs/guides/connect-domain/verification/propagation)).
If Auto-populate fails or you prefer full control, use **Manual setup** below. Details: [Auto-populate DNS](/docs/guides/connect-domain/auto-populate) and [Auto-populate failed](/docs/guides/connect-domain/troubleshoot/auto-populate-failed).
## Manual setup
### Log in to NameSilo
Open [NameSilo](https://www.namesilo.com/account_domain.php) and go to DNS for the domain whose **nameservers** match `dnsowl.com / hostsilo.com`.
NameSilo → Domain Manager → DNS Manager.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | NameSilo field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## NameSilo-specific notes
- NameSilo syncs Domain Connect templates from the public registry, so prefer Auto-populate when offered.
- Confirm you are editing DNS at NameSilo (not a different host pointed to by custom NS).
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to NameSilo. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/plesk.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/plesk
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/plesk.md
Add Reloop’s DNS records in Plesk using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to Plesk
Open your host’s [Plesk](https://www.plesk.com) panel (URL varies by host) and go to DNS for the domain. Nameservers often belong to the hosting brand, not a shared `plesk.*` hostname.
Plesk → Domains → DNS Settings.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | Plesk field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Plesk-specific notes
- Plesk supports Domain Connect as a DNS provider. Reloop’s template is **not** onboarded for every Plesk installation. Use manual setup unless Reloop shows **Auto populate**.
- Plesk is often behind a hosting brand: open the panel that actually serves your public nameservers.
- Login URL varies by host; Reloop cannot deep-link to a single Plesk URL.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to Plesk. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/porkbun.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/porkbun
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/porkbun.md
Add Reloop’s DNS records in Porkbun using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to Porkbun
Open [Porkbun](https://porkbun.com/account/domains) and go to DNS for the domain whose **nameservers** match `porkbun.com`.
Porkbun → Domain Management → DNS Records.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | Porkbun field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Porkbun-specific notes
- Porkbun uses relative host fields; omit your domain suffix.
- Confirm DNS is managed at Porkbun (not parked elsewhere).
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to Porkbun. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/route53.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/route53
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/route53.md
Add Reloop’s DNS records in AWS Route 53 using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to AWS Route 53
Open [AWS Route 53](https://console.aws.amazon.com/route53) and go to DNS for the domain whose **nameservers** match `awsdns-`.
Route 53 → Hosted zones → your zone → Records.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | AWS Route 53 field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## AWS Route 53-specific notes
- Edit the hosted zone whose NS match the public nameservers (`awsdns-`).
- For MX, set the Priority field separately from the Value.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to AWS Route 53. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/squarespace.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/squarespace
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/squarespace.md
Add Reloop’s DNS records in Squarespace using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to Squarespace
Open [Squarespace](https://account.squarespace.com/domains) and go to DNS for the domain whose **nameservers** match `squarespace.com / sqsp.net / googledomains.com`.
Squarespace → Domains → DNS Settings.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | Squarespace field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Squarespace-specific notes
- Domains migrated from Google Domains often still show `googledomains.com` nameservers. Manage them in Squarespace.
- Use `@` for the root host when Squarespace requires it.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to Squarespace. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/strato.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/strato
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/strato.md
Add Reloop’s DNS records in Strato using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to Strato
Open [Strato](https://www.strato.de/apps/CustomerService) and go to DNS for the domain whose **nameservers** match `strato.de / strato-hosting.eu`.
Strato Kunden-Login → Domains → DNS / Domainverwaltung.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | Strato field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Strato-specific notes
- Strato’s DNS UI labels vary by language (DE/EN).
- Confirm public NS match Strato before editing.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to Strato. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/unknown-provider.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/unknown-provider
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/unknown-provider.md
You can connect **any** domain. If Reloop shows **Auto populate** for your host, use that; otherwise use the same manual path below.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep the DNS records table open for copy/paste.
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain).
## Find the live DNS host
```bash
dig NS yourdomain.com +short
```
Or follow [Find your DNS provider](/docs/guides/connect-domain/find-dns-provider). Edit DNS **only** where those nameservers point.
## Add records
For each Reloop row, create a matching Type / Name / Value (and MX Priority) in that provider’s zone editor.
Omit your domain from the Name/Host field when the UI appends it automatically.
Merge `include:reloop.sh` into an existing SPF TXT. Do not create two SPF records. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Receiving emails
Enable receiving in Reloop only if you want MX for that hostname to point at Reloop (`inbound.reloop.sh`). That can replace another inbox on the same name. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts).
## Complete verification
Return to Reloop → **Verify**. See [propagation](/docs/guides/connect-domain/verification/propagation) if checks lag.
## Troubleshooting
- [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying)
- [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers)
---
# guides/connect-domain/providers/vercel.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/vercel
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/vercel.md
Vercel can use Reloop **Auto-populate** when nameservers match. Manual steps are always available as a fallback.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Automatic setup (recommended)
Vercel supports Reloop **Auto-populate** (Domain Connect). This is usually the fastest path.
Go to [Domains](https://reloop.sh/dashboard/domain) and open the domain you added.
New domains start with **Email Receiving** on, so Auto-populate usually includes inbound MX (`inbound.reloop.sh`). If this hostname must keep Google Workspace / Microsoft 365, turn **Email Receiving** off first, or use a [separate subdomain](/docs/guides/connect-domain/what-is-a-domain). See [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
Reloop opens Vercel for consent. Sign in if asked, review the records, and approve.
Return to Reloop and click **Verify & finish** or **Verify Domain** to start checks. Status should move to **active** within a few minutes (see [propagation](/docs/guides/connect-domain/verification/propagation)).
If Auto-populate fails or you prefer full control, use **Manual setup** below. Details: [Auto-populate DNS](/docs/guides/connect-domain/auto-populate) and [Auto-populate failed](/docs/guides/connect-domain/troubleshoot/auto-populate-failed).
## Manual setup
### Log in to Vercel
Open [Vercel](https://vercel.com/dashboard/domains) and go to DNS for the domain whose **nameservers** match `vercel-dns.com`.
Vercel → Domains → domain DNS.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | Vercel field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## Vercel-specific notes
- Prefer Reloop Auto-populate when Vercel nameservers are detected.
- If the domain is only bought at Vercel but NS point elsewhere, edit that host.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to Vercel. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/providers/wordpress-com.mdx
Source: https://reloop.sh/docs/guides/connect-domain/providers/wordpress-com
Markdown: https://reloop.sh/docs/guides/connect-domain/providers/wordpress-com.md
Add Reloop’s DNS records in WordPress.com using the values from your domain page in Reloop.
## Add a domain in Reloop
1. Log in to [Reloop](https://reloop.sh/dashboard/domain) and [add a domain](/docs/guides/connect-domain/add-domain).
2. Keep that tab open. Every host and value you paste must match Reloop exactly (especially the DKIM key).
Reloop works with the [root domain, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain), for example `example.com`, `mail.example.com`, or `updates.example.com`.
## Manual setup
### Log in to WordPress.com
Open [WordPress.com](https://wordpress.com/domains/manage) and go to DNS for the domain whose **nameservers** match `wordpress.com / wp.com`.
WordPress.com → Domains → DNS records.
If Reloop or `dig NS yourdomain.com` shows a different host, stop and edit that host instead. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
### Field mapping
| Reloop | WordPress.com field | Example |
| --- | --- | --- |
| Type | Type | `TXT` / `MX` / `CNAME` |
| Name / Host | Name | `@`, `reloop._domainkey`, `_dmarc`, `link`, … |
| Value / Content | Value | Exact value from Reloop |
| TTL | TTL | `Auto` or provider default |
| Priority (MX only) | Priority | As shown in Reloop (often `10`) |
Omit your domain from the Name/Host field when the provider appends it automatically. Paste `reloop._domainkey` or `link` (or `link.` for subdomains), not the full FQDN ending in `.example.com`. Always copy the host from Reloop.
### Add the records from Reloop
For **each required row** on the Reloop domain page:
Match Reloop’s type: `TXT`, `MX`, or `CNAME`.
Copy the host from Reloop (`@`, `reloop._domainkey`, `_dmarc`, `link` or `link.`, …).
Copy the value exactly. For MX, also set **Priority** as shown (often `10`). If priority `10` is already used by another MX you must keep, use the next free value only when Reloop’s UI allows it. Otherwise, use a [subdomain](/docs/guides/connect-domain/what-is-a-domain).
Save the record, then continue with the next Reloop row.
Typical Reloop rows (always confirm in the dashboard):
| Purpose | Type | Host (typical) | Value (typical) |
| --- | --- | --- | --- |
| SPF | TXT | `@` | `v=spf1 include:reloop.sh -all` (or merge into existing SPF) |
| DKIM | TXT | `reloop._domainkey` | `v=DKIM1; k=rsa; p=…` (unique per domain) |
| DMARC | TXT | `_dmarc` | `v=DMARC1; p=reject;` (or your existing policy), required when Email Sending is on |
| Tracking | CNAME | `link` (or `link.` for subdomains) | `link.reloop.sh` (on by default for new domains) |
| Receiving MX | MX | `@` or subdomain host | `inbound.reloop.sh` (on by default for new domains; turn Email Receiving off if you keep another inbox here) |
Never create a second SPF TXT on the same host. Merge `include:reloop.sh` into the existing SPF. See [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts).
## WordPress.com-specific notes
- WordPress.com is listed as a Domain Connect DNS provider. Reloop’s template is **not** onboarded there yet. Use manual setup unless Reloop shows **Auto populate**.
- If the domain uses custom nameservers elsewhere, edit that host instead.
## Receiving emails
**Email Receiving** is on by default for new domains. If it stays on, you must publish the inbound **MX** Reloop shows (typically `inbound.reloop.sh`). Turn Email Receiving off for send-only if this hostname must keep another inbox.
When Reloop’s MX is active on a hostname, Reloop receives mail for that hostname according to MX priority. That can conflict with another inbox already using MX on the same name. Use a separate hostname for Reloop receiving if needed. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) and [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Complete verification
1. Return to [Domains](https://reloop.sh/dashboard/domain) in Reloop.
2. Open the domain and click **Verify Domain** (setup page: **Verify & finish**; menu: **Re-verify DNS**).
3. Wait until status is **active**. Most providers update within minutes; allow up to 48–72 hours in rare cases ([propagation](/docs/guides/connect-domain/verification/propagation)).
## Troubleshooting
Re-open the Reloop domain page and compare every Type / Host / Value to WordPress.com. Fix typos, truncated DKIM keys, and duplicate SPF records. Then click **Verify Domain** again.
Confirm you edited the DNS host that matches public nameservers (`dig NS yourdomain.com`). See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
Follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) and [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Manual DNS setup](/docs/guides/connect-domain/manual-setup)
- [Auto-populate DNS](/docs/guides/connect-domain/auto-populate)
- [All provider guides](/docs/guides/connect-domain/providers)
---
# guides/connect-domain/registrar-vs-dns-host.mdx
Source: https://reloop.sh/docs/guides/connect-domain/registrar-vs-dns-host
Markdown: https://reloop.sh/docs/guides/connect-domain/registrar-vs-dns-host.md
Two different jobs are easy to confuse:
| Role | What they do | Examples |
| --- | --- | --- |
| **Registrar** | Sells/renews the domain name; stores who owns it | Namecheap, GoDaddy, NameSilo, Cloudflare Registrar |
| **DNS host** | Answers DNS queries for the domain (hosts the zone) | Cloudflare DNS, Route 53, Vercel DNS, the registrar’s own DNS |
Reloop records must be added at the **DNS host** (the place your **nameservers** point to), not necessarily the place you paid for the domain.
## Common setups
### Same company for both
You bought the domain at Namecheap and left default Namecheap nameservers (`registrar-servers.com`). Add Reloop records in Namecheap Advanced DNS.
### Split (very common)
You bought the domain at GoDaddy, but pointed nameservers to Cloudflare:
```
ada.ns.cloudflare.com
bob.ns.cloudflare.com
```
Add Reloop records in **Cloudflare**, not GoDaddy’s DNS UI. Changes in GoDaddy’s zone editor will be ignored while NS point elsewhere.
### Website host as DNS
Domains on Vercel, Netlify, or Squarespace often use that product’s nameservers. Manage Reloop records in that product’s domain/DNS settings.
## How to tell which you have
1. In Reloop, open the domain. We show the detected provider when nameservers match a known host.
2. Or look up nameservers; see [Find your DNS provider](/docs/guides/connect-domain/find-dns-provider).
3. Or check your registrar’s “Nameservers” page: if it lists Cloudflare/AWS/Vercel hosts, use that host’s panel.
## Rule of thumb
> Edit DNS where the **nameservers** live.
If Auto-populate or verification fails after you “added records,” you almost always edited the wrong panel. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
---
# guides/connect-domain/sending-vs-receiving.mdx
Source: https://reloop.sh/docs/guides/connect-domain/sending-vs-receiving
Markdown: https://reloop.sh/docs/guides/connect-domain/sending-vs-receiving.md
Reloop can **send** mail as your domain, **receive** mail into Reloop, or both. DNS requirements differ.
New domains start with **Email Sending**, **Email Receiving**, and click/open **tracking** on. Turn Receiving off before publishing DNS if this hostname must keep another inbox (Google Workspace, Microsoft 365, etc.).
## Sending (outbound)
Goal: Gmail/Outlook/others accept mail from `you@yourdomain.com` as authenticated.
Typically involves:
- **SPF**: authorize Reloop (`include:reloop.sh`)
- **DKIM**: sign messages (`reloop._domainkey`)
- **DMARC**: policy for failures (required for Reloop verification when Email Sending is on)
- **Tracking CNAME**: on by default for new domains (`link` on apex, or `link.` when using a subdomain) → `link.reloop.sh`
Sending does **not** require an MX record. MX is only for inbound mail.
You can keep another provider as the inbox on the same domain **if** you leave **Email Receiving** off and do not publish Reloop’s inbound MX for that hostname.
## Receiving (inbound)
Goal: Mail addressed to your domain is delivered into Reloop (Agent Inbox / inbound pipelines).
Requires **Receiving MX** pointing at Reloop’s inbound host, typically:
```
inbound.reloop.sh
```
(with the priority shown in the dashboard).
MX priority decides where inbound mail goes (lower number = higher preference). Publishing Reloop’s inbound MX (`inbound.reloop.sh`) on a hostname that already has Google/Microsoft MX can divert or split delivery. Prefer a [separate hostname](/docs/guides/connect-domain/what-is-a-domain) for Reloop receiving, or turn **Email Receiving** off for send-only. See [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts).
## Dashboard toggles
On the domain **DNS Records** and **Configuration** tabs you can enable or disable sending, receiving, and tracking. When you change them, Reloop shows which DNS rows are required. Publish what the dashboard marks as needed, then verify again.
## Practical recipes
| Goal | Approach |
| --- | --- |
| Transactional send only; keep existing inbox | Root or subdomain + SPF/DKIM/DMARC (+ tracking as needed); turn **Email Receiving** off; do not publish inbound MX → `inbound.reloop.sh` |
| Full Reloop inbox + send | Subdomain or dedicated domain; Receiving MX → `inbound.reloop.sh` |
| Send from `news.acme.com`, inbox stays on `acme.com` | Add `news.acme.com` in Reloop; leave root MX alone |
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts)
---
# guides/connect-domain/troubleshoot/auto-populate-failed.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot/auto-populate-failed
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot/auto-populate-failed.md
## Messages you might see
| Message (paraphrased) | Meaning | Action |
| --- | --- | --- |
| Provider doesn’t support automatic configuration | No Domain Connect / no sync UX | [Manual setup](/docs/guides/connect-domain/manual-setup) |
| Supports Domain Connect but hasn’t onboarded Reloop’s template | Protocol yes, Reloop template no | [Manual setup](/docs/guides/connect-domain/manual-setup) |
| Failed to start auto-configuration / signing key | Reloop server-side apply URL issue | Retry later or manual; contact support if persistent |
| Popup blocked / nothing opened | Browser blocked the provider tab | Allow popups for Reloop, click again |
| You cancelled at the provider | Consent denied | Retry Auto-populate or configure manually |
## When Auto-populate is offered
**Auto populate** appears when Reloop recognizes nameservers for a host where Reloop’s Domain Connect template is known to work today: Cloudflare, Vercel, Domain Chief, and NameSilo. Clicking the button also runs live discovery as a safety check. See [Auto-populate DNS](/docs/guides/connect-domain/auto-populate).
Other Domain Connect–capable hosts may still need [manual setup](/docs/guides/connect-domain/manual-setup) until Reloop’s template is available there and the dashboard offers the button.
## Fallback
Every domain can be configured manually with the same records. Auto-populate is convenience, not a hard requirement.
---
# guides/connect-domain/troubleshoot/cname-conflicts.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot/cname-conflicts
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot/cname-conflicts.md
## Common cases
### Cloudflare orange cloud
Mail and auth records should usually be **DNS only** (grey cloud). Proxying MX/TXT/CNAME for mail often breaks verification or delivery. Set Reloop-related records to DNS only.
### Apex CNAME
Some providers forbid CNAME on `@`. Reloop’s tracking record uses a **subdomain** (`link`), which is allowed almost everywhere. If your provider rejects a CNAME, confirm you did not place it on `@` by mistake.
### Existing record on the same host
If `link.yourdomain.com` already has an A/AAAA/CNAME for another product, Reloop’s tracking CNAME cannot coexist on that exact name. Either:
- Change Reloop’s tracking subdomain in domain settings (if available), or
- Repoint `link` to Reloop and move the old service elsewhere
See [Tracking CNAME](/docs/guides/connect-domain/troubleshoot/tracking-cname).
### CNAME + other types on same name
DNS forbids mixing CNAME with other record types on the same owner name. Remove conflicting A/TXT on `link` before adding the Reloop CNAME.
---
# guides/connect-domain/troubleshoot/dkim-dmarc-typos.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot/dkim-dmarc-typos
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot/dkim-dmarc-typos.md
## DKIM checklist
| Check | Details |
| --- | --- |
| Host | Usually `reloop._domainkey`, not `reloop._domainkey.yourdomain.com` if the UI adds the domain |
| Type | TXT |
| Value | Starts with `v=DKIM1; k=rsa; p=` and a long base64 key |
| Length | If the panel truncates, use “split TXT” / multi-string support or a provider that accepts long TXT |
### Verify
```bash
dig TXT reloop._domainkey.yourdomain.com +short
```
The `p=` material should match Reloop’s dashboard (ignoring quotes/spaces some dig output adds).
## DMARC checklist
| Check | Details |
| --- | --- |
| Host | `_dmarc` |
| Type | TXT |
| Value | Starts with `v=DMARC1;` |
Reloop may suggest `p=reject`. If you already run a careful DMARC program with Google/Microsoft, do not blindly overwrite without reviewing `rua` / policy impact.
## Quotes and spaces
Some UIs wrap TXT values in quotes automatically. Prefer pasting the raw value Reloop shows. Avoid inserting line breaks in the middle of the DKIM key unless the provider’s docs say to split properly.
---
# guides/connect-domain/troubleshoot/domain-already-registered.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot/domain-already-registered
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot/domain-already-registered.md
Reloop allows a domain to be verified in one organization at a time (product rules may vary by environment). If you see that the domain is already registered:
1. Confirm you are in the correct Reloop organization.
2. Ask the owner of the other org to remove the domain if it was added by mistake.
3. If you believe this is an error (expired trial, lost access), contact Reloop support with proof of domain ownership (registrar WHOIS / DNS challenge).
Also see the guides article: [Domain already registered](/docs/guides/domain-already-registered).
---
# guides/connect-domain/troubleshoot/domain-mismatch.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot/domain-mismatch
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot/domain-mismatch.md
## Symptom
API or SMTP send fails because the `From` address domain is not a verified domain in your organization.
## Fix
1. Verify the domain (or subdomain) you want to send from. See [Add a domain](/docs/guides/connect-domain/add-domain).
2. Send with a `From` on that exact verified hostname (e.g. if you verified `mail.acme.com`, use `you@mail.acme.com`).
Also see: [Domain mismatch](/docs/guides/error-domain-mismatch).
---
# guides/connect-domain/troubleshoot/index.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot.md
Pick the problem that matches what you see:
| Symptom | Guide |
| --- | --- |
| Verify stays pending/failed | [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) |
| Added records but dig is empty / wrong host | [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers) |
| Two SPF records / SPF fail | [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts) |
| CNAME not allowed / orange cloud | [CNAME conflicts](/docs/guides/connect-domain/troubleshoot/cname-conflicts) |
| Existing inbox broke after MX change | [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts) |
| DKIM/DMARC value looks wrong | [DKIM / DMARC typos](/docs/guides/connect-domain/troubleshoot/dkim-dmarc-typos) |
| `link` subdomain clash | [Tracking CNAME](/docs/guides/connect-domain/troubleshoot/tracking-cname) |
| Auto-populate error toast | [Auto-populate failed](/docs/guides/connect-domain/troubleshoot/auto-populate-failed) |
| Domain taken in Reloop | [Already registered](/docs/guides/connect-domain/troubleshoot/domain-already-registered) |
| From domain does not match | [Domain mismatch](/docs/guides/connect-domain/troubleshoot/domain-mismatch) |
---
# guides/connect-domain/troubleshoot/mx-conflicts.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot/mx-conflicts
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot/mx-conflicts.md
MX records decide **where inbound mail is delivered**. Lower priority number = higher preference.
## Sending-only with an existing inbox
If you only need Reloop to **send**, keep your current inbox MX where it is. Publish SPF, DKIM, DMARC, and tracking as shown in Reloop, and turn **Email Receiving** off so Reloop does not expect inbound MX (`inbound.reloop.sh`). Sending does not require an MX record.
New domains start with Email Receiving on; turn it off before Auto-populate or manual publish if you must keep Google Workspace / Microsoft 365 on that hostname.
## Receiving with Reloop
Enabling Reloop receiving expects MX toward `inbound.reloop.sh`. On the **same hostname** as another inbox, adding Reloop’s inbound MX can divert or split delivery depending on priority and which MX records you keep. Do not remove Google/Microsoft MX unless you intend Reloop to receive mail for that name.
### Safe pattern
Use separate hostnames:
- `acme.com` MX → your existing inbox
- `mail.acme.com` MX → Reloop (inbound + Reloop send)
Reloop supports the [root, a subdomain, or both](/docs/guides/connect-domain/what-is-a-domain). See also [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving).
## Multiple MX priorities
If you experiment with multiple MX targets, lower priority number = higher preference. Mixing two inbox providers on one hostname is advanced and usually the wrong fix. Prefer a separate hostname for Reloop receiving.
---
# guides/connect-domain/troubleshoot/not-verifying.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot/not-verifying
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot/not-verifying.md
Work through this list in order. Most failures are wrong panel, typo, SPF duplicate, or propagation.
## 1. Confirm you edited the live DNS host
Lookup NS for your domain. The company in those hostnames must be where you added records.
```bash
dig NS yourdomain.com +short
```
If NS say Cloudflare but you edited GoDaddy, Reloop will never see your changes. See [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers).
## 2. Confirm records are public
```bash
dig TXT reloop._domainkey.yourdomain.com +short
dig TXT yourdomain.com +short
dig TXT _dmarc.yourdomain.com +short
dig MX yourdomain.com +short
dig CNAME link.yourdomain.com +short
```
Compare to the Reloop dashboard (for subdomains, dig the subdomain hosts Reloop shows, e.g. `link.mail.yourdomain.com`). If dig is empty, wait for [propagation](/docs/guides/connect-domain/verification/propagation) or fix the publish step.
## 3. Fix common record mistakes
- **DKIM truncated**: paste the full `p=` key ([typos](/docs/guides/connect-domain/troubleshoot/dkim-dmarc-typos))
- **Two SPF TXT records**: merge ([SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts))
- **Wrong host**: UI double-appended the domain (`reloop._domainkey.example.com.example.com`)
- **Proxied Cloudflare records**: set mail-related records to DNS only
- **Missing required group**: receiving MX or tracking still expected while toggles are on (defaults are on for new domains)
## 4. Re-verify
In Reloop, open the domain → **Verify Domain** (setup: **Verify & finish**; menu: **Re-verify DNS**). Repeat after 15–30 minutes if dig just started showing values.
## 5. Still failing?
- Open the matching [provider guide](/docs/guides/connect-domain/providers)
- Try [Auto-populate](/docs/guides/connect-domain/auto-populate) if the dashboard shows **Auto populate**
- Contact Reloop support with: domain name, screenshot of DNS rows, and `dig` output
> [!NOTE]
> Self-hosting? If every record fails at once and `dig` from the host shows them
> fine, the containers cannot reach a resolver. See
> [Network Requirements](/docs/self-host/requirements#network-requirements).
---
# guides/connect-domain/troubleshoot/spf-conflicts.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot/spf-conflicts
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot/spf-conflicts.md
## Rule
A hostname should have **one** SPF policy: one TXT that starts with `v=spf1`. Multiple SPF TXT records cause unpredictable failures.
## Reloop’s include
```
include:reloop.sh
```
Example merged record (illustrative):
```
v=spf1 include:_spf.google.com include:reloop.sh -all
```
Keep your existing includes; add Reloop’s; keep a single ending mechanism (`-all` or `~all` as you prefer; Reloop’s generated record uses `-all` when it creates a fresh SPF).
## What not to do
- Do not add a second TXT `v=spf1 include:reloop.sh -all` beside Google’s SPF
- Do not put SPF inside a DMARC or DKIM record
- Do not use outdated includes like `_spf.reloop.sh` unless your dashboard explicitly shows them; hosted Reloop uses `include:reloop.sh`
## After merging
Save DNS → wait briefly → Verify in Reloop. Check:
```bash
dig TXT yourdomain.com +short
```
You should see one SPF string containing `include:reloop.sh`.
---
# guides/connect-domain/troubleshoot/tracking-cname.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot/tracking-cname
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot/tracking-cname.md
Reloop typically expects:
| Type | Host | Value |
| --- | --- | --- |
| CNAME | `link` | `link.reloop.sh` |
So `link.yourdomain.com` resolves to Reloop’s tracking edge.
## Symptoms
- Tracking record shows failed in Reloop
- Another app already uses `link.yourdomain.com`
- Provider error when saving the CNAME
## Fixes
1. Remove or rename the old `link` record if you no longer need it.
2. If you must keep `link` for something else, change Reloop’s tracking subdomain in domain settings (when available) and publish the new host.
3. Ensure no A/AAAA sits on the same host as the CNAME.
4. On Cloudflare, use **DNS only** for the tracking CNAME unless Reloop docs say otherwise.
## Related
- [CNAME conflicts](/docs/guides/connect-domain/troubleshoot/cname-conflicts)
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
---
# guides/connect-domain/troubleshoot/wrong-nameservers.mdx
Source: https://reloop.sh/docs/guides/connect-domain/troubleshoot/wrong-nameservers
Markdown: https://reloop.sh/docs/guides/connect-domain/troubleshoot/wrong-nameservers.md
## Symptom
- Reloop Verify fails
- `dig` does not show the records you added
- Or `dig` shows an old zone
## Cause
DNS records only count at the host named by your domain’s **NS** records. Editing the registrar’s “DNS” tab while NS point to Cloudflare (or vice versa) publishes into a zone nobody queries.
## Fix
```bash
dig NS yourdomain.com +short
```
Use [Find your DNS provider](/docs/guides/connect-domain/find-dns-provider) to map NS → company.
Copy from the Reloop dashboard again into the correct panel.
Remove mistaken duplicate records from the unused panel so you are not confused later.
## Changing nameservers on purpose
If you intentionally move DNS (e.g. Namecheap → Cloudflare):
1. Copy existing important records to the new host first
2. Change NS at the registrar
3. Wait for NS propagation
4. Re-add Reloop records at the new host
5. Verify again
See also [Change DNS later](/docs/guides/connect-domain/after/change-dns).
---
# guides/connect-domain/verification/how-verification-works.mdx
Source: https://reloop.sh/docs/guides/connect-domain/verification/how-verification-works
Markdown: https://reloop.sh/docs/guides/connect-domain/verification/how-verification-works.md
## High level
1. You (or Domain Connect) publish records in your DNS host.
2. Reloop queries public DNS for each required name/type.
3. Values are compared to what Reloop stored for your domain (SPF include, DKIM public key, DMARC, MX, tracking CNAME, …).
4. If required records match, status becomes **active**.
## What Reloop does *not* do
- Reloop does not log into your registrar for you (except via Domain Connect consent you approve).
- Reloop does not see private DNS behind a firewall, only what the public internet resolves.
- Reloop cannot fix typos in your panel; you must edit DNS and re-verify.
## Partial matches
Some records may pass while others fail (e.g. SPF OK, DKIM truncated). The domain stays non-active until required rows succeed. Open the domain’s DNS records UI in Reloop to see which groups still fail.
## Re-verification
After any DNS edit, click **Verify Domain** (or **Re-verify DNS** in the domain menu) again to start a new check. Reloop may retry that job after it starts; it does not silently verify while status stays pending. Cached resolvers may lag. See [propagation](/docs/guides/connect-domain/verification/propagation).
## Related
- [DNS records explained](/docs/guides/connect-domain/dns-records-explained)
- [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying)
---
# guides/connect-domain/verification/index.mdx
Source: https://reloop.sh/docs/guides/connect-domain/verification
Markdown: https://reloop.sh/docs/guides/connect-domain/verification.md
After you publish DNS records, Reloop checks the public internet for a match. This section explains statuses, the check process, and propagation delays.
## In this section
- [Domain statuses](/docs/guides/connect-domain/verification/statuses)
- [How verification works](/docs/guides/connect-domain/verification/how-verification-works)
- [DNS propagation](/docs/guides/connect-domain/verification/propagation)
## Quick path
1. Publish records ([auto](/docs/guides/connect-domain/auto-populate) or [manual](/docs/guides/connect-domain/manual-setup))
2. Click **Verify** on the domain page
3. Wait until status is **active**
4. [Send a test](/docs/guides/connect-domain/after/test-domain)
If checks fail, start at [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying).
---
# guides/connect-domain/verification/propagation.mdx
Source: https://reloop.sh/docs/guides/connect-domain/verification/propagation
Markdown: https://reloop.sh/docs/guides/connect-domain/verification/propagation.md
**Propagation** means “how long until DNS resolvers worldwide see your new records.” It is normal for Reloop verification to fail for a few minutes after you save.
## Typical times
| Provider class | Often visible within |
| --- | --- |
| Cloudflare, Vercel, many modern APIs | Seconds to a few minutes |
| Traditional registrars | Minutes to a few hours |
| Worst case (high TTL / stubborn caches) | Up to 48–72 hours |
Lower TTLs help future changes propagate faster; the first publish still depends on old cache entries.
## Check yourself
Replace `acme.com` with your domain:
```bash
# SPF / apex TXT
dig TXT acme.com +short
# DKIM
dig TXT reloop._domainkey.acme.com +short
# DMARC
dig TXT _dmarc.acme.com +short
# Tracking
dig CNAME link.acme.com +short
# MX
dig MX acme.com +short
```
If `dig` does not show the Reloop values yet, Reloop cannot verify either. Wait and retry.
Online “DNS checker” sites that query multiple regions are also fine.
## Tips
- After Domain Connect approve, wait 1–2 minutes before the first Verify.
- Do not delete and recreate records repeatedly, as that resets TTL clocks.
- Confirm you edited the live nameserver host ([wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers)).
## Related
- [How verification works](/docs/guides/connect-domain/verification/how-verification-works)
- [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying)
---
# guides/connect-domain/verification/statuses.mdx
Source: https://reloop.sh/docs/guides/connect-domain/verification/statuses
Markdown: https://reloop.sh/docs/guides/connect-domain/verification/statuses.md
| Status | Meaning | What you should do |
| --- | --- | --- |
| **pending** | Domain created; DNS not confirmed yet | Publish records (auto or manual), then verify |
| **verifying** | Reloop is checking public DNS | Wait; re-check after propagation |
| **active** | Required records matched | Send mail; optionally [test](/docs/guides/connect-domain/after/test-domain) |
| **failed** | Checks did not match expected records | Fix DNS using [not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying) |
| **suspended** | Domain disabled (policy, billing, or admin) | Contact support / fix account issues |
## Banner behavior
While status is `pending`, `verifying`, or `failed`, the domain page shows the DNS helper banner:
- **Auto populate** when Reloop discovers Domain Connect support for your host
- **Open DNS settings** / manual guidance otherwise
## Related
- [How verification works](/docs/guides/connect-domain/verification/how-verification-works)
- [Propagation](/docs/guides/connect-domain/verification/propagation)
---
# guides/connect-domain/what-is-a-domain.mdx
Source: https://reloop.sh/docs/guides/connect-domain/what-is-a-domain
Markdown: https://reloop.sh/docs/guides/connect-domain/what-is-a-domain.md
A **domain** is the human-readable name for your brand on the internet, for example `acme.com` or `shop.acme.com`.
## Domain vs website vs email
| Concept | Example | Meaning |
| --- | --- | --- |
| **Domain** | `acme.com` | The name you registered |
| **Website** | `https://acme.com` | Pages hosted somewhere (Vercel, Netlify, WordPress, …) |
| **Email address** | `hello@acme.com` | Mail identity that uses the domain after the `@` |
Reloop is email infrastructure, not a website host. Your site can stay on Vercel, Netlify, WordPress, or anywhere else. Reloop only needs permission (via DNS) to **authenticate and deliver** mail for that domain.
## Who “owns” a domain?
Ownership is recorded at a **registrar** (GoDaddy, Namecheap, NameSilo, Cloudflare Registrar, Squarespace, etc.). You renew it yearly (or longer). Owning the domain means you can:
- Change **nameservers** (who answers DNS questions)
- Create **DNS records** (or point NS to another DNS host that does)
- Prove control to services like Reloop
## Root domain vs subdomain
- **Root (apex):** `acme.com`
- **Subdomain:** `mail.acme.com`, `news.acme.com`, `send.acme.com`
You can connect the **root**, a **subdomain**, or **both** to Reloop.
### Pros of a subdomain
- Existing MX on the root stays untouched
- Easier SPF story if the root already has a long SPF
- Separate sending reputation from corporate mail
- Clearer mental model: “product mail lives on `mail.`”
From addresses look like `hello@mail.acme.com` unless you also configure other identities. Plan branding accordingly.
### Pros of the root domain
- Addresses look like `hello@acme.com`
- Fewer labels to manage
- Fine when Reloop is the primary mail platform for that domain
## What Reloop needs from you
1. A domain (or subdomain) you control
2. Ability to publish DNS records at the host that serves that domain’s nameservers
3. Verification in the Reloop dashboard
---
# guides/connect-domain/what-is-dns.mdx
Source: https://reloop.sh/docs/guides/connect-domain/what-is-dns
Markdown: https://reloop.sh/docs/guides/connect-domain/what-is-dns.md
**DNS** (Domain Name System) is the internet’s phone book. When someone sends mail to `hello@acme.com` or opens `acme.com`, computers ask DNS: “Where should this go?” and “Is this sender allowed?”
For Reloop, DNS is how you **prove you own the domain** and **authorize Reloop to send mail** on your behalf.
## Nameservers
**Nameservers** are the servers that hold your domain’s DNS zone.
Example:
```
ada.ns.cloudflare.com
bob.ns.cloudflare.com
```
Whoever runs those nameservers is your **DNS host**, which is where you add Reloop’s records. Often that is the same company as your registrar, but not always (see [Registrar vs DNS host](/docs/guides/connect-domain/registrar-vs-dns-host)).
## DNS records
A **record** is one instruction in your zone. Reloop asks for several types:
| Type | Role for Reloop |
| --- | --- |
| **TXT** | SPF, DKIM public key, DMARC policy, ownership checks |
| **MX** | Where inbound mail is delivered (only if you enable receiving) |
| **CNAME** | Click/open tracking host (e.g. `link.acme.com` → Reloop) |
Each record has roughly:
- **Host / Name**: which name the record applies to (`@`, `reloop._domainkey`, `link`, …)
- **Type**: TXT, MX, CNAME, …
- **Value / Points to / Content**: the data Reloop shows in the dashboard
- **TTL**: how long resolvers may cache the answer (Auto / 300 / 3600 are common)
Copy these **exactly** from Reloop. A missing character in a DKIM key breaks verification.
## How Reloop uses DNS
```
You add domain in Reloop
↓
Reloop generates records (SPF, DKIM, DMARC, …)
↓
You publish them at your DNS host (auto or manual)
↓
Reloop looks up public DNS worldwide
↓
If records match → domain becomes active
```
## Propagation
After you save a record, the internet does not update instantly everywhere. Caches honor **TTL**. Most providers update in minutes; worst case up to ~48–72 hours. Details: [DNS propagation](/docs/guides/connect-domain/verification/propagation).
## You do not need to memorize this
In practice:
1. Open the domain in [Reloop](https://reloop.sh/dashboard/domain)
2. Use Auto-populate **or** copy each row into your DNS panel
3. Click **Verify**
For a field-by-field map of Reloop’s records, see [DNS records explained](/docs/guides/connect-domain/dns-records-explained).
---
# guides/creating-email-address.mdx
Source: https://reloop.sh/docs/guides/creating-email-address
Markdown: https://reloop.sh/docs/guides/creating-email-address.md
How to create a sender email address in Reloop.
## Overview
This guide helps you understand and configure this sending feature in Reloop.
## Details
Understanding your sending configuration is key to successful email delivery. Review the [Reloop dashboard](https://reloop.sh/dashboard) for your current settings and limits.
## Configuration
Check your current plan and usage in the [Reloop dashboard](https://reloop.sh/dashboard/settings).
Adjust your sending configuration based on your requirements and plan limits.
Keep track of your sending volume and adjust as needed to stay within limits.
## Learn More
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
- [Sending Emails](/docs/learn/emails)
- [Email Best Practices](/docs/integrations/agent-skills/email-best-practices)
---
# guides/dedicated-ips.mdx
Source: https://reloop.sh/docs/guides/dedicated-ips
Markdown: https://reloop.sh/docs/guides/dedicated-ips.md
Understanding dedicated IP addresses for email sending.
## Overview
This guide helps you understand and configure this sending feature in Reloop.
## Details
Understanding your sending configuration is key to successful email delivery. Review the [Reloop dashboard](https://reloop.sh/dashboard) for your current settings and limits.
## Configuration
Check your current plan and usage in the [Reloop dashboard](https://reloop.sh/dashboard/settings).
Adjust your sending configuration based on your requirements and plan limits.
Keep track of your sending volume and adjust as needed to stay within limits.
## Learn More
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
- [Sending Emails](/docs/learn/emails)
- [Email Best Practices](/docs/integrations/agent-skills/email-best-practices)
---
# guides/delete-account.mdx
Source: https://reloop.sh/docs/guides/delete-account
Markdown: https://reloop.sh/docs/guides/delete-account.md
How to permanently delete your Reloop account.
## Overview
Manage your Reloop account settings effectively to ensure smooth operation.
## Steps
Navigate to the [Account Settings](https://reloop.sh/dashboard/settings) page in your Reloop dashboard.
Follow the on-screen instructions to update your account configuration.
Review and confirm your changes. Some changes may require email verification.
If you need assistance with account changes, contact [Reloop support](https://reloop.sh/help).
## Learn More
- [Handling API Keys](/docs/guides/handling-api-keys)
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
---
# guides/delivered-not-arriving.mdx
Source: https://reloop.sh/docs/guides/delivered-not-arriving
Markdown: https://reloop.sh/docs/guides/delivered-not-arriving.md
What to do when an email shows delivered but was not received.
## Overview
Understanding this topic is important for maintaining good email deliverability with Reloop.
## Key Points
- Follow email sending best practices to maintain a strong sender reputation
- Monitor your delivery rates in the [Reloop dashboard](https://reloop.sh/dashboard)
- Keep your email lists clean and up-to-date
## Best Practices
Regularly check your delivery, bounce, and complaint rates in the Reloop dashboard.
Ensure SPF, DKIM, and DMARC are properly configured for your domain.
Always include an unsubscribe mechanism in marketing emails and honor opt-out requests promptly.
For detailed guidance on email deliverability, see our [Email Best Practices](/docs/integrations/agent-skills/email-best-practices) skill documentation.
## Learn More
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Warm-up Guide](/docs/guides/warmup-guide)
- [Audience Hygiene](/docs/guides/audience-hygiene)
- [Domains Guide](/docs/learn/domain)
---
# guides/dkim-key-length.mdx
Source: https://reloop.sh/docs/guides/dkim-key-length
Markdown: https://reloop.sh/docs/guides/dkim-key-length.md
Do you need 2048-bit DKIM keys?
## Overview
Understanding this topic is important for maintaining good email deliverability with Reloop.
## Key Points
- Follow email sending best practices to maintain a strong sender reputation
- Monitor your delivery rates in the [Reloop dashboard](https://reloop.sh/dashboard)
- Keep your email lists clean and up-to-date
## Best Practices
Regularly check your delivery, bounce, and complaint rates in the Reloop dashboard.
Ensure SPF, DKIM, and DMARC are properly configured for your domain.
Always include an unsubscribe mechanism in marketing emails and honor opt-out requests promptly.
For detailed guidance on email deliverability, see our [Email Best Practices](/docs/integrations/agent-skills/email-best-practices) skill documentation.
## Learn More
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Warm-up Guide](/docs/guides/warmup-guide)
- [Audience Hygiene](/docs/guides/audience-hygiene)
- [Domains Guide](/docs/learn/domain)
---
# guides/dns-cloudflare.mdx
Source: https://reloop.sh/docs/guides/dns-cloudflare
Markdown: https://reloop.sh/docs/guides/dns-cloudflare.md
This page moved to **[Cloudflare DNS setup](/docs/guides/connect-domain/providers/cloudflare)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-dreamhost.mdx
Source: https://reloop.sh/docs/guides/dns-dreamhost
Markdown: https://reloop.sh/docs/guides/dns-dreamhost.md
This page moved to **[DreamHost DNS setup](/docs/guides/connect-domain/providers/dreamhost)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-gandi.mdx
Source: https://reloop.sh/docs/guides/dns-gandi
Markdown: https://reloop.sh/docs/guides/dns-gandi.md
This page moved to **[Gandi DNS setup](/docs/guides/connect-domain/providers/gandi)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-godaddy.mdx
Source: https://reloop.sh/docs/guides/dns-godaddy
Markdown: https://reloop.sh/docs/guides/dns-godaddy.md
This page moved to **[GoDaddy DNS setup](/docs/guides/connect-domain/providers/godaddy)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-hetzner.mdx
Source: https://reloop.sh/docs/guides/dns-hetzner
Markdown: https://reloop.sh/docs/guides/dns-hetzner.md
This page moved to **[Hetzner DNS setup](/docs/guides/connect-domain/providers/hetzner)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-hostinger.mdx
Source: https://reloop.sh/docs/guides/dns-hostinger
Markdown: https://reloop.sh/docs/guides/dns-hostinger.md
This page moved to **[Hostinger DNS setup](/docs/guides/connect-domain/providers/hostinger)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-ionos.mdx
Source: https://reloop.sh/docs/guides/dns-ionos
Markdown: https://reloop.sh/docs/guides/dns-ionos.md
This page moved to **[IONOS DNS setup](/docs/guides/connect-domain/providers/ionos)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-namecheap.mdx
Source: https://reloop.sh/docs/guides/dns-namecheap
Markdown: https://reloop.sh/docs/guides/dns-namecheap.md
This page moved to **[Namecheap DNS setup](/docs/guides/connect-domain/providers/namecheap)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-porkbun.mdx
Source: https://reloop.sh/docs/guides/dns-porkbun
Markdown: https://reloop.sh/docs/guides/dns-porkbun.md
This page moved to **[Porkbun DNS setup](/docs/guides/connect-domain/providers/porkbun)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-route53.mdx
Source: https://reloop.sh/docs/guides/dns-route53
Markdown: https://reloop.sh/docs/guides/dns-route53.md
This page moved to **[AWS Route 53 DNS setup](/docs/guides/connect-domain/providers/route53)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-setup.mdx
Source: https://reloop.sh/docs/guides/dns-setup
Markdown: https://reloop.sh/docs/guides/dns-setup.md
DNS setup for Reloop now lives under **[Connect a domain](/docs/guides/connect-domain)**.
- [All provider guides](/docs/guides/connect-domain/providers)
- [Auto-populate](/docs/guides/connect-domain/auto-populate)
- [Manual setup](/docs/guides/connect-domain/manual-setup)
- [Find your DNS provider](/docs/guides/connect-domain/find-dns-provider)
---
# guides/dns-squarespace.mdx
Source: https://reloop.sh/docs/guides/dns-squarespace
Markdown: https://reloop.sh/docs/guides/dns-squarespace.md
This page moved to **[Squarespace DNS setup](/docs/guides/connect-domain/providers/squarespace)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-strato.mdx
Source: https://reloop.sh/docs/guides/dns-strato
Markdown: https://reloop.sh/docs/guides/dns-strato.md
This page moved to **[Strato DNS setup](/docs/guides/connect-domain/providers/strato)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/dns-vercel.mdx
Source: https://reloop.sh/docs/guides/dns-vercel
Markdown: https://reloop.sh/docs/guides/dns-vercel.md
This page moved to **[Vercel DNS setup](/docs/guides/connect-domain/providers/vercel)**.
See also the [provider guides hub](/docs/guides/connect-domain/providers) and [Connect a domain](/docs/guides/connect-domain).
---
# guides/domain-already-registered.mdx
Source: https://reloop.sh/docs/guides/domain-already-registered
Markdown: https://reloop.sh/docs/guides/domain-already-registered.md
What to do when your domain is already registered.
## Overview
This article covers common causes and solutions for this issue when using Reloop.
## Common Causes
- Misconfigured DNS records
- API key permissions not matching the domain
- Rate limiting or account restrictions
## Resolution Steps
Review your domain settings in the [Reloop dashboard](https://reloop.sh/dashboard/domain) and ensure all DNS records are correctly configured.
Ensure your API key has the correct permissions for the operation you're performing. Go to [API Keys](https://reloop.sh/dashboard/api-keys) to review.
If the issue persists, reach out to [Reloop support](https://reloop.sh/help) with your error details and domain information.
## Learn More
- [Domain Not Verifying](/docs/guides/domain-not-verifying)
- [Avoid MX Conflicts](/docs/guides/avoid-mx-conflicts)
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
---
# guides/domain-not-verifying.mdx
Source: https://reloop.sh/docs/guides/domain-not-verifying
Markdown: https://reloop.sh/docs/guides/domain-not-verifying.md
This guide moved to **[Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying)** under Connect a domain.
Also useful:
- [Wrong nameservers](/docs/guides/connect-domain/troubleshoot/wrong-nameservers)
- [SPF conflicts](/docs/guides/connect-domain/troubleshoot/spf-conflicts)
- [Connect a domain](/docs/guides/connect-domain)
---
# guides/downloading-documents.mdx
Source: https://reloop.sh/docs/guides/downloading-documents
Markdown: https://reloop.sh/docs/guides/downloading-documents.md
How to download invoices and other documents.
## Overview
Manage your Reloop account settings effectively to ensure smooth operation.
## Steps
Navigate to the [Account Settings](https://reloop.sh/dashboard/settings) page in your Reloop dashboard.
Follow the on-screen instructions to update your account configuration.
Review and confirm your changes. Some changes may require email verification.
If you need assistance with account changes, contact [Reloop support](https://reloop.sh/help).
## Learn More
- [Handling API Keys](/docs/guides/handling-api-keys)
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
---
# guides/e2e-testing-playwright.mdx
Source: https://reloop.sh/docs/guides/e2e-testing-playwright
Markdown: https://reloop.sh/docs/guides/e2e-testing-playwright.md
Test your email flows end-to-end using Playwright and Reloop's API.
## Approach
Use Reloop's API to verify that emails were sent correctly during your E2E tests. Instead of checking actual inboxes, query the Reloop API to confirm emails were delivered.
## Setup
```bash
npm install @playwright/test reloop
```
## Example Test
```typescript
const reloop = new Reloop(process.env.RELOOP_API_KEY);
test('signup sends welcome email', async ({ page }) => {
// Trigger the signup flow
await page.goto('/signup');
await page.fill('[name="email"]', 'test@example.com');
await page.click('button[type="submit"]');
// Wait for the email to be sent
await page.waitForTimeout(3000);
// Verify via Reloop API
const { data } = await reloop.emails.list();
const welcomeEmail = data?.data?.find(
(e) => e.to?.includes('test@example.com') && e.subject?.includes('Welcome')
);
expect(welcomeEmail).toBeDefined();
expect(welcomeEmail?.last_event).toBe('delivered');
});
```
Use [testing email addresses](/docs/guides/testing-email-addresses) to avoid sending real emails during tests.
---
# guides/email-consent.mdx
Source: https://reloop.sh/docs/guides/email-consent
Markdown: https://reloop.sh/docs/guides/email-consent.md
Understanding email consent and its legal requirements.
## Overview
Understanding this topic is important for maintaining good email deliverability with Reloop.
## Key Points
- Follow email sending best practices to maintain a strong sender reputation
- Monitor your delivery rates in the [Reloop dashboard](https://reloop.sh/dashboard)
- Keep your email lists clean and up-to-date
## Best Practices
Regularly check your delivery, bounce, and complaint rates in the Reloop dashboard.
Ensure SPF, DKIM, and DMARC are properly configured for your domain.
Always include an unsubscribe mechanism in marketing emails and honor opt-out requests promptly.
For detailed guidance on email deliverability, see our [Email Best Practices](/docs/integrations/agent-skills/email-best-practices) skill documentation.
## Learn More
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Warm-up Guide](/docs/guides/warmup-guide)
- [Audience Hygiene](/docs/guides/audience-hygiene)
- [Domains Guide](/docs/learn/domain)
---
# guides/emails-going-to-spam.mdx
Source: https://reloop.sh/docs/guides/emails-going-to-spam
Markdown: https://reloop.sh/docs/guides/emails-going-to-spam.md
Why your emails might be going to spam and how to fix it.
## Overview
Understanding this topic is important for maintaining good email deliverability with Reloop.
## Key Points
- Follow email sending best practices to maintain a strong sender reputation
- Monitor your delivery rates in the [Reloop dashboard](https://reloop.sh/dashboard)
- Keep your email lists clean and up-to-date
## Best Practices
Regularly check your delivery, bounce, and complaint rates in the Reloop dashboard.
Ensure SPF, DKIM, and DMARC are properly configured for your domain.
Always include an unsubscribe mechanism in marketing emails and honor opt-out requests promptly.
For detailed guidance on email deliverability, see our [Email Best Practices](/docs/integrations/agent-skills/email-best-practices) skill documentation.
## Learn More
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Warm-up Guide](/docs/guides/warmup-guide)
- [Audience Hygiene](/docs/guides/audience-hygiene)
- [Domains Guide](/docs/learn/domain)
---
# guides/error-1010.mdx
Source: https://reloop.sh/docs/guides/error-1010
Markdown: https://reloop.sh/docs/guides/error-1010.md
How to resolve the 403 Error 1010 when sending emails.
## Overview
This article covers common causes and solutions for this issue when using Reloop.
## Common Causes
- Misconfigured DNS records
- API key permissions not matching the domain
- Rate limiting or account restrictions
## Resolution Steps
Review your domain settings in the [Reloop dashboard](https://reloop.sh/dashboard/domain) and ensure all DNS records are correctly configured.
Ensure your API key has the correct permissions for the operation you're performing. Go to [API Keys](https://reloop.sh/dashboard/api-keys) to review.
If the issue persists, reach out to [Reloop support](https://reloop.sh/help) with your error details and domain information.
## Learn More
- [Domain Not Verifying](/docs/guides/domain-not-verifying)
- [Avoid MX Conflicts](/docs/guides/avoid-mx-conflicts)
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
---
# guides/error-domain-mismatch.mdx
Source: https://reloop.sh/docs/guides/error-domain-mismatch
Markdown: https://reloop.sh/docs/guides/error-domain-mismatch.md
How to fix the 403 domain mismatch error.
## Overview
This article covers common causes and solutions for this issue when using Reloop.
## Common Causes
- Misconfigured DNS records
- API key permissions not matching the domain
- Rate limiting or account restrictions
## Resolution Steps
Review your domain settings in the [Reloop dashboard](https://reloop.sh/dashboard/domain) and ensure all DNS records are correctly configured.
Ensure your API key has the correct permissions for the operation you're performing. Go to [API Keys](https://reloop.sh/dashboard/api-keys) to review.
If the issue persists, reach out to [Reloop support](https://reloop.sh/help) with your error details and domain information.
## Learn More
- [Domain Not Verifying](/docs/guides/domain-not-verifying)
- [Avoid MX Conflicts](/docs/guides/avoid-mx-conflicts)
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
---
# guides/error-reloop-dev-domain.mdx
Source: https://reloop.sh/docs/guides/error-reloop-dev-domain
Markdown: https://reloop.sh/docs/guides/error-reloop-dev-domain.md
Why you cannot send from the `reloop.sh` domain via the public send API.
## Overview
`reloop.sh` (or your deployment's `ONBOARDING_TEST_DOMAIN`) is a **Reloop-owned onboarding test domain**. It is reserved for the onboarding **Send email** button, not for arbitrary customer domain setup.
If you call `POST /api/mail/v1/send` with a From address on that domain, Reloop returns an error directing you to use your own verified domain.
## Common Causes
- Using `onboarding@reloop.sh` (or similar) in a public `/send` request
- Trying to add `reloop.sh` as a customer domain in the dashboard
- Expecting open sandbox sends to any recipient from the platform domain
## What works instead
After generating an API key, click **Send test email to me**. That path sends only to your account email from the platform domain.
Add your domain in the [dashboard](https://reloop.sh/dashboard/domain), finish DNS verification, then send with a From address on that domain.
If a legitimate platform test fails, contact [Reloop support](https://reloop.sh/help) with the error payload and approximate time.
## Learn More
- [Email Addresses for Testing](/docs/guides/testing-email-addresses)
- [Domain Not Verifying](/docs/guides/domain-not-verifying)
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
---
# guides/fix-cors-issues.mdx
Source: https://reloop.sh/docs/guides/fix-cors-issues
Markdown: https://reloop.sh/docs/guides/fix-cors-issues.md
How to resolve CORS errors when calling the Reloop API.
## Overview
This article covers common causes and solutions for this issue when using Reloop.
## Common Causes
- Misconfigured DNS records
- API key permissions not matching the domain
- Rate limiting or account restrictions
## Resolution Steps
Review your domain settings in the [Reloop dashboard](https://reloop.sh/dashboard/domain) and ensure all DNS records are correctly configured.
Ensure your API key has the correct permissions for the operation you're performing. Go to [API Keys](https://reloop.sh/dashboard/api-keys) to review.
If the issue persists, reach out to [Reloop support](https://reloop.sh/help) with your error details and domain information.
## Learn More
- [Domain Not Verifying](/docs/guides/domain-not-verifying)
- [Avoid MX Conflicts](/docs/guides/avoid-mx-conflicts)
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
---
# guides/handling-api-keys.mdx
Source: https://reloop.sh/docs/guides/handling-api-keys
Markdown: https://reloop.sh/docs/guides/handling-api-keys.md
Create keys in the [dashboard](https://reloop.sh/dashboard/api-keys) or with code. Use the secret in your app. Manage with either platform.
## Quick rules
- Copy on create/rotate: secret is not shown again
- Store as `RELOOP_API_KEY`
- Leak? **Disable** → replace → update apps
## Guide
[API Keys](/docs/learn/api-keys): dashboard + code for create, use, rotate, delete.
---
# guides/inbound-forward-emails.mdx
Source: https://reloop.sh/docs/guides/inbound-forward-emails
Markdown: https://reloop.sh/docs/guides/inbound-forward-emails.md
Reloop's inbound email feature can be used to forward received emails to another email address automatically.
## How It Works
1. Reloop receives an email on your verified domain
2. A webhook is triggered with the email data
3. Your webhook handler forwards the email via the Reloop API
## Setup
Go to your [domain settings](https://reloop.sh/dashboard/domain) and enable receiving.
Set up a webhook endpoint that listens for `email.received` events.
In your webhook handler, use the Reloop API to send the email to the forwarding address:
```typescript
const reloop = new Reloop(process.env.RELOOP_API_KEY);
async function handleWebhook(event) {
if (event.type === 'email.received') {
const email = await reloop.emails.receiving.get(event.data.email_id);
await reloop.mail.send({
from: 'forwarding@yourdomain.com',
to: 'destination@example.com',
subject: `Fwd: ${email.data.subject}`,
html: email.data.html,
});
}
}
```
## Learn More
- [Receiving Emails](/docs/guides/receiving-emails)
- [Webhooks](/docs/webhooks)
---
# guides/index.mdx
Source: https://reloop.sh/docs/guides
Markdown: https://reloop.sh/docs/guides.md
Practical guides for Reloop covering domains, DNS setup by provider, deliverability, sending, troubleshooting, and more.
## Domains & DNS
Domain connection docs now live under **[Connect a domain](/docs/guides/connect-domain)** (foundations, Auto-populate, every provider guide, verification, troubleshooting).
- **[Connect a domain](/docs/guides/connect-domain)**: start here
- **[Provider guides](/docs/guides/connect-domain/providers)**: Cloudflare, Vercel, GoDaddy, Route 53, NameSilo, and more
- [Avoid MX Conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts)
- [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying)
## Deliverability
- [Managing the Suppression List](/docs/guides/suppression-list)
- [Receiving Emails](/docs/guides/receiving-emails)
- [Subdomain vs Root Domain](/docs/guides/subdomain-vs-root)
- [Delivered Email Not Arriving](/docs/guides/delivered-not-arriving)
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Sending to Apple Private Relay](/docs/guides/apple-private-relay)
- [Open Rates Not Accurate](/docs/guides/open-rates-accuracy)
- [Audience Hygiene](/docs/guides/audience-hygiene)
- [Warm-up Guide](/docs/guides/warmup-guide)
- [What is Email Consent?](/docs/guides/email-consent)
- [DKIM Key Length](/docs/guides/dkim-key-length)
## Sending
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
- [What Sending Feature to Use?](/docs/guides/sending-feature-guide)
- [Why Use Topics?](/docs/guides/why-use-topics)
- [When to Add an Unsubscribe Link](/docs/guides/unsubscribe-link)
- [How Do Dedicated IPs Work?](/docs/guides/dedicated-ips)
- [Creating an Email Address](/docs/guides/creating-email-address)
- [Turn Off Message Storage](/docs/guides/turn-off-message-storage)
- [Unsupported Attachment Types](/docs/guides/unsupported-attachments)
## Tutorials
- [Supabase Quickstart](/docs/guides/supabase-quickstart)
- [Supabase Go-Live Checklist](/docs/guides/supabase-deliverability)
- [Set Up Your Logo on Apple Mail](/docs/guides/apple-branded-mail)
- [Send with an Avatar](/docs/guides/send-with-avatar)
- [Inbound: Forward Emails](/docs/guides/inbound-forward-emails)
## Troubleshooting
- [Error 1010](/docs/guides/error-1010)
- [Domain Mismatch](/docs/guides/error-domain-mismatch)
- [reloop.sh Domain Error](/docs/guides/error-reloop-dev-domain)
- [Domain Already Registered](/docs/guides/domain-already-registered)
- [Avoid Gmail's Spam Folder](/docs/guides/avoid-gmail-spam)
- [Avoid Outlook's Spam Folder](/docs/guides/avoid-outlook-spam)
- [CORS Issues](/docs/guides/fix-cors-issues)
## Automation Tools
- [n8n Integration](/docs/guides/n8n-integration)
## Account Management
- [Change Your Email Address](/docs/guides/change-email-address)
- [Delete Your Account](/docs/guides/delete-account)
- [Production Approval](/docs/guides/production-approval)
- [Pricing](/docs/guides/pricing)
- [Configuring TLS](/docs/guides/configuring-tls)
- [Handling API Keys](/docs/guides/handling-api-keys)
- [Multi-Tenant Setup](/docs/guides/multi-tenant-setup)
- [Downloading Documents](/docs/guides/downloading-documents)
## Testing
- [E2E Testing with Playwright](/docs/guides/e2e-testing-playwright)
- [Email Addresses for Testing](/docs/guides/testing-email-addresses)
---
# guides/multi-tenant-setup.mdx
Source: https://reloop.sh/docs/guides/multi-tenant-setup
Markdown: https://reloop.sh/docs/guides/multi-tenant-setup.md
How to configure Reloop for multi-tenant applications.
## Overview
Manage your Reloop account settings effectively to ensure smooth operation.
## Steps
Navigate to the [Account Settings](https://reloop.sh/dashboard/settings) page in your Reloop dashboard.
Follow the on-screen instructions to update your account configuration.
Review and confirm your changes. Some changes may require email verification.
If you need assistance with account changes, contact [Reloop support](https://reloop.sh/help).
## Learn More
- [Handling API Keys](/docs/guides/handling-api-keys)
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
---
# guides/n8n-integration.mdx
Source: https://reloop.sh/docs/guides/n8n-integration
Markdown: https://reloop.sh/docs/guides/n8n-integration.md
[n8n](https://n8n.io) is an open-source workflow automation tool. Use the Reloop integration to send emails as part of your automated workflows.
## Setup
In your n8n instance, search for "Reloop" in the nodes panel and install the Reloop integration.
Add your Reloop API key in n8n's credentials settings:
1. Go to **Credentials** → **New Credential**
2. Search for "Reloop"
3. Enter your API key from [reloop.sh/dashboard/api-keys](https://reloop.sh/dashboard/api-keys)
Drag the Reloop node into your workflow and configure the email parameters (from, to, subject, body).
## Example Workflows
- **Form submission → Email notification**: Send an email when a form is submitted
- **CRM trigger → Welcome email**: Send a welcome email when a new contact is created
- **Scheduled reports**: Send daily/weekly reports via email
## Learn More
- [Reloop API Reference](/docs/api)
- [n8n Documentation](https://docs.n8n.io)
---
# guides/open-rates-accuracy.mdx
Source: https://reloop.sh/docs/guides/open-rates-accuracy
Markdown: https://reloop.sh/docs/guides/open-rates-accuracy.md
Why email open rates may not be accurate.
## Overview
Understanding this topic is important for maintaining good email deliverability with Reloop.
## Key Points
- Follow email sending best practices to maintain a strong sender reputation
- Monitor your delivery rates in the [Reloop dashboard](https://reloop.sh/dashboard)
- Keep your email lists clean and up-to-date
## Best Practices
Regularly check your delivery, bounce, and complaint rates in the Reloop dashboard.
Ensure SPF, DKIM, and DMARC are properly configured for your domain.
Always include an unsubscribe mechanism in marketing emails and honor opt-out requests promptly.
For detailed guidance on email deliverability, see our [Email Best Practices](/docs/integrations/agent-skills/email-best-practices) skill documentation.
## Learn More
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Warm-up Guide](/docs/guides/warmup-guide)
- [Audience Hygiene](/docs/guides/audience-hygiene)
- [Domains Guide](/docs/learn/domain)
---
# guides/pricing.mdx
Source: https://reloop.sh/docs/guides/pricing
Markdown: https://reloop.sh/docs/guides/pricing.md
Reloop publishes **one** pricing source of truth: the marketing [pricing page](https://reloop.sh/pricing) and its markdown twin [pricing.md](https://reloop.sh/pricing.md) (generated from `@reloop/pricing`).
This docs page exists so agents and the docs MCP do not invent plans. Do not cite “Essentials $9” or “Free has no daily limit.”
## Reloop Cloud (hosted)
| Plan | Price | Included volume | Daily limit | Overage |
|------|-------|-----------------|-------------|---------|
| Free | $0 | 6,000 emails / month | 200 / day | None (sending pauses) |
| Pro | $10 / month | 50,000 emails / month | None | $0.50 / 1,000 |
| Growth | $20 / month | 100,000 emails / month | None | $0.50 / 1,000 |
| Enterprise | Custom | Custom | None | Custom |
No credit card is required for Free. Data retention on published tiers is 45 days.
## Self-host
Self-hosting Reloop is Apache 2.0 software with Reloop Labs use restrictions. There is **no Reloop SaaS fee** and **no hosted-tier SKU** for running the stack yourself. You pay your own servers, IPs, and operations. See [self-host](https://reloop.sh/self-host) and [license](https://reloop.sh/license).
## Quotas in the product
Plan limits are enforced on Reloop Cloud. Dashboard usage: [Account settings](https://reloop.sh/dashboard/settings). Sending guides: [Account quotas](/docs/guides/account-quotas-limits), [Sending](/docs/learn/emails).
## Learn more
- [pricing.md](https://reloop.sh/pricing.md): canonical agent-readable plans
- [Compare Reloop](https://reloop.sh/compare.md)
- [Handling API Keys](/docs/guides/handling-api-keys)
---
# guides/production-approval.mdx
Source: https://reloop.sh/docs/guides/production-approval
Markdown: https://reloop.sh/docs/guides/production-approval.md
Does Reloop require production approval before sending.
## Overview
Manage your Reloop account settings effectively to ensure smooth operation.
## Steps
Navigate to the [Account Settings](https://reloop.sh/dashboard/settings) page in your Reloop dashboard.
Follow the on-screen instructions to update your account configuration.
Review and confirm your changes. Some changes may require email verification.
If you need assistance with account changes, contact [Reloop support](https://reloop.sh/help).
## Learn More
- [Handling API Keys](/docs/guides/handling-api-keys)
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
---
# guides/receiving-emails.mdx
Source: https://reloop.sh/docs/guides/receiving-emails
Markdown: https://reloop.sh/docs/guides/receiving-emails.md
Reloop receives email for any domain you have added, stores it in a mailbox, threads it by conversation and gives every message a spam verdict. You can then read and reply from the dashboard, your code, an AI agent or a normal mail client.
## Set up receiving
Use a subdomain such as `inbox.yourdomain.com` or `agents.yourdomain.com`. If your root domain already receives mail through Google Workspace or Microsoft 365, pointing its MX at Reloop would split or divert that mail. See [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving) and [MX conflicts](/docs/guides/avoid-mx-conflicts).
Add the domain in the [dashboard](https://reloop.sh/dashboard) or open an existing one. **Email Receiving** is on by default for new domains. You can toggle it on the domain detail page.
At your DNS host, add the MX record Reloop shows for the domain:
| Type | Name | Value | Priority |
| --- | --- | --- | --- |
| `MX` | your subdomain, for example `inbox` | `inbound.reloop.sh` | `10` |
[Auto-populate](/docs/guides/connect-domain/auto-populate) adds it for you on supported DNS hosts.
Create a mailbox for the address, for example `support@inbox.yourdomain.com`, from the domain page or with the API. Mail sent to that address now lands in the mailbox.
Send a message to the address from any personal account. It should appear in the mailbox within a few seconds.
## Read and reply
Choose how your app, agent or team works with received mail:
| Method | Best for | Docs |
| --- | --- | --- |
| **Dashboard** | People reading and replying by hand | [Agent inbox](/docs/learn/agent-inbox) |
| **Webhooks** | Reacting to each new message in your code | [`email.received`](/docs/webhooks/emails/received) |
| **API and SDKs** | Listing threads, fetching messages, replying in a thread | [API reference](/docs/api) |
| **MCP** | Letting Claude, Cursor or another AI client read and answer mail | [MCP server](/docs/integrations/ai-tools/mcp-server) |
| **IMAP** | Apple Mail, Outlook, Thunderbird or any IMAP client | See below |
### Webhooks
Subscribe a webhook to `email.received`. Each event includes the mailbox, sender, subject, `thread_id`, `is_spam` and the `email_id` you use to fetch the full message or reply. Always [verify the signature](/docs/webhooks/verify-webhooks-requests) before trusting the payload.
To forward received mail to another address, see [Forward emails](/docs/guides/inbound-forward-emails).
### IMAP
| Setting | Value |
| --- | --- |
| Host | `imap.reloop.sh` |
| Port | `993` (TLS) |
| Username | The mailbox address, for example `support@inbox.yourdomain.com` |
| Password | A Reloop API key with access to that mailbox |
Changes sync both ways: messages you read, move or archive in your mail client show the same way in the dashboard and the API.
## Troubleshooting
- **Nothing arrives.** Check the MX record with `dig MX inbox.yourdomain.com +short`. It should return `10 inbound.reloop.sh.` DNS changes can take up to an hour to propagate.
- **Mail arrives somewhere else.** Another MX record on the same hostname has a lower priority number. Remove it or use a different subdomain.
- **Mail is marked as spam.** Every message gets a spam verdict. Messages with `is_spam: true` are still stored in the mailbox, so check for flagged messages before assuming mail was lost.
## Learn more
- [Agent inbox](/docs/learn/agent-inbox)
- [Domains](/docs/learn/domain)
- [Webhook event types](/docs/webhooks/event-types)
- [Give an AI agent its own email address](/blog/give-ai-agent-email-address)
---
# guides/send-with-avatar.mdx
Source: https://reloop.sh/docs/guides/send-with-avatar
Markdown: https://reloop.sh/docs/guides/send-with-avatar.md
Some email clients display a sender avatar next to emails. Here's how to set one up for your Reloop sending domain.
## Using Gravatar
The simplest approach is to register your sending email address with [Gravatar](https://gravatar.com):
1. Go to [gravatar.com](https://gravatar.com) and create an account using your sending email address
2. Upload your brand logo or avatar
3. Emails sent from that address will now show the Gravatar in supported clients
## Using Google Workspace
If you use Google Workspace, you can set a profile picture for your sending address:
1. Log in to Google Admin Console
2. Navigate to the user's account settings
3. Upload a profile photo
## Using BIMI
For a more universal approach, see our [Apple Branded Mail guide](/docs/guides/apple-branded-mail) which covers BIMI setup.
Not all email clients support avatars. Gravatar works in many clients, but BIMI provides the widest coverage.
---
# guides/sending-feature-guide.mdx
Source: https://reloop.sh/docs/guides/sending-feature-guide
Markdown: https://reloop.sh/docs/guides/sending-feature-guide.md
Choosing the right sending method for your use case.
## Overview
This guide helps you understand and configure this sending feature in Reloop.
## Details
Understanding your sending configuration is key to successful email delivery. Review the [Reloop dashboard](https://reloop.sh/dashboard) for your current settings and limits.
## Configuration
Check your current plan and usage in the [Reloop dashboard](https://reloop.sh/dashboard/settings).
Adjust your sending configuration based on your requirements and plan limits.
Keep track of your sending volume and adjust as needed to stay within limits.
## Learn More
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
- [Sending Emails](/docs/learn/emails)
- [Email Best Practices](/docs/integrations/agent-skills/email-best-practices)
---
# guides/subdomain-vs-root.mdx
Source: https://reloop.sh/docs/guides/subdomain-vs-root
Markdown: https://reloop.sh/docs/guides/subdomain-vs-root.md
This guide moved to **[What is a domain?](/docs/guides/connect-domain/what-is-a-domain)** under Connect a domain.
---
# guides/supabase-deliverability.mdx
Source: https://reloop.sh/docs/guides/supabase-deliverability
Markdown: https://reloop.sh/docs/guides/supabase-deliverability.md
Before going live with Supabase + Reloop, follow this checklist to ensure your auth emails reach every inbox.
## Checklist
- [ ] **Verify your domain**: Complete DNS verification in the [Reloop dashboard](https://reloop.sh/dashboard/domain).
- [ ] **Use a subdomain**: Send auth emails from a subdomain like `auth.example.com` to protect your root domain reputation.
- [ ] **Set up DMARC**: Add a DMARC record to monitor and protect against spoofing.
- [ ] **Test deliverability**: Send test emails to Gmail, Outlook, and Yahoo to confirm inbox placement.
- [ ] **Warm up your domain**: If you're sending to a large audience, gradually increase volume. See our [Warm-up Guide](/docs/guides/warmup-guide).
- [ ] **Set appropriate rate limits**: Don't send more than your plan allows. See [Account Quotas](/docs/guides/account-quotas-limits).
## Learn More
- [Supabase Quickstart](/docs/guides/supabase-quickstart)
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Warm-up Guide](/docs/guides/warmup-guide)
---
# guides/supabase-quickstart.mdx
Source: https://reloop.sh/docs/guides/supabase-quickstart
Markdown: https://reloop.sh/docs/guides/supabase-quickstart.md
Learn how to set up Reloop as the email provider for your Supabase project.
## Prerequisites
- A [Supabase](https://supabase.com) project
- A [Reloop API key](https://reloop.sh/dashboard/api-keys)
- A [verified domain](https://reloop.sh/dashboard/domain) in Reloop
## Setup
Go to [reloop.sh/dashboard/api-keys](https://reloop.sh/dashboard/api-keys) and create a new API key.
In your Supabase dashboard, go to **Settings → Auth → SMTP Settings** and enable the custom SMTP option.
Configure with the following values:
| Setting | Value |
|---------|-------|
| Host | `smtp.reloop.sh` |
| Port | `587` |
| Username | `reloop` |
| Password | Your Reloop API key |
| Sender email | An address from your verified domain |
Trigger a test email (e.g., a password reset) to confirm everything works.
## Learn More
- [Supabase Go-Live Checklist](/docs/guides/supabase-deliverability)
- [Sending Emails](/docs/learn/emails)
---
# guides/suppression-list.mdx
Source: https://reloop.sh/docs/guides/suppression-list
Markdown: https://reloop.sh/docs/guides/suppression-list.md
How to manage your email suppression list in Reloop.
## Overview
Understanding this topic is important for maintaining good email deliverability with Reloop.
## Key Points
- Follow email sending best practices to maintain a strong sender reputation
- Monitor your delivery rates in the [Reloop dashboard](https://reloop.sh/dashboard)
- Keep your email lists clean and up-to-date
## Best Practices
Regularly check your delivery, bounce, and complaint rates in the Reloop dashboard.
Ensure SPF, DKIM, and DMARC are properly configured for your domain.
Always include an unsubscribe mechanism in marketing emails and honor opt-out requests promptly.
For detailed guidance on email deliverability, see our [Email Best Practices](/docs/integrations/agent-skills/email-best-practices) skill documentation.
## Learn More
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Warm-up Guide](/docs/guides/warmup-guide)
- [Audience Hygiene](/docs/guides/audience-hygiene)
- [Domains Guide](/docs/learn/domain)
---
# guides/testing-email-addresses.mdx
Source: https://reloop.sh/docs/guides/testing-email-addresses
Markdown: https://reloop.sh/docs/guides/testing-email-addresses.md
When testing your Reloop integration, use these special email addresses to simulate different scenarios without sending real emails.
## First test after API key (onboarding domain)
During onboarding, after you generate an API key, use **Send email**. Reloop sends a message from the onboarding test domain:
| Field | Value |
|-------|--------|
| From | `onboarding@{ONBOARDING_TEST_DOMAIN}` (not `RELOOP_SENDER_DOMAIN`) |
| To | Your account email only |
You do **not** need a verified customer domain for this path. For production From addresses, add and verify your own domain.
## Simulated recipient addresses
| Address | Behavior |
|---------|----------|
| `delivered@reloop.sh` | Simulates a successful delivery |
| `bounced@reloop.sh` | Simulates a hard bounce |
| `complained@reloop.sh` | Simulates a spam complaint |
## Development tips
- Use the onboarding button or your own addresses for real inbox checks
- Use the simulated addresses above for automated tests that should not hit real inboxes
- Check the [Reloop dashboard](https://reloop.sh/dashboard/emails) for send status
Never use real customer email addresses for automated load or bounce testing. Prefer the simulated addresses above, or a dedicated inbox you control.
## Learn More
- [E2E Testing with Playwright](/docs/guides/e2e-testing-playwright)
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
- [reloop.sh Domain Error](/docs/guides/error-reloop-dev-domain)
---
# guides/turn-off-message-storage.mdx
Source: https://reloop.sh/docs/guides/turn-off-message-storage
Markdown: https://reloop.sh/docs/guides/turn-off-message-storage.md
How to disable email content storage for sensitive data.
## Overview
This guide helps you understand and configure this sending feature in Reloop.
## Details
Understanding your sending configuration is key to successful email delivery. Review the [Reloop dashboard](https://reloop.sh/dashboard) for your current settings and limits.
## Configuration
Check your current plan and usage in the [Reloop dashboard](https://reloop.sh/dashboard/settings).
Adjust your sending configuration based on your requirements and plan limits.
Keep track of your sending volume and adjust as needed to stay within limits.
## Learn More
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
- [Sending Emails](/docs/learn/emails)
- [Email Best Practices](/docs/integrations/agent-skills/email-best-practices)
---
# guides/unsubscribe-link.mdx
Source: https://reloop.sh/docs/guides/unsubscribe-link
Markdown: https://reloop.sh/docs/guides/unsubscribe-link.md
Guidelines for including unsubscribe links in your emails.
## Overview
This guide helps you understand and configure this sending feature in Reloop.
## Details
Understanding your sending configuration is key to successful email delivery. Review the [Reloop dashboard](https://reloop.sh/dashboard) for your current settings and limits.
## Configuration
Check your current plan and usage in the [Reloop dashboard](https://reloop.sh/dashboard/settings).
Adjust your sending configuration based on your requirements and plan limits.
Keep track of your sending volume and adjust as needed to stay within limits.
## Learn More
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
- [Sending Emails](/docs/learn/emails)
- [Email Best Practices](/docs/integrations/agent-skills/email-best-practices)
---
# guides/unsupported-attachments.mdx
Source: https://reloop.sh/docs/guides/unsupported-attachments
Markdown: https://reloop.sh/docs/guides/unsupported-attachments.md
Which file types are not supported as email attachments.
## Overview
This guide helps you understand and configure this sending feature in Reloop.
## Details
Understanding your sending configuration is key to successful email delivery. Review the [Reloop dashboard](https://reloop.sh/dashboard) for your current settings and limits.
## Configuration
Check your current plan and usage in the [Reloop dashboard](https://reloop.sh/dashboard/settings).
Adjust your sending configuration based on your requirements and plan limits.
Keep track of your sending volume and adjust as needed to stay within limits.
## Learn More
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
- [Sending Emails](/docs/learn/emails)
- [Email Best Practices](/docs/integrations/agent-skills/email-best-practices)
---
# guides/warmup-guide.mdx
Source: https://reloop.sh/docs/guides/warmup-guide
Markdown: https://reloop.sh/docs/guides/warmup-guide.md
How to warm up a new sending domain for optimal deliverability.
## Overview
Understanding this topic is important for maintaining good email deliverability with Reloop.
## Key Points
- Follow email sending best practices to maintain a strong sender reputation
- Monitor your delivery rates in the [Reloop dashboard](https://reloop.sh/dashboard)
- Keep your email lists clean and up-to-date
## Best Practices
Regularly check your delivery, bounce, and complaint rates in the Reloop dashboard.
Ensure SPF, DKIM, and DMARC are properly configured for your domain.
Always include an unsubscribe mechanism in marketing emails and honor opt-out requests promptly.
For detailed guidance on email deliverability, see our [Email Best Practices](/docs/integrations/agent-skills/email-best-practices) skill documentation.
## Learn More
- [Emails Going to Spam](/docs/guides/emails-going-to-spam)
- [Warm-up Guide](/docs/guides/warmup-guide)
- [Audience Hygiene](/docs/guides/audience-hygiene)
- [Domains Guide](/docs/learn/domain)
---
# guides/why-use-topics.mdx
Source: https://reloop.sh/docs/guides/why-use-topics
Markdown: https://reloop.sh/docs/guides/why-use-topics.md
Benefits of using topics to organize your email sending.
## Overview
This guide helps you understand and configure this sending feature in Reloop.
## Details
Understanding your sending configuration is key to successful email delivery. Review the [Reloop dashboard](https://reloop.sh/dashboard) for your current settings and limits.
## Configuration
Check your current plan and usage in the [Reloop dashboard](https://reloop.sh/dashboard/settings).
Adjust your sending configuration based on your requirements and plan limits.
Keep track of your sending volume and adjust as needed to stay within limits.
## Learn More
- [Account Quotas and Limits](/docs/guides/account-quotas-limits)
- [Sending Emails](/docs/learn/emails)
- [Email Best Practices](/docs/integrations/agent-skills/email-best-practices)
---
# integrations/bolt.mdx
Source: https://reloop.sh/docs/integrations/bolt
Markdown: https://reloop.sh/docs/integrations/bolt.md
[Bolt.new](https://bolt.new) allows you to edit, run, and deploy full-stack web applications entirely in your browser using AI. Adding Reloop support takes only two steps.
---
## Step 1: Set Environment Variables in Bolt.new
Inside your Bolt.new chat or the terminal panel:
1. Create or open the `.env` file at the root of the project.
2. Add your Reloop API key:
```env
RELOOP_API_KEY=rl_your_api_key_here
```
3. Alternatively, prompt the AI agent:
> "Configure my `.env` variables to include `RELOOP_API_KEY`."
---
## Step 2: Prompt Bolt.new for Email Dispatch
Ask the Bolt AI assistant to add the email logic:
> "Install `reloop-email` and create a server endpoint `api/send-email` that sends a transactional email when a user registers."
---
# integrations/cloudflare.mdx
Source: https://reloop.sh/docs/integrations/cloudflare
Markdown: https://reloop.sh/docs/integrations/cloudflare.md
[Cloudflare Workers](https://workers.cloudflare.com) allow you to run serverless code globally. Since standard TCP sockets can be limited in edge environments, routing emails through the Reloop HTTP API is the recommended approach.
---
## Step 1: Bind Secret in Wrangler
To add your Reloop API key to your Cloudflare Worker environment securely, run the following command in your terminal:
```bash
wrangler secret put RELOOP_API_KEY
```
When prompted, input your Reloop API key starting with `rl_`.
---
## Step 2: Send Email via Fetch in Cloudflare Workers
Send emails with a standard POST request inside your Worker:
```typescript
async fetch(request, env) {
const response = await fetch('https://api.reloop.sh/v1/emails', {
method: 'POST',
headers: {
'Authorization': `Bearer ${env.RELOOP_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: 'alerts@yourdomain.com',
to: 'user@example.com',
subject: 'Edge Alert',
html: 'Sent from the edge via Reloop!
',
}),
});
return response;
}
};
```
---
# integrations/coolify.mdx
Source: https://reloop.sh/docs/integrations/coolify
Markdown: https://reloop.sh/docs/integrations/coolify.md
Point Coolify at Reloop SMTP. The same API key sends Coolify's password resets and alerts, and mail from the apps you deploy there.
Create the key in the [Reloop dashboard](https://reloop.sh/dashboard/api-keys). The address you send from has to be on a [verified domain](https://reloop.sh/dashboard/domain).
## Coolify's own email
In Coolify, go to **Settings → Email**.

Turn SMTP on and use these values. The username and the password are both your API key.
| Field | Value |
| :--- | :--- |
| **Host** | `smtp.reloop.sh` |
| **Port** | `587` |
| **Encryption** | `StartTLS` |
| **SMTP username** | Your API key (`rl_...`) |
| **SMTP password** | The same API key |
| **From address** | `hello@yourdomain.com` |
| **From name** | `Coolify` |
Click **Save**, then send the test from the same page. The message shows up in your [Reloop logs](https://reloop.sh/dashboard).
## Apps you deploy on Coolify
Open the application, go to **Environment Variables**, and add the key as a secret.
```bash
RELOOP_API_KEY=rl_your_api_key
```
Save and redeploy so the app picks up the key. Send with the [SMTP relay](/docs/examples/smtp/introduction) or an [SDK](/docs).
To run Reloop itself on Coolify, follow [Deploy on Coolify](/docs/self-host/coolify).
---
# integrations/cursor.mdx
Source: https://reloop.sh/docs/integrations/cursor
Markdown: https://reloop.sh/docs/integrations/cursor.md
[Cursor](https://cursor.com) is an AI-first code editor. Connect it to the Reloop MCP server and the Cursor agent can send test emails, look up contacts, check domains, and manage templates directly from the chat window.
## Connect Cursor
Add the hosted server and sign in with your Reloop account. No API key needed.
```json
{
"mcpServers": {
"reloop": {
"url": "https://reloop.sh/mcp"
}
}
}
```
Put this in `.cursor/mcp.json`, then click **Connect** next to reloop in **Cursor Settings → MCP & Integrations**.
One-click install, sign-in, verifying the connection, and troubleshooting.
## Use it in chat
In the Cursor chat panel, try:
> "Send a test email to customer@example.com saying 'Hello from Cursor!' using Reloop."
See [example prompts](/docs/integrations/ai-tools/mcp-prompts) for more, and the [MCP server overview](/docs/integrations/ai-tools/mcp-server) for everything the server can do.
---
# integrations/directus.mdx
Source: https://reloop.sh/docs/integrations/directus
Markdown: https://reloop.sh/docs/integrations/directus.md
[Directus](https://directus.io) is a highly flexible, open-source data platform and headless CMS. By default, Directus does not send system emails (like password resets, user invitations, or activity notifications) unless an email transport is configured.
Connecting Directus with Reloop takes less than two minutes and is configured entirely through your `.env` configuration file.
---
## Prerequisites
Before starting, make sure you have:
1. A verified sending domain in your [Reloop Dashboard](https://reloop.sh/dashboard).
2. A Reloop API Key (starts with `rl_`).
---
## Step 1: Configure SMTP in `.env`
Open the `.env` file in the root of your Directus project and locate the email configuration variables. Add or update the values to match the Reloop SMTP credentials below:
```env
# Email General Settings
EMAIL_FROM="notifications@yourdomain.com"
EMAIL_TRANSPORT="smtp"
# Reloop SMTP Configuration
EMAIL_SMTP_HOST="smtp.reloop.sh"
EMAIL_SMTP_PORT=587
EMAIL_SMTP_USER="YOUR_RELOOP_API_KEY"
EMAIL_SMTP_PASSWORD="YOUR_RELOOP_API_KEY"
EMAIL_SMTP_SECURE="false"
EMAIL_SMTP_IGNORE_TLS="false"
```
> [!IMPORTANT]
> - Ensure `EMAIL_FROM` matches a verified email address from your Reloop sending domain.
> - `EMAIL_SMTP_SECURE` must be set to `false`. Reloop uses port `587` with standard `STARTTLS` encryption (which is initiated automatically after connection). Setting this to `true` forces SSL on connection (SMLTPS on port 465) and will fail.
---
## Step 2: Restart Directus
To apply the configuration changes, restart your Directus service.
If you are running Directus locally or via Node:
```bash
npx directus start
```
If you are using Docker/Docker Compose:
```bash
docker compose restart directus
```
---
## Step 3: Test Email Delivery
1. Log in to the Directus Admin Panel.
2. Go to **Settings** → **User Directory** and invite a new user.
3. Check the inbox of the invited user to confirm receipt of the invite email.
4. Verify the delivery metrics and logs inside the **Reloop Dashboard**.
---
# integrations/docker.mdx
Source: https://reloop.sh/docs/integrations/docker
Markdown: https://reloop.sh/docs/integrations/docker.md
[Docker](https://docker.com) simplifies shipping applications. Configure your containerized services to connect to Reloop's SMTP or API endpoints.
---
## Step 1: Add Credentials to Docker Compose
Add the credentials under the `environment` section in your `docker-compose.yml` file:
```yaml
version: '3.8'
services:
app:
image: node:18
environment:
- RELOOP_API_KEY=rl_your_api_key_here
- SMTP_HOST=smtp.reloop.sh
- SMTP_PORT=587
- SMTP_USER=reloop
- SMTP_PASS=rl_your_api_key_here
ports:
- "3000:3000"
command: npm start
```
---
## Step 2: Use credentials in your container
Your application inside the container can now access the environment variables naturally (e.g. `process.env.RELOOP_API_KEY` in Node.js or `os.environ.get('RELOOP_API_KEY')` in Python).
---
# integrations/ghost.mdx
Source: https://reloop.sh/docs/integrations/ghost
Markdown: https://reloop.sh/docs/integrations/ghost.md
[Ghost](https://ghost.org) is a popular open-source publishing platform. While Ghost uses Mailgun for sending bulk newsletters, it uses a standard SMTP configuration for **transactional emails** (such as member login links, password resets, signup notifications, and staff invitations).
Configuring Ghost to route these emails through Reloop ensures that your login links and administrative emails arrive in the inbox instantly.
---
## Prerequisites
Before starting, make sure you have:
1. A verified sending domain in your [Reloop Dashboard](https://reloop.sh/dashboard).
2. A Reloop API Key (starts with `rl_`).
---
## Step 1: Locate your Ghost Configuration File
In self-hosted Ghost environments, the configuration is stored in a JSON file in the root of your Ghost installation directory.
- Production file: `config.production.json`
- Development/Local file: `config.development.json`
---
## Step 2: Edit Mail Configuration
Open your configuration file in a text editor and locate or add the `"mail"` block. Replace the default settings with your Reloop SMTP credentials:
```json
{
"url": "https://yourdomain.com",
"database": { ... },
"mail": {
"transport": "SMTP",
"options": {
"host": "smtp.reloop.sh",
"port": 587,
"auth": {
"user": "YOUR_RELOOP_API_KEY",
"pass": "YOUR_RELOOP_API_KEY"
}
}
}
}
```
> [!NOTE]
> Make sure to replace `YOUR_RELOOP_API_KEY` with your actual Reloop API Key. Both the `user` and `pass` values should be set to your Reloop API Key.
---
## Step 3: Restart Ghost
For the email changes to take effect, you need to restart the Ghost process:
```bash
ghost restart
```
If you are running Ghost in a Docker container, restart the container:
```bash
docker restart ghost_container_name
```
---
## Step 3: Verify the Integration
1. Go to your Ghost admin panel (`https://yourdomain.com/ghost/`).
2. Go to **Settings** → **Staff** and invite a new staff member or send a test email.
3. Check the inbox of the invited user to confirm receipt of the invitation link.
4. Verify the delivery status and headers in your **Reloop Dashboard Logs**.
---
# integrations/index.mdx
Source: https://reloop.sh/docs/integrations
Markdown: https://reloop.sh/docs/integrations.md
# Integrations
Reloop integrates seamlessly with the hosting platforms, databases, AI frameworks, and developer tools you already use.
## Hosting & Cloud Platforms
Deploy and run Reloop or route emails directly from your cloud providers.
Deploy frontend applications and route transactional emails.
Read Guide →
Host your Reloop instance or run your backends.
Read Guide →
Deliver emails globally using Cloudflare Workers and Pages.
Read Guide →
Connect your Netlify builds and serverless functions.
Read Guide →
Run Reloop services locally or in production containers.
Read Guide →
Send Coolify's own mail, and mail from apps on Coolify, through Reloop.
Read Guide →
## AI App Builders
Integrate Reloop email sending and inbox automation into AI-generated applications.
Build full-stack apps with natural language using Reloop SMTP or REST APIs.
Read Guide →
Integrate Reloop into full-stack web applications generated in the browser.
Read Guide →
Integrate Reloop email templates and APIs into AI-generated React/Next.js components.
Read Guide →
Deploy Reloop-integrated apps and agent inboxes instantly on Replit Repls.
Read Guide →
## AI & Agent Developer Tools
Build intelligent agents that send and receive email autonomously.
Connect any MCP client to the hosted server at reloop.sh/mcp and sign in with Reloop.
Read Guide →
Add Reloop to Claude Code with one command.
Read Guide →
Add Reloop as a custom connector on the web or in the desktop app.
Read Guide →
Use Cursor's agent and chat with Reloop's MCP server.
Read Guide →
Use Reloop from GitHub Copilot agent mode.
Read Guide →
Add Reloop to the Codex CLI and IDE extension.
Read Guide →
Code alongside the Windsurf agent with full monorepo and API context.
Read Guide →
## No-Code & Automation Tools
Integrate Reloop email sending into your automated workflows, databases, and websites.
Automate workflows and send emails using Reloop SMTP or REST APIs.
Read Guide →
Integrate Reloop email sending into your Make scenarios.
Read Guide →
Connect Reloop with thousands of apps via Zapier Zaps.
Read Guide →
Route your WordPress site's emails through Reloop SMTP.
Read Guide →
Configure Ghost CMS to send newsletter signups and transactional emails.
Read Guide →
Configure the Strapi headless CMS to send emails via Reloop SMTP.
Read Guide →
Configure Directus CMS via environment variables to send system emails.
Read Guide →
Configure Supabase authentication and database emails via Reloop SMTP.
Read Guide →
---
# integrations/lovable.mdx
Source: https://reloop.sh/docs/integrations/lovable
Markdown: https://reloop.sh/docs/integrations/lovable.md
[Lovable](https://lovable.dev) is a powerful AI builder that generates full-stack web applications from natural language. You can easily prompt Lovable to integrate Reloop SMTP or REST APIs to handle email communications.
---
## Step 1: Provide Reloop Credentials to Lovable
To let your Lovable-generated backend send emails:
1. Copy your Reloop API key from the Reloop dashboard.
2. In the Lovable chat panel, configure the environment variables by prompting:
> "Add `RELOOP_API_KEY` to the environment variables with value `rl_your_key`."
3. Prompt Lovable to configure SMTP settings if using SMTP:
- Host: `smtp.reloop.sh`
- Port: `587`
- User: `reloop`
- Pass: `rl_your_key`
---
## Step 2: Prompt Lovable to write Reloop logic
You can ask Lovable to write code to send verification or transactional emails using the SDK or API:
> "Create a contact form submission handler that sends an email to admin@mydomain.com using the Reloop API key."
---
# integrations/make.mdx
Source: https://reloop.sh/docs/integrations/make
Markdown: https://reloop.sh/docs/integrations/make.md
[Make.com](https://make.com) (formerly Integromat) is a powerful visual automation platform. You can connect Reloop to any Make scenario using either:
1. **SMTP Connection** (using Make's built-in **Email** app)
2. **REST API Connection** (using Make's built-in **HTTP** app)
---
## Method 1: SMTP via Make's Email App
The standard **Email** app in Make allows you to send transactional messages instantly using Reloop SMTP.
### Step 1: Add the Email Module
1. In your Make scenario, click **Add module** (+).
2. Search for and select the **Email** app.
3. Select the **Send an email** module.
### Step 2: Create a Connection
1. Click **Add** next to the **Connection** drop-down.
2. Choose **Other SMTP server** as your connection type.
3. Configure the connection settings:
- **Connection name**: `Reloop SMTP`
- **SMTP server**: `smtp.reloop.sh`
- **Port**: `587`
- **Use secure connection**: `TLS` or `STARTTLS` (recommended)
- **Username**: *Your Reloop API Key* (starts with `rl_`)
- **Password**: *Your Reloop API Key* (starts with `rl_`)
4. Click **Save**.
### Step 3: Map Email Details
Configure the fields inside the Email module:
- **To**: Add recipients (use dynamic values from previous nodes, e.g., CRM contacts).
- **Subject**: Enter a subject line.
- **Content**: Choose `HTML` or `Plain Text` and compose your message body.
- **Sender**: Click **Show advanced settings** and enter a verified sender email address from your Reloop dashboard (e.g., `sales@yourdomain.com`).
---
## Method 2: REST API via Make's HTTP App
If you want to use advanced features like email templates, tagging, or attachment files from Google Drive or Dropbox, you can call the Reloop API directly.
### Step 1: Add the HTTP Module
1. In your Make scenario, click **Add module**.
2. Search for and select the **HTTP** app.
3. Select the **Make a request** module.
### Step 2: Configure the API Request
Configure the module fields as follows:
- **URL**: `https://reloop.sh/api/mail/v1/send`
- **Method**: `POST`
- **Headers**:
- Item 1:
- **Name**: `x-api-key`
- **Value**: *Your Reloop API Key* (starts with `rl_`)
- Item 2:
- **Name**: `Content-Type`
- **Value**: `application/json`
- **Body type**: `Raw`
- **Content type**: `JSON (application/json)`
- **Request content**: Compose the JSON payload. For example:
```json
{
"from": "Support Team ",
"to": "{{1.email}}",
"subject": "Ticket Resolved",
"html": "Hi {{1.name}}, your support ticket has been closed.
",
"tags": [
{
"name": "type",
"value": "transactional"
}
]
}
```
*Note: Replace `{{1.email}}` and `{{1.name}}` with mapping variables from previous nodes in your Make scenario.*
- **Parse response**: `Yes` (this allows subsequent nodes to easily reference the `messageId` and status).
---
## Step 3: Run the Scenario
Click **Run once** at the bottom left of the Make dashboard to test. Verify the execution bubbles show green checkboxes, check the recipient's inbox, and inspect the delivery logs in the **Reloop Dashboard**.
---
# integrations/n8n.mdx
Source: https://reloop.sh/docs/integrations/n8n
Markdown: https://reloop.sh/docs/integrations/n8n.md
[n8n](https://n8n.io) is a fair-code, extendable workflow automation tool. You can connect Reloop to your n8n workflows using either:
1. **SMTP Connection** (using n8n's built-in **Send Email** node)
2. **REST API Connection** (using n8n's built-in **HTTP Request** node)
---
## Method 1: SMTP via n8n's Send Email Node
You can route emails directly using n8n's native **Send Email** node configured with Reloop's SMTP credentials.
### Step 1: Add the Send Email Node
1. Inside your n8n workflow editor, click the **+** icon to add a node.
2. Search for and select the **Send Email** node.
### Step 2: Configure Credentials
1. Under **Credential for Send Email**, select **Create New Credential**.
2. Set the connection settings as follows:
| Parameter | Value |
| :--- | :--- |
| **Host** | `smtp.reloop.sh` |
| **Port** | `587` |
| **SSL/TLS** | `STARTTLS` (enabled via toggle / selection) |
| **User** | *Your Reloop API Key* (starts with `rl_`) |
| **Password** | *Your Reloop API Key* (starts with `rl_`) |
3. Save the credential.
### Step 3: Set Email Parameters
Configure the email body and headers:
- **From Email**: Enter a verified email address from your Reloop sending domain (e.g., `app@yourdomain.com`).
- **To Email**: Map the recipient's email address dynamically from prior nodes.
- **Subject**: Enter a subject line.
- **HTML / Text**: Choose your email body format and insert your content.
---
## Method 2: REST API via HTTP Request Node
Use n8n's **HTTP Request** node to access advanced API features such as templating, tag tracking, attachments, or scheduling.
### Step 1: Add the HTTP Request Node
1. Click the **+** icon to add a new node.
2. Search for and select **HTTP Request**.
### Step 2: Configure Node Parameters
Configure the node parameters for the Reloop Send Mail API:
- **Method**: `POST`
- **URL**: `https://reloop.sh/api/mail/v1/send`
- **Authentication**: `Header Auth` (or choose None and add it manually in Headers)
- **Headers**:
- Name: `x-api-key`
- Value: *Your Reloop API Key*
- Name: `Content-Type`
- Value: `application/json`
- **Specify Body**: `Using JSON`
- **Body Parameters**: Toggle to **JSON** format and enter the payload:
```json
{
"from": "Marketing ",
"to": "={{ $json.email }}",
"subject": "Thank you for subscribing!",
"html": "Welcome {{ $json.name }}!
We are glad to have you aboard.
",
"tags": [
{
"name": "campaign",
"value": "onboarding_n8n"
}
]
}
```
*Note: Use n8n's expression syntax `={{ ... }}` to pull data from your webhook or database triggers.*
---
## Step 3: Test and Verify
1. Click **Test step** or **Execute Node** in n8n.
2. Inspect the JSON response to ensure it returns a success confirmation:
```json
{
"success": true,
"messageId": "msg_8d8b9c2a1e...",
"status": "sent"
}
```
3. Check your recipient inbox and check the **Reloop Dashboard Logs** to confirm the email was delivered successfully.
---
# integrations/netlify.mdx
Source: https://reloop.sh/docs/integrations/netlify
Markdown: https://reloop.sh/docs/integrations/netlify.md
[Netlify](https://netlify.com) makes deploying frontends and serverless functions effortless. Follow these steps to route emails via Reloop from your Netlify-deployed applications.
---
## Step 1: Configure Environment Variables
1. Go to your **Netlify Team Dashboard**.
2. Select your project and navigate to **Site configuration** → **Environment variables**.
3. Click **Add a variable** and choose **Add single variable**.
4. Configure the variable:
- **Key**: `RELOOP_API_KEY`
- **Value**: `rl_your_api_key`
5. Click **Create variable**.
---
## Step 2: Use in Netlify Functions
Retrieve the variable inside a Netlify Serverless Function:
```typescript
const reloop = new Reloop(process.env.RELOOP_API_KEY);
const result = await reloop.mail.send({
from: 'hello@yourdomain.com',
to: 'customer@example.com',
subject: 'Netlify Alert',
html: 'Sent from a Netlify Serverless Function!
',
});
return {
statusCode: 200,
body: JSON.stringify(result),
};
};
```
---
# integrations/railway.mdx
Source: https://reloop.sh/docs/integrations/railway
Markdown: https://reloop.sh/docs/integrations/railway.md
[Railway](https://railway.app) simplifies hosting backends, databases, and cron jobs. Configure your Railway services to send emails via Reloop with environment variables.
---
## Step 1: Set Environment Variables in Railway
1. Go to your **Railway Project**.
2. Select the service that will send emails.
3. Click on the **Variables** tab.
4. Add the following variable:
- **Name**: `RELOOP_API_KEY`
- **Value**: `rl_your_api_key`
5. If using standard SMTP connections, also configure:
- **Name**: `SMTP_HOST` → `smtp.reloop.sh`
- **Name**: `SMTP_PORT` → `587`
- **Name**: `SMTP_USER` → `reloop`
- **Name**: `SMTP_PASS` → `rl_your_api_key`
Railway will automatically redeploy your service to apply the variables.
---
## Step 2: Code Integration
Retrieve the environment variables in your server code:
```typescript
const apiKey = process.env.RELOOP_API_KEY;
```
---
# integrations/replit.mdx
Source: https://reloop.sh/docs/integrations/replit
Markdown: https://reloop.sh/docs/integrations/replit.md
[Replit](https://replit.com) is an online IDE and hosting service. Follow these steps to configure your Replit Repl to send emails via Reloop.
---
## Step 1: Add API Key to Replit Secrets
1. Open your Repl workspace on **Replit**.
2. Click on the **Secrets** tool in the left sidebar (represented by a lock icon).
3. Add a new secret:
- **Key**: `RELOOP_API_KEY`
- **Value**: `rl_your_api_key_here`
4. Click **Add Secret**.
Replit automatically injects secrets as environment variables.
---
## Step 2: Write Email Dispatch Logic
Send an email in Node.js on Replit:
```javascript
const reloop = new Reloop(process.env.RELOOP_API_KEY);
reloop.mail.send({
from: 'hello@yourdomain.com',
to: 'friend@example.com',
subject: 'Greetings from Replit',
html: 'This was sent from my online Repl!
'
});
```
---
# integrations/strapi.mdx
Source: https://reloop.sh/docs/integrations/strapi
Markdown: https://reloop.sh/docs/integrations/strapi.md
[Strapi](https://strapi.io) is a leading open-source headless CMS. Out of the box, Strapi provides a mock email sender. To send real transactional emails (like user password resets, email address confirmations, or custom workflow triggers), you must configure an email provider.
Reloop SMTP integrates seamlessly with Strapi using the standard `@strapi/provider-email-nodemailer` package.
---
## Prerequisites
Before starting, make sure you have:
1. A verified sending domain in your [Reloop Dashboard](https://reloop.sh/dashboard).
2. A Reloop API Key (starts with `rl_`).
---
## Step 1: Install Nodemailer Provider
Run the following command in your Strapi project root folder to install the official Nodemailer email provider:
```package-install
npm install @strapi/provider-email-nodemailer
```
---
## Step 2: Configure the Plugin
Create or edit your Strapi plugin configuration file.
- File path: `config/plugins.js` (or `config/plugins.ts` for TypeScript projects)
Add the `email` provider configuration block:
```javascript
module.exports = ({ env }) => ({
// ... existing plugins
email: {
config: {
provider: 'nodemailer',
providerOptions: {
host: 'smtp.reloop.sh',
port: 587,
auth: {
user: env('RELOOP_API_KEY'),
pass: env('RELOOP_API_KEY'),
},
},
settings: {
defaultFrom: env('RELOOP_FROM_EMAIL', 'hello@yourdomain.com'),
defaultReplyTo: env('RELOOP_REPLY_TO_EMAIL', 'hello@yourdomain.com'),
},
},
},
});
```
---
## Step 3: Set Environment Variables
Add the required environment variables to your `.env` file:
```env
RELOOP_API_KEY="your-reloop-api-key"
RELOOP_FROM_EMAIL="sender@yourdomain.com"
RELOOP_REPLY_TO_EMAIL="sender@yourdomain.com"
```
> [!IMPORTANT]
> Make sure `RELOOP_FROM_EMAIL` is a verified email address from your Reloop sending domain.
---
## Step 4: Verify the Integration
1. Restart your Strapi application (`npm run develop` or `npm run build && npm run start`).
2. Log in to the Strapi Admin Panel.
3. Trigger a password reset email or go to **Settings** → **Email Settings** to send a test message.
4. Verify receipt of the email and check delivery metrics in your **Reloop Dashboard**.
---
# integrations/v0.mdx
Source: https://reloop.sh/docs/integrations/v0
Markdown: https://reloop.sh/docs/integrations/v0.md
[v0 by Vercel](https://v0.dev) is a generative UI system that creates production-ready frontend code. To connect v0 components to a backend that sends emails with Reloop:
---
## Step 1: Prompt v0 for Frontend UI
Generate your components in v0:
> "Generate a beautiful contact form component that sends form data to a Next.js App Router API route at `/api/send`."
Copy the generated code into your Next.js project.
---
## Step 2: Build the API Route with Reloop
In your Next.js project, create `/app/api/send/route.ts` to process the form using Reloop:
```typescript
const reloop = new Reloop(process.env.RELOOP_API_KEY);
const { name, email, message } = await req.json();
const data = await reloop.mail.send({
from: 'support@yourdomain.com',
to: 'info@yourdomain.com',
subject: `New form submission from ${name}`,
html: `Name: ${name}
Email: ${email}
Message: ${message}
`,
});
return NextResponse.json(data);
}
```
---
# integrations/vercel.mdx
Source: https://reloop.sh/docs/integrations/vercel
Markdown: https://reloop.sh/docs/integrations/vercel.md
[Vercel](https://vercel.com) is the premier cloud platform for frontends and serverless functions. Integrating Reloop with your Vercel-deployed applications takes just a few steps.
---
## Step 1: Configure Vercel Environment Variables
To allow your serverless functions or Next.js API routes to authenticate with Reloop, configure your API key in the Vercel dashboard:
1. Go to your project on the **Vercel Dashboard**.
2. Navigate to **Settings** → **Environment Variables**.
3. Add a new variable:
- **Key**: `RELOOP_API_KEY`
- **Value**: `YOUR_RELOOP_API_KEY` (starts with `rl_`)
4. Click **Save**.
---
## Step 2: Deploy and Route Emails
Use the official Node.js SDK to send emails in your serverless code:
```typescript
const reloop = new Reloop(process.env.RELOOP_API_KEY);
const data = await reloop.mail.send({
from: 'onboarding@yourdomain.com',
to: 'user@example.com',
subject: 'Welcome!',
html: 'Thanks for signing up!
',
});
}
```
---
# integrations/windsurf.mdx
Source: https://reloop.sh/docs/integrations/windsurf
Markdown: https://reloop.sh/docs/integrations/windsurf.md
[Windsurf](https://codeium.com/windsurf) is an agentic IDE built by Codeium. You can register Reloop's MCP server to grant the Windsurf agent capability to dispatch emails and interact with the Reloop platform.
---
## Step 1: Add Reloop MCP to Windsurf Config
1. Open Windsurf and navigate to **Settings** → **Developer Tools** → **MCP**.
2. Click **Add Server**.
3. Input the following configurations:
- **Name**: `reloop`
- **Transport**: `STDIO`
- **Command**: `npx`
- **Arguments**: `-y`, `reloop-mcp`
4. Set the environment variable:
- `RELOOP_API_KEY`: `rl_your_api_key`
5. Save the configuration.
---
## Step 2: Query the Agent
In the Windsurf chat or agent panel, prompt the agent:
> "Send an onboarding email to contact@example.com using the Reloop MCP tool."
---
# integrations/wordpress.mdx
Source: https://reloop.sh/docs/integrations/wordpress
Markdown: https://reloop.sh/docs/integrations/wordpress.md
By default, WordPress sends transactional emails (like password resets, user registrations, and order notifications) using PHP's built-in `mail()` function. These emails often lack authentication and end up in the spam folder.
Using **Reloop SMTP** ensures your WordPress emails are fully authenticated with SPF, DKIM, and DMARC, leading to high inbox delivery rates.
---
## Prerequisites
Before starting, make sure you have:
1. A verified sending domain in your [Reloop Dashboard](https://reloop.sh/dashboard).
2. A Reloop API Key (which acts as your SMTP username and password).
---
## Step 1: Install an SMTP Plugin
To route WordPress emails through Reloop, you need to install an SMTP plugin. We recommend **WP Mail SMTP** (the most popular free option), but any standard SMTP plugin will work.
1. Log in to your WordPress Admin Dashboard.
2. Go to **Plugins** → **Add New**.
3. Search for **WP Mail SMTP**.
4. Click **Install Now** and then **Activate**.
---
## Step 2: Configure SMTP Settings
1. In your WordPress dashboard, navigate to **WP Mail SMTP** → **Settings**.
2. Under the **General** tab, configure the following:
- **From Email**: Enter a verified email address from your Reloop sending domain (e.g., `info@yourdomain.com`). Check the **Force From Email** box to ensure consistency.
- **From Name**: The sender name you want your recipients to see.
3. Scroll down to the **Mailer** section and select **Other SMTP**.

4. Enter the Reloop SMTP credentials under the **Other SMTP** settings:
| Setting | Value |
| :--- | :--- |
| **SMTP Host** | `smtp.reloop.sh` |
| **Encryption** | `TLS` (or `STARTTLS` depending on the plugin) |
| **SMTP Port** | `587` |
| **Auto TLS** | **ON** |
| **Authentication** | **ON** |
| **SMTP Username** | *Your Reloop API Key* (starts with `rl_`) |
| **SMTP Password** | *Your Reloop API Key* (starts with `rl_`) |
5. Click **Save Settings**.
---
## Step 3: Send a Test Email
Verify that WordPress can successfully connect and send emails through Reloop.
1. In the WP Mail SMTP menu, go to the **Tools** tab (or **Email Test**).
2. In the **Send To** field, enter your personal email address.
3. Click **Send Email**.
4. Check your inbox (and spam folder) for the test message. Once received, check your **Reloop Dashboard Logs** to see the delivery metrics and headers.
---
## Troubleshooting
### Email Bounced with "Domain Verification Required"
Verify that the **From Email** configured in WP Mail SMTP belongs to a domain you have added and fully verified (with valid MX, TXT, and CNAME records) in the Reloop dashboard. Reloop will block emails sent from unverified domains.
### Connection Timeout (Port 587 Blocked)
Some shared hosting providers block outgoing port `587` to prevent spam. If the connection fails or times out:
1. Try changing the port to `2587` or `2025` with `TLS` / `STARTTLS` encryption enabled.
2. If those are blocked, contact your hosting provider's support team to request that they open outbound connections on port `587` for `smtp.reloop.sh`.
---
# integrations/zapier.mdx
Source: https://reloop.sh/docs/integrations/zapier
Markdown: https://reloop.sh/docs/integrations/zapier.md
[Zapier](https://zapier.com) is the leading workflow automation platform. You can integrate Reloop with Zapier in two ways to send emails when triggers happen in other apps (like CRM updates, new form responses, or database inserts):
1. **SMTP Integration** (using Zapier's built-in **SMTP by Zapier** action)
2. **API Integration** (using Zapier's built-in **Webhooks by Zapier** action)
---
## Method 1: SMTP by Zapier
This is the easiest way to send plain text or HTML emails from any app trigger without writing API payloads.
### Step 1: Add a Trigger
Create a new Zap and configure the trigger app (e.g., Google Sheets, Typeform, Stripe).
### Step 2: Add the "SMTP by Zapier" Action
1. Search for and select **SMTP by Zapier** as your action step.
2. Select **Send Email** as the Event and click **Continue**.
### Step 3: Connect Reloop SMTP
In the **Choose Account** tab, set up the SMTP server configuration:
| Field | Value |
| :--- | :--- |
| **SMTP Host** | `smtp.reloop.sh` |
| **SMTP Port** | `587` |
| **Username** | *Your Reloop API Key* (starts with `rl_`) |
| **Password** | *Your Reloop API Key* (starts with `rl_`) |
| **Security/Encryption** | `TLS` or `STARTTLS` |
### Step 4: Configure Email Fields
Set up the message template by mapping dynamic fields from your trigger step:
- **From Email**: Enter a verified email address from your Reloop sending domain (e.g., `notification@yourdomain.com`).
- **To**: Select the recipient's email address from your trigger.
- **Subject**: Enter the subject line (you can mix static text and dynamic fields).
- **Body (HTML or Text)**: Enter the body of the email. Set **Format** to `html` if you want to send styled content.
---
## Method 2: Webhooks by Zapier (REST API)
Use this method if you need advanced features like email tagging, custom headers, scheduling emails to send later, or attachments.
### Step 1: Add a Trigger
Set up your Zap's trigger step.
### Step 2: Add Webhooks by Zapier
1. Add a new action step and select **Webhooks by Zapier**.
2. Select **Custom Request** or **POST** as the Event and click **Continue**.
### Step 3: Configure the API Request
Fill out the API endpoint details exactly as follows:
- **Method**: `POST`
- **URL**: `https://reloop.sh/api/mail/v1/send`
- **Data (Payload)**: Define your email parameters in JSON format. For example:
```json
{
"from": "Your Company ",
"to": "recipient@example.com",
"subject": "Hello from Zapier Webhook!",
"html": "Hi {{first_name}}, this email was sent automatically.
",
"tags": [
{
"name": "source",
"value": "zapier"
}
]
}
```
*Note: Replace `{{first_name}}` and `recipient@example.com` with dynamic values from your Zap trigger.*
- **Headers**: Add the following headers for authorization:
| Header Name | Value |
| :--- | :--- |
| **x-api-key** | *Your Reloop API Key* |
| **Content-Type** | `application/json` |
---
## Step 4: Test your Zap
Click **Test Step** in Zapier. You should receive a `200 OK` response with a `messageId`. Verify the message has arrived in the recipient's inbox and appears under the Logs tab in your **Reloop Dashboard**.
---
# learn/agent-inbox.mdx
Source: https://reloop.sh/docs/learn/agent-inbox
Markdown: https://reloop.sh/docs/learn/agent-inbox.md
An **agent inbox** is a mailbox on a domain you own, such as `support@inbox.yourdomain.com`. Reloop receives mail for it, groups messages into threads, flags spam, and lets you send and reply from the same address. The same mailbox works for a person in the dashboard, for your code through the API, and for an AI agent through MCP.
## How it works
1. **Mail arrives.** Your domain's MX record points to `inbound.reloop.sh`. Reloop accepts mail for every mailbox on that domain.
2. **It is stored and threaded.** Each message lands in its mailbox and is attached to a thread with the earlier messages in the conversation.
3. **It gets a spam verdict.** Every inbound message is scored. Messages scored as spam are still stored, with `is_spam: true`.
4. **You are notified.** An `email.received` webhook fires with the mailbox, sender, subject, thread and message IDs.
5. **You reply in the thread.** Replies are sent from the mailbox address and stay in the same conversation for the recipient.
## Set up a mailbox
Add a domain with **Email Receiving** turned on, and publish the MX record `inbound.reloop.sh`. Use a subdomain if your root domain already receives mail elsewhere. See [Receiving emails](/docs/guides/receiving-emails).
Create it from the domain page in the [dashboard](https://reloop.sh/dashboard), with the [Mailboxes API](/docs/api/inbox/mailboxes), or with the Node.js SDK:
```typescript
const { mailbox, mailboxError } = await reloop.inbox.mailboxes.create({
domainId: "dom_...",
email: "support@inbox.yourdomain.com",
displayName: "Acme Support",
});
```
Email the address from any personal account. The message appears in the mailbox within a few seconds.
## Threads and messages
| Concept | What it is |
| --- | --- |
| **Mailbox** | One address, such as `support@inbox.yourdomain.com`, with its own display name |
| **Thread** | A conversation. Status is `active`, `archived`, `closed` or `trash`, and threads can be starred, labelled and marked read |
| **Message** | One inbound or outbound email in a thread, with attachments and the raw source available |
| **Draft** and **note** | Unsent replies, and internal notes your team leaves on a thread |
Load the whole thread before replying, so a person or model sees every earlier message, inbound and outbound.
## Send and reply
- **Reply** or **reply all** to a message to keep the conversation in one thread. Reloop sets the threading headers for you.
- **Forward** a message to another address.
- **Send** a new message from the mailbox.
- **Delay a send** with `scheduledAt`, or give it an undo window with `undoWindowSeconds`. A pending send can be cancelled until it goes out.
The [Messages API](/docs/api/inbox/messages) and the SDK (`reloop.inbox.messages.reply`, `replyAll`, `forward`, `send`, `cancelPending`) cover all of these.
## Ways to work with an inbox
| Method | Best for |
| --- | --- |
| **Dashboard** | People reading, replying, labelling and leaving notes |
| **Webhooks** | Reacting to each new message in your code. Subscribe to [`email.received`](/docs/webhooks/emails/received) and [verify the signature](/docs/webhooks/verify-webhooks-requests). |
| **API and SDKs** | Listing threads, fetching messages, replying and sending ([Threads](/docs/api/inbox/threads), [Messages](/docs/api/inbox/messages)) |
| **MCP** | Letting Claude, Cursor or another AI client read and answer mail. See [MCP server](/docs/integrations/ai-tools/mcp-server). |
| **IMAP** | Apple Mail, Outlook, Thunderbird or any IMAP client |
### IMAP settings
| Setting | Value |
| --- | --- |
| Host | `imap.reloop.sh` |
| Port | `993` (TLS) or `143` (STARTTLS) |
| Username | The mailbox address |
| Password | A Reloop API key from the mailbox's organization that is allowed to use that address |
## Restrict what an agent can reach
Give each agent an API key restricted to its own address or domain. A restricted key can only list, read, change and send from the mailboxes its scope allows. This applies to the whole inbox API, to sending, to SMTP and to IMAP login. Requests for any other mailbox return `403`.
Treat inbound email as untrusted input. An email can contain instructions written to manipulate an AI agent, so do not let a model follow instructions found in a message, and require human approval before an agent sends anything consequential.
## Limits
Each plan includes a number of agent inboxes:
| Plan | Agent inboxes |
| --- | --- |
| Free | 3 |
| Pro | 5 |
| Growth | 10 |
| Enterprise | Custom |
Creating or re-activating a mailbox beyond your plan's limit fails. Each mailbox has a 5 GB storage quota by default. Self-hosted Reloop has no inbox limit.
## Learn more
- [Receiving emails](/docs/guides/receiving-emails)
- [Webhook event types](/docs/webhooks/event-types)
- [MCP server](/docs/integrations/ai-tools/mcp-server)
- [Domains](/docs/learn/domain)
---
# learn/contacts/channels.mdx
Source: https://reloop.sh/docs/learn/contacts/channels
Markdown: https://reloop.sh/docs/learn/contacts/channels.md
## View all channels
The [Channels Dashboard](https://reloop.sh/dashboard/contacts/channels) shows every subscription channel in your organization, including subscriber count, visibility, and default enrollment.
Channels are topics contacts can opt in or out of (for example **Product Updates** or **Newsletter**) instead of a single global unsubscribe. Visibility and default subscription stay on the channel; enrollments live on each [contact](/docs/learn/contacts).

## Create a channel
1. Go to [Contacts → Channels](https://reloop.sh/dashboard/contacts/channels)
2. Click **Create channel** (or press `C`)
3. Enter a descriptive name (e.g. `Product Updates` or `Newsletter`)
4. Optionally add a description, set **Default Subscription**, and choose **Public Channel**
5. Click **Create Channel** (or press `Enter`)
**Default Subscription** controls whether new contacts are enrolled automatically (**opt-in**) or must be enrolled manually (**opt-out**). **Public** channels appear on the preference center; **private** channels stay hidden from subscribers.
## Edit a channel
You can update a channel’s name, description, default subscription, and public/private visibility. Editing does not remove existing enrollments.
1. Go to [Contacts → Channels](https://reloop.sh/dashboard/contacts/channels)
2. Click the edit icon (or row menu **···** → **Edit Channel**)
3. Update the fields you need
4. Click **Update Channel** (or press `Enter`)
## View channel subscribers
**View Subscribers** opens Contacts filtered to contacts enrolled in that channel. The subscriber count on the channel card matches this enrolled list, not every contact in your organization.
1. Open [Contacts → Channels](https://reloop.sh/dashboard/contacts/channels)
2. Click the row menu (**···**) → **View Subscribers**
3. Review the filtered contacts list (clear the channel filter chip to see everyone again)
## Remove a contact from a channel
Removing a contact from a channel only updates that topic preference. The contact stays in your organization and can remain enrolled in other channels.
1. Open [Contacts](https://reloop.sh/dashboard/contacts) (or open a contact detail)
2. Edit the contact and remove the channel (e.g. **Product Updates**)
3. Save the contact
## Delete a channel
Deleting removes the channel and its enrollment records. Contact profiles themselves remain in your organization.
1. Open [Contacts → Channels](https://reloop.sh/dashboard/contacts/channels)
2. Click the row menu (**···**) → **Delete Channel**
3. Confirm permanent removal
## View all channels
Fetch a paginated list of channels in your organization, or get a single channel by ID.
## Create a channel
Create a subscription channel with optional description, default subscription, and visibility.
## Edit a channel
## Enroll a contact in a channel
## Remove a contact from a channel
## Delete a channel
## FAQ
Subscription topics such as Product Updates or Newsletter. Contacts opt in or out per channel instead of a single global unsubscribe.
**Opt-in** enrolls new contacts automatically. **Opt-out** means contacts must be enrolled manually or choose to join themselves.
**Public** channels appear on the preference center so subscribers can manage them. **Private** channels are only visible in the Reloop dashboard.
No. Channel opt-out only affects that topic. The contact can remain subscribed overall and stay enrolled in other [channels](/docs/learn/contacts/channels).
Deleting removes the channel and its enrollment records. [Contact](/docs/learn/contacts) profiles themselves remain.
From the Channels page, open the row menu (**···**) and select **View Subscribers** to open Contacts filtered to that channel’s enrolled contacts.
## Related
Add, search, and manage people in your organization.
Lists you send to without changing subscription status.
Custom traits on each contact record.
## API reference
New channel.
All channels.
Channel details.
Change settings.
Add a contact to a channel.
Change channel enrollment.
Remove channel.
---
# learn/contacts/groups.mdx
Source: https://reloop.sh/docs/learn/contacts/groups
Markdown: https://reloop.sh/docs/learn/contacts/groups.md
## View all contact groups
The [Groups Dashboard](https://reloop.sh/dashboard/contacts/groups) shows every group in your organization. Open a group to see its members, rename it, or add and remove contacts.
Groups are lists you send to. One [contact](/docs/learn/contacts) can sit in many groups; the person stays a single record. Membership does not change subscription status; that stays on the contact.

## Create a group
1. Go to [Contacts → Groups](https://reloop.sh/dashboard/contacts/groups)
2. Click **Create Group**
3. Enter a descriptive name (e.g. `VIP Customers` or `Free Trial`)
4. Click **Create Group** (or press `Enter`)
## Rename a group
Currently, only the **name** of a group can be edited. Renaming does not change members or how the group is targeted.
1. Go to [Contacts → Groups](https://reloop.sh/dashboard/contacts/groups)
2. Click a group to open its detail page
3. Edit the name in the header
4. Click **Save** (or press `Enter`)
## Add contacts to a group
Use groups to target campaigns without changing who someone is. For example, put trial users in **Free Trial** and paying customers in **VIP**, then send an upgrade campaign only to Free Trial.
1. Open a group from [Contacts → Groups](https://reloop.sh/dashboard/contacts/groups)
2. Click **Add Contacts to Group**
3. Search and select contacts (or **Select All**)
4. Click **Add Contacts**
## Remove contacts from a group
Removing a member leaves the contact in your organization; they only leave that list. Their profile, status, properties, and other group memberships are unchanged.
1. Open the group from [Contacts → Groups](https://reloop.sh/dashboard/contacts/groups)
2. Click the member row menu (**···**) → **Remove from Group**
3. Confirm removal
## Delete a group
Deleting soft-deletes the group. Contacts are not deleted; they stay in your organization and are no longer targeted via that group.
1. Open [Contacts → Groups](https://reloop.sh/dashboard/contacts/groups) (or open the group detail)
2. Click the row menu (**···**) → **Delete** (or delete from detail)
3. Confirm permanent removal
## View all contact groups
Fetch a paginated list of groups in your organization, or get a single group by ID.
## Create a group
Create a new group to segment contacts for targeted sends.
## Rename a group
## Add contacts to a group
## Remove contacts from a group
## Delete a group
## FAQ
Groups are lists you send to, for example VIP Customers or Free Trial. One contact can belong to many groups; the person stays a single [contact](/docs/learn/contacts) record.
No. Groups organize contacts for targeting. Subscription status and [channel](/docs/learn/contacts/channels) preferences stay on the contact itself.
Yes. Open the group in the dashboard and use **Add Contacts to Group**, or call the Groups API to add a member by contact ID or email.
The contact leaves that group only. Their profile, status, [properties](/docs/learn/contacts/properties), and other group memberships are unchanged.
Yes. Update changes the group **name** only, either from the group detail header in the dashboard or via the Groups update API.
No. Deleting soft-deletes the group. Contacts stay in your organization; they are no longer targeted via that group.
No. There is no CSV import for group members today. Add members in the dashboard or via the API. **Export CSV** from a group detail includes email, status, and created-at only.
Reloop does not enforce a fixed maximum per organization. Create, add, and remove are rate-limited (**30** requests per **60** seconds per organization). List endpoints paginate (max page size **100**). See [Usage Limits](/docs/api/usage-limits).
## Related
Add, search, and manage people in your organization.
Custom traits on each contact record.
Subscription topics and channel preferences.
## API reference
New group.
All groups.
Group details.
List members.
Enroll a contact.
Unenroll a contact.
Rename group.
Delete group.
---
# learn/contacts/index.mdx
Source: https://reloop.sh/docs/learn/contacts
Markdown: https://reloop.sh/docs/learn/contacts.md
## View all contacts
The [Contacts Dashboard](https://reloop.sh/dashboard/contacts) shows you all the contacts in your organization along with their status, audience totals, and recent activity.

## Add a contact
1. Go to [Contacts](https://reloop.sh/dashboard/contacts)
2. Click **Add contact** (or press `C`)
3. Choose **Import CSV file**, **Copy paste**, or **Sync via SDK**
4. Finish that flow: map CSV columns, paste emails, or follow the SDK snippets
5. Confirm to add the contacts
Email is unique per organization. If the address already exists, Reloop returns an error; update that contact instead of creating another one.
## Search and filter contacts
1. Open [Contacts](https://reloop.sh/dashboard/contacts)
2. Use **Search contacts…** (or press `/`) to find by email
3. Optionally filter by **Status** (Subscribed, Unsubscribed, or Blocked)
4. Click a row to open detail, or use **Export CSV** (`E`) for an offline copy
## Edit a contact
You can update a contact’s name or [properties](/docs/learn/contacts/properties). Subscription status is changed separately with [Subscribe or unsubscribe](#subscribe-or-unsubscribe-a-contact).
1. Go to [Contacts](https://reloop.sh/dashboard/contacts)
2. Click the row menu (**···**) next to the contact you want to edit
3. Select **Edit**
4. Update the fields and click **Update** (or press `Enter`)
## Subscribe or unsubscribe a contact
Unsubscribing opts the contact out of your list without deleting them. Status can be `subscribed`, `unsubscribed`, or `blocked` (often from deliverability).
1. Open [Contacts](https://reloop.sh/dashboard/contacts)
2. Click the row menu (**···**) → **Subscribe** or **Unsubscribe**
3. The contact status updates on the row
## Delete a contact
Deleting permanently removes the contact record. Recreate the contact if you need them again.
1. Open [Contacts](https://reloop.sh/dashboard/contacts)
2. Click the row menu (**···**) → **Delete**
3. Confirm permanent removal
## View all contacts
Fetch a paginated list of contacts in your organization.
## Add a contact
Create a contact with email and optional name, status, properties, groups, or channels.
Email is unique per organization. A duplicate email returns an error; update the existing contact instead.
## Edit a contact
## Subscribe or unsubscribe a contact
## Delete a contact
## FAQ
Yes. A Reloop contact is one person record keyed by a unique email address in your organization. Subscription status, custom properties, groups, and channel preferences all live on that same record.
Email is unique per organization. Creating a contact with an email that already exists returns an error; [update](#edit-a-contact) the existing contact instead of creating another one.
**Unsubscribed** means the contact opted out of your list. **Blocked** usually comes from deliverability issues such as a hard bounce or spam complaint, with a suppression reason on the contact.
Yes. From the contacts list in the dashboard, use **Export CSV** to download email, status, and created-at for the current view.
Use the Reloop SDK or REST API to create, list, get, update, and delete contacts, the same operations as the dashboard.
No built-in two-way CRM sync. Use the Contacts API and [webhooks](/docs/webhooks) to sync yourself, or connect [Zapier](/docs/integrations/zapier) / [n8n](/docs/integrations/n8n) between Reloop and your CRM.
Yes: `contact.create`, `contact.update`, `contact.delete`, `contact.subscribed`, `contact.unsubscribed`, and `contact.blocked`. See [Contact webhooks](/docs/webhooks/contacts/created) and [event types](/docs/webhooks/event-types).
Yes. Limits are per organization (e.g. create **200**/min, list/get **60**/min, update/delete **30**/min). Over the limit → `429` with `Retry-After`. Full table: [Usage Limits](/docs/api/usage-limits).
No fixed total-contact cap per plan. You can store as many contacts as you need; bulk create/update is still gated by [API rate limits](/docs/api/usage-limits).
## Related
Custom string and number fields.
Lists for targeted sends.
Per-topic subscription preferences.
## API reference
Add a new contact.
Paginated list of organization contacts.
Get a contact by ID.
Change name, status, or properties.
Permanently delete a contact.
---
# learn/contacts/properties.mdx
Source: https://reloop.sh/docs/learn/contacts/properties
Markdown: https://reloop.sh/docs/learn/contacts/properties.md
## View all contact properties
The [Properties Dashboard](https://reloop.sh/dashboard/contacts/properties) shows every custom field in your organization: name, type, default, and when it was last updated.
Properties are custom **String** or **Number** fields you attach to a [contact](/docs/learn/contacts). Use them for personalization and targeting (for example `company` or `plan_tier`). An optional default is used when a contact has no value set.

## Create a property
1. Go to [Contacts → Properties](https://reloop.sh/dashboard/contacts/properties)
2. Click **Add property** (or press `C`)
3. Enter a name (e.g. `plan_tier` or `company_size`) using letters, numbers, and underscores only
4. Choose type **String** or **Number**, and optionally set a default
5. Click **Add property** (or press `Enter`)
## Edit a property
After create, only the **default (fallback)** value can be changed. Name and type are fixed. To use a different name, create a new property and set values on contacts under that name.
1. Go to [Contacts → Properties](https://reloop.sh/dashboard/contacts/properties)
2. Click the row menu (**···**) next to the property you want to edit
3. Select **Edit**
4. Update the default value and click **Save** (or press `Enter`)
## Set values on a contact
The Properties page defines the **schema**. Values live on each contact. Set them when you edit a contact in the dashboard or via the Contacts update API.
1. Open [Contacts](https://reloop.sh/dashboard/contacts)
2. Click the row menu (**···**) → **Edit** (or open the contact)
3. Fill the custom property fields (e.g. `plan_tier`, `timezone`, `company_size`)
4. Click **Update** (or press `Enter`)
## Delete a property
Deleting removes the schema definition. Existing contacts keep raw key-value data, but the field is no longer managed globally.
1. Open [Contacts → Properties](https://reloop.sh/dashboard/contacts/properties)
2. Click the row menu (**···**) → **Delete**
3. Confirm permanent removal
## View all contact properties
Fetch a paginated list of property definitions in your organization.
## Create a property
Create a new String or Number property schema for your contacts.
## Edit a property
## Set values on a contact
Set custom property values when you update a contact.
## Delete a property
## FAQ
Properties are small details you add to a contact. They are custom **String** or **Number** fields with an optional default. Use them for personalization and targeting (for example `company` or `plan_tier`).
**String** (free-form text) and **Number** (integer or decimal), plus an optional fallback when a contact has no value.
Deleting removes the schema definition. Existing contacts keep raw key-value data, but the field is no longer managed globally.
Use letters, numbers, and underscores only. Names must start with a letter or underscore. Spaces become underscores. When setting values on a contact, names must be lowercase (`a-z`, `0-9`, underscores).
No. After you create a property, you can only change its **default (fallback)** value; name and type stay fixed. To use a different name, create a new property and set values on contacts under that name.
No. Names are unique per organization by exact name. When setting values on a contact, names must be lowercase (`a-z`, `0-9`, underscores), so don’t rely on casing variants as separate fields.
Yes. The Properties page defines the shared field (schema). Each contact stores its own value. The optional default is only a fallback when a contact has no value set.
No. There is no CSV import for custom property values today. Set values when you [edit a contact](/docs/learn/contacts) in the dashboard or via the Contacts update API. **Export CSV** from the contacts list includes email, status, and created-at only.
Reloop does not enforce a fixed maximum per organization. Create is rate-limited (**30** requests per **60** seconds per organization). See [Usage Limits](/docs/api/usage-limits).
## Related
Add, search, and manage people in your organization.
Segment contacts for targeted sends.
Subscription topics and channel preferences.
## API reference
New property schema.
All properties.
Change default value.
Remove schema.
---
# learn/domain.mdx
Source: https://reloop.sh/docs/learn/domain
Markdown: https://reloop.sh/docs/learn/domain.md
## View all domains
The [Domains Dashboard](https://reloop.sh/dashboard/domain) lists every domain in your organization with status, search, and filters.

## Add a domain
1. Go to [Domains](https://reloop.sh/dashboard/domain)
2. Click **Add domain** (or press `C`)
3. Enter the hostname (e.g. `acme.com` or `mail.acme.com`)
4. Optionally open **Advanced options** to set click/open tracking defaults
5. Click **Add Domain**
Reloop creates the domain in `pending` status and opens the DNS setup page with the records you need to publish. Email Sending, Email Receiving, and click/open tracking start **on** by default. Turn Receiving off before publishing DNS if this hostname must keep another inbox.

Prefer a guided path for beginners? See the full [Connect a domain](/docs/guides/connect-domain) guide, with DNS basics, provider tutorials, and troubleshooting.
## Configure DNS
After you add a domain, Reloop shows the required DNS records (SPF, DKIM, DMARC, and receiving/tracking rows based on your toggles). Sending does not require MX.
**Auto-populate** (when available; today Cloudflare, Vercel, Domain Chief, and NameSilo):
1. On the setup or domain page, click **Auto populate**
2. Approve Reloop’s Domain Connect template at your DNS host
3. Return to Reloop and [verify](#verify-a-domain)
**Manual setup:**
1. Copy each record from the Reloop DNS table
2. Add them at the host that manages your [nameservers](/docs/guides/connect-domain/find-dns-provider)
3. Merge SPF if you already have an SPF TXT; do not create a second SPF record
Deep dives: [Auto-populate](/docs/guides/connect-domain/auto-populate) · [Manual setup](/docs/guides/connect-domain/manual-setup) · [DNS records explained](/docs/guides/connect-domain/dns-records-explained) · [Provider guides](/docs/guides/connect-domain/providers)
## Verify a domain
1. Open the domain from [Domains](https://reloop.sh/dashboard/domain) (or stay on the post-add setup page)
2. Click **Verify & finish** on setup, or **Verify Domain** on the detail page (menu → **Re-verify DNS**)
3. Wait for status to move through `pending` → `verifying` → `active`


Status meanings and propagation tips: [Domain statuses](/docs/guides/connect-domain/verification/statuses) and [Propagation](/docs/guides/connect-domain/verification/propagation). Stuck? [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying).
## Configure sending, receiving, and tracking
On the domain detail page you can toggle capabilities without deleting the domain:
1. Open [Domains](https://reloop.sh/dashboard/domain) and click a domain
2. On the **DNS Records** tab, toggle **Email Sending**, **Email Receiving**, or **Tracking** as needed
3. Open the **Configuration** tab for click tracking, open tracking, and TLS (`opportunistic` or `enforced`)
Email Receiving is on by default for new domains. Publishing Reloop inbound MX (`inbound.reloop.sh`) on a hostname that already uses Google Workspace or Microsoft 365 can divert or split delivery. Turn Receiving off for send-only, or use a subdomain. See [Sending vs receiving](/docs/guides/connect-domain/sending-vs-receiving) and [MX conflicts](/docs/guides/connect-domain/troubleshoot/mx-conflicts).
## Forward DNS records
Share the Reloop DNS table with a teammate who manages DNS:
1. Open the domain detail or setup page
2. Click **Forward Records** (or use the row/header menu)
3. Enter the recipient email and send
## Delete a domain
Deleting permanently removes the domain from Reloop. You can no longer send with that hostname until you add and verify it again. Optionally remove Reloop DNS rows at your host afterward. See [Remove a domain](/docs/guides/connect-domain/after/remove-domain).
1. Open [Domains](https://reloop.sh/dashboard/domain)
2. Click the row menu (**···**) → **Delete Domain** (or open the domain and use the header menu)
3. Type the confirmation text and confirm permanent removal
You can also select multiple domains on the list and delete them in bulk.
## View all domains
Fetch a paginated list of domains in your organization.
## Add a domain
Create a domain and receive the DNS records Reloop expects you to publish.
Creating a domain does not verify it. Publish DNS (Auto-populate or manual), then call verify.
## Verify a domain
## Update domain configuration
Toggle sending, receiving, click/open tracking, or TLS.
## Delete a domain
## FAQ
Verification proves you control DNS for that hostname and publishes SPF, DKIM, and related records so mailbox providers can authenticate mail from Reloop. Unverified domains cannot send reliably.
**Auto-populate** uses [Domain Connect](/docs/guides/connect-domain/auto-populate) so your DNS host applies Reloop’s records after you approve a template. Reloop currently offers **Auto populate** for Cloudflare, Vercel, Domain Chief, and NameSilo. **Manual setup** means copying each TXT, MX, and CNAME into your DNS panel yourself.
Yes. Reloop supports root domains (e.g. `acme.com`) and subdomains (e.g. `mail.acme.com`). Subdomains are often easier when the root already has other email or DNS services. See [What is a domain?](/docs/guides/connect-domain/what-is-a-domain).
Toggles pause that capability without deleting the domain. Disabling **sending** blocks outbound From addresses on that domain; disabling **receiving** turns off Reloop inbound MX for that host. Re-enable either anytime from the domain detail page or the Update Domain API.
Often minutes after records publish, but propagation can take up to 48 hours depending on TTL and your DNS host. Click **Verify Domain** / **Verify & finish** / **Re-verify DNS** after changes. Details: [Propagation](/docs/guides/connect-domain/verification/propagation).
Confirm you edited DNS at the **nameserver** host, merge SPF instead of adding a second SPF TXT, check DKIM/DMARC hostnames for typos, then follow [Domain not verifying](/docs/guides/connect-domain/troubleshoot/not-verifying).
Yes: `domain.create`, `domain.updated`, and `domain.deleted`. See [Domain webhooks](/docs/webhooks/domains/created) and [event types](/docs/webhooks/event-types).
## Related
Full DNS setup guide for beginners and experts.
Send after your domain is active.
Authenticate REST and SMTP with API keys.
## API reference
Add a domain and get DNS records.
Paginated list of organization domains.
Get a domain by ID.
Toggle sending, receiving, tracking, or TLS.
Check public DNS against Reloop records.
Email DNS records to a teammate.
Permanently remove a domain.
---
# learn/emails/details.mdx
Source: https://reloop.sh/docs/learn/emails/details
Markdown: https://reloop.sh/docs/learn/emails/details.md
### Key Capabilities
* **REST API & SMTP Relay**: Integrate using our REST API or point your existing app configurations to our high-performance SMTP relay.
* **SDK Support**: First-class SDKs available for Node.js, Go, Python, and Rust.
* **Instant Delivery Verification**: Real-time status updates from delivery to click.
* **Rich Styling Support**: Render using raw HTML, Markdown, or React-Email templates.
---
# learn/emails/index.mdx
Source: https://reloop.sh/docs/learn/emails
Markdown: https://reloop.sh/docs/learn/emails.md
Reloop provides robust outbound transactional mailing infrastructure built for developers. Scale from a few emails a day to millions, with instant delivery tracking and optimized rendering across clients.
---
# learn/index.mdx
Source: https://reloop.sh/docs/learn
Markdown: https://reloop.sh/docs/learn.md
Reloop is designed to simplify and supercharge your email workflows, combining high-deliverability outbound transactional mailing with a smart AI-powered inbound Agent Inbox, real-time logging, and interactive workflow automation.
Explore the core features of the Reloop platform:
Receive, route, and reply to inbound emails dynamically with AI classification.
Send transactional emails at scale with guaranteed delivery and rich styling.
Track click rates, open rates, bounces, and delivery performance.
Manage lists, segments, custom traits, and email suppressions.
Build reusable and dynamic email layouts using Lexical or raw HTML.
Manage fine-grained API credentials and rotate tokens securely.
Stream and search through detailed dispatch logs in real-time.
Configure SPF, DKIM, DMARC, and custom sending subdomains.
Create visual automated flows, drip campaigns, and auto-responders.
---
# learn/logs.mdx
Source: https://reloop.sh/docs/learn/logs
Markdown: https://reloop.sh/docs/learn/logs.md
Debug and audit your transactional flows with Reloop's real-time **Logs** console. View full SMTP transaction records, headers, payloads, and delivery feedback loops instantly.
## Key capabilities
* **Live streaming console**: Watch outbound dispatches and incoming webhook responses in real-time.
* **Full header inspection**: Inspect DKIM signatures, custom headers, and envelope addresses.
* **Error tracing**: Instantly view SMTP error codes, response strings, and bounce details returned by receiving servers.
* **Search & filters**: Query logs by status (delivered, bounced, opened), recipient, domain, or API key.
---
# learn/metrics.mdx
Source: https://reloop.sh/docs/learn/metrics
Markdown: https://reloop.sh/docs/learn/metrics.md
Gain complete visibility into your email sender reputation and user engagement with Reloop's detailed **Metrics** and analytics engine.
## Key capabilities
* **Delivery funnel**: Track Delivery, Open, Click, Bounce, Complaint, and Unsubscribe counts.
* **Granular time filtering**: Filter reports by hour, day, week, or month.
* **Tag & metadata filtering**: Group and analyze metrics by custom metadata tags (e.g. campaign ID, user region, email type).
* **PostgreSQL backed**: Delivery metrics are stored and queried in PostgreSQL alongside the rest of your Reloop data.
---
# learn/templates.mdx
Source: https://reloop.sh/docs/learn/templates
Markdown: https://reloop.sh/docs/learn/templates.md
Create and maintain beautiful email templates within Reloop. Use our interactive editor or write raw code to deploy production-ready layouts that render flawlessly across all email clients.
## Key capabilities
* **Lexical editor**: An intuitive visual drag-and-drop builder for creating complex responsive email layouts without writing HTML.
* **Variable interpolation**: Embed variables in triple braces (`{{{first_name}}}`) and pass their values in `template.variables` when you send through the API. Campaigns and workflows can also read contact fields directly (`{{contact.first_name}}`). Values are HTML-escaped when they are inserted into the HTML body, so a value like `Ada` shows as text rather than markup. Subjects and plain-text bodies get the value as-is.
* **Layout inheritance**: Design universal headers/footers and extend them for newsletter, marketing, or transactional templates.
* **Developer previews**: Instantly test how templates render in dark mode, light mode, or mobile screens.
---
# learn/workflows.mdx
Source: https://reloop.sh/docs/learn/workflows
Markdown: https://reloop.sh/docs/learn/workflows.md
An **automation** sends emails for you when something happens in your app. You track a **trigger** (a custom event like `user.signed_up`) for a contact, and Reloop runs the automation's steps for them: send an email, wait, check a condition, and branch.
## Create a trigger
1. Go to [Automation → Triggers](https://reloop.sh/dashboard/automation/triggers)
2. Click **Create Trigger**
3. Name it (for example `User signed up`) and add the properties your app will send, such as `plan`
4. Save, then copy its key from the row menu (**Copy key**)
## Build an automation
1. Go to [Automation](https://reloop.sh/dashboard/automation)
2. Click **Create Automation**
3. Pick the trigger that starts it
4. Add steps after it: **Send email**, **Delay** and **Condition**. A condition has a **Yes** and a **No** path
5. Click **Publish** to start it
After you edit an active automation, click **Publish changes** so new contacts get the new steps.
## Start it from your app
The trigger's code drawer shows a ready-to-paste snippet with its key. You can also use the **Code** tab on this page.
## Pause an automation
Open the automation and click **Pause automation**. It stops starting for new events, and contacts in progress are cancelled at their next step.
## Create a trigger
Define the event and the properties it carries. The key is what your app sends as `event` when starting automations.
Keys always start with `trigger.`, so `user.signed_up` is stored as `trigger.user.signed_up`. You can leave the prefix off anywhere you pass a key.
## Create an automation
An automation is a list of **steps** and the **connections** between them. Each step has a `key` you choose, which connections use to link steps. Pass `status: "active"` to start it right away, or leave it as a draft.
If something is wrong with the steps, the error's `why` lists every problem at once.
## Start the automation
Call `automations.start` when the event happens in your app. Every active automation listening for it starts for that contact. Pass `email` and the contact is created if it doesn't exist yet.
The response tells you how many automations started (`enrollments`). Properties marked `required` must be sent, and missing ones fall back to their `defaultValue`.
## Enroll a contact directly
Start one active automation for a contact without a trigger, for example to backfill existing users.
## Activate, edit and pause
Changing the steps of an active automation saves them as a draft. Activate it again, or pass `status: "active"` in the same update, to publish them.
## See who went through it
List the contacts enrolled in an automation, then open one to see what happened at each step, including the email a send step sent.
## Steps
| Type | Config | What it does |
|---|---|---|
| `trigger` | `{ eventName }` | Starts the automation. Every automation has exactly one. |
| `send_email` | `{ template: { id }, from?, subject? }` | Sends a [template](/docs/learn/templates) to the contact. Its From and subject come from the template; `from` and `subject` are used only when the template has none. |
| `delay` | `{ amount, unit }` | Waits before the next step. `unit` is `minutes`, `hours` or `days`. |
| `condition` | a rule or a group | Checks the contact or the event and takes the Yes or No path. |
Connections link steps by key: `{ from, to, type }`. `type` is `default` between steps. A condition has one `condition_met` (Yes) and one `condition_not_met` (No) connection.
## Conditions
A rule checks one field:
```json
{ "type": "rule", "field": "trigger.plan", "operator": "eq", "value": "pro" }
```
A group combines rules. With `and` every rule must match, and with `or` any rule can:
```json
{
"type": "and",
"rules": [
{ "type": "rule", "field": "trigger.plan", "operator": "eq", "value": "pro" },
{ "type": "rule", "field": "contact.company", "operator": "exists" }
]
}
```
| Field | Reads |
|---|---|
| `trigger.` | A property sent with the event, e.g. `trigger.plan` |
| `contact.email`, `contact.firstName`, `contact.lastName`, `contact.status` | The contact |
| `contact.` | A [contact property](/docs/learn/contacts/properties), e.g. `contact.company` |
| Operator | Matches when the field |
|---|---|
| `eq` / `neq` | equals / does not equal `value` |
| `contains`, `starts_with`, `ends_with` | contains, starts with, or ends with `value` |
| `gt`, `gte`, `lt`, `lte` | is a number greater than, at least, less than, or at most `value` |
| `exists` / `not_exists` | is set / is not set |
| `is_empty` | is not set or is blank (no `value` needed) |
Text comparisons ignore case. To compare against another field instead of a fixed value, pass `{ "var": "trigger.expected_plan" }` as `value`.
## How a contact moves through it
- **Once per automation, unless re-entry is on.** By default a contact goes through each automation once, and tracking the event again does nothing. For flows that can happen more than once, turn on **Allow re-entry** when you create the automation or from the **…** menu in the editor. Each new event then starts it again. **Cancel the run in progress** decides what happens if the contact is still in an earlier run: on, that run is cancelled and the fresh one takes over (good for an abandoned checkout that should start over); off, both runs keep going (good for one sequence per order). In the API this is `reentry`: `"once"` (default), `"restart"` or `"parallel"`. Changes apply right away, without publishing.
- **Only subscribed contacts.** If the contact unsubscribes or is blocked, they leave the automation at their next step.
- **Pinned to a version.** Each enrollment finishes on the steps that were published when it started, even if you publish changes later.
- **Enrollment status.** `active` while it runs, then `completed`, `cancelled` (paused, deleted, unsubscribed, or replaced by a newer run; `replacedByEnrollmentId` points to it) or `failed`.
## API reference
- [Track event](/docs/api/workflow/events/post-api-workflow-v1events-track)
- [Events](/docs/api/workflow/events/post-api-workflow-v1events)
- [Automations](/docs/api/workflow/automations/post-api-workflow-v1automations)
- [Enrollments](/docs/api/workflow/automations/get-api-workflow-v1automations-by-automation_id-enrollments)
## FAQ
A trigger, which is a custom event your app tracks (for example `user.signed_up`). [Start it](#start-the-automation) and every active automation that uses it starts for that contact. You can also [enroll one contact directly](#enroll-a-contact-directly).
Only if you turn on [re-entry](#how-a-contact-moves-through-it) for that automation. By default a contact goes through each automation once; tracking the event again does nothing and enrolling them directly returns `409`. With re-entry on, every new event starts it again. You choose whether a run still in progress is cancelled (`restart`) or keeps going alongside the new one (`parallel`).
Pausing stops new enrollments, and contacts already in it are cancelled when their next step comes due. Activating again does not resume them.
No. Saving steps on an active automation keeps them as a draft. Activate it again (**Publish changes** in the dashboard) to publish them. Contacts already enrolled finish on the version they started with.
No. Custom events only start automations. Tracking one does not send webhooks, and webhook events do not start automations.
---
# self-host/azure.mdx
Source: https://reloop.sh/docs/self-host/azure
Markdown: https://reloop.sh/docs/self-host/azure.md
# Deploy on Azure
Coming Soon
---
# self-host/caprover.mdx
Source: https://reloop.sh/docs/self-host/caprover
Markdown: https://reloop.sh/docs/self-host/caprover.md
Reloop ships a CapRover one-click template at
[`install/caprover/reloop.yml`](https://github.com/reloop-labs/reloop/blob/main/install/caprover/reloop.yml).
It runs the same stack as the [VPS installer](/docs/self-host/vps), with
CapRover generating every secret and its nginx handling HTTPS.
---
## Before you start
| Requirement | Value |
| :--- | :--- |
| **CapRover** | A working instance with a wildcard root domain, for example `*.captain.example.com` |
| **Architecture** | x86_64 (the dashboard and links images are amd64 only) |
| **RAM** | 8 GB for Reloop and CapRover together |
| **Disk** | 50 GB. The container images alone take about 22 GB |
| **Ports** | `25`, `465` and `587` free on the server, inbound and outbound |
| **DNS** | Two extra records, see [DNS](#dns) |
Many providers block port `25` by default. Reloop delivers to recipient mail
servers on outbound `25`, and receives mail on inbound `25`, so ask your
provider to open it.
---
## Deploy
1. In CapRover, open **Apps** → **One-Click Apps/Databases**.
2. Select **>> TEMPLATE <<** at the bottom of the list, paste the contents of
[`reloop.yml`](https://raw.githubusercontent.com/reloop-labs/reloop/main/install/caprover/reloop.yml)
and click **Next**.
3. Enter an app name, for example `reloop`. The examples below assume it.
4. Copy the **Administrator Setup Key** somewhere safe. You need it to sign in.
5. Click **Deploy**. The first deploy downloads about 5 GB of images.
CapRover creates 22 apps, all named after the app name: `reloop` (the proxy),
`reloop-links`, `reloop-dashboard`, `reloop-postgres`, `reloop-redis`,
`reloop-nats`, `reloop-smtp`, `reloop-inbound`, `reloop-spam` and one app per
backend service.
`reloop-auth` creates the database schema before it starts. Until it has, the
other backend apps fail and CapRover restarts them, so expect a few restarts
in the first minutes.
Every generated value is fixed after the first deploy. The database password
is set once Postgres initialises, changing the tracking or preferences secrets
breaks links in mail that was already sent, and changing the webhook key breaks
the signature on every existing webhook.
### Enable HTTPS
1. Open the `reloop` app, click **Enable HTTPS**, then turn on **Force HTTPS by
redirecting all HTTP traffic to HTTPS** and save.
2. Do the same on `reloop-links`.
Campaign sends, automation email steps, inbox replies and system mail through
`RELOOP_API_KEY` call `https://reloop.captain.example.com` from inside the
stack, so they fail until the certificate is issued.
---
## DNS
The wildcard record CapRover already needs covers the dashboard, the API and
tracking. Add these:
```text
reloop-inbound.captain.example.com A 203.0.113.10
reloop.captain.example.com TXT "v=spf1 ip4:203.0.113.10 -all"
```
- `reloop-inbound.` is the MX target for mail your verified domains receive.
The wildcard covers it too, but add the `A` record if your wildcard is a
`CNAME`, since MX targets must not be one.
- The SPF record authorises the server to send for the host domain, which is
what the `include:` in each sending domain's SPF resolves to.
Also ask your provider for a PTR record on the server IP that resolves to
`reloop.captain.example.com`. Receiving servers check it.
---
## Custom domains
The template serves Reloop on `reloop.captain.example.com`, tracking on
`reloop-links.captain.example.com` and inbound mail on
`reloop-inbound.captain.example.com`. To use your own domains instead:
1. Point the three hostnames at the server with `A` records.
2. Open `reloop`, add `reloop.example.com` under **HTTP Settings** →
**Connect New Domain**, then enable HTTPS on it.
3. Do the same on `reloop-links` with the tracking domain.
4. Run this on any machine with `curl` and `jq`, with your own values in the
first lines. It rewrites the three hostnames in every Reloop app's
environment, and CapRover restarts each app.
```bash
URL=https://captain.captain.example.com
PASSWORD='your CapRover password'
APP=reloop
ROOT=captain.example.com
DOMAIN=reloop.example.com
LINKS=link.reloop.example.com
INBOUND=mx.reloop.example.com
TOKEN=$(curl -s -H 'x-namespace: captain' -H 'Content-Type: application/json' \
-d "{\"password\":\"$PASSWORD\"}" $URL/api/v2/login | jq -r .data.token)
api() { curl -s -H 'x-namespace: captain' -H "x-captain-auth: $TOKEN" -H 'Content-Type: application/json' "$@"; }
api $URL/api/v2/user/apps/appDefinitions | jq -c --arg app "$APP" \
--arg old "$APP.$ROOT" --arg new "$DOMAIN" \
--arg oldl "$APP-links.$ROOT" --arg newl "$LINKS" \
--arg oldi "$APP-inbound.$ROOT" --arg newi "$INBOUND" '
.data.appDefinitions[]
| select(.appName | startswith($app + "-"))
| select(any(.envVars[]; .value | contains($old)))
| .envVars |= map(.value |= (split($oldl) | join($newl) | split($oldi) | join($newi) | split($old) | join($new)))' |
while read -r app; do api -d "$app" $URL/api/v2/user/apps/appDefinitions/update | jq -r .description; done
```
Set the real domains before you add any sending domain in the dashboard.
Reloop writes the DNS records it shows you at the moment a domain is created,
so a domain added while the instance still runs on the CapRover subdomain keeps
pointing there.
A sending domain with click or open tracking gets a `link.` CNAME pointing at
the tracking domain. CapRover only issues certificates for domains attached to
an app, so connect each customer tracking hostname, such as
`link.customer.com`, to `reloop-links` and enable HTTPS on it.
---
## First sign-in
1. Open `/dashboard/setup` on your Reloop domain, for example
`https://reloop.captain.example.com/dashboard/setup`.
2. Paste the Administrator Setup Key you copied during deploy.
3. Choose the first administrator's name, email and password.
4. Name the first organization.
The key works once. If you lost it, it is the `ADMIN_SETUP_KEY` variable under
**App Configs** on `reloop-auth`. If you think it leaked before setup, change
it there and save: the old key stops working.
Setup signs you in with a session that lasts 7 days. To sign in again after
that, Reloop emails you a code, so configure system email before it runs out.
---
## System email
Until system email is configured, Reloop sends no sign-in codes, invitations
or notifications. Fill in the **System Email** variables when you deploy, or
set these later:
| Variable | Value |
| :--- | :--- |
| `RELOOP_SENDER_DOMAIN` | The domain system mail is sent from. Always required |
| `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `SMTP_SECURE` | Any SMTP provider you already use (Postmark, SES, your own). `SMTP_SECURE=true` means implicit TLS, normally with port `465` |
| `RELOOP_API_KEY` | Instead of SMTP: an API key of the organization that owns `RELOOP_SENDER_DOMAIN`, once that domain is verified in this instance |
---
## Configuration
Each CapRover app has its own copy of the environment. To change a value after
deploy, open **App Configs** on every app that lists it, change it, and save.
CapRover restarts the app.
| Variable | Default | Purpose |
| :--- | :--- | :--- |
| `DISABLE_SIGNUP` | `true` | Blocks public registration. Invited addresses can still join. If setup leaves public sign-ups off, it also saves that in `/run/reloop/.env` on the `reloop-auth` volume, which keeps registration closed even when this is `false` |
| `DISABLE_ORG_CREATION` | `false` | Stops users creating further organizations |
| `APP_NAME` | `Reloop` | Instance name in system email, shown as `Self-hosted Reloop × ` |
| `S3_ENDPOINT`, `S3_ACCESS_KEY`, `S3_SECRET_KEY` | empty | S3-compatible storage for template images and attachments. The bucket must serve objects publicly, because Reloop hands out URLs as `{S3_ENDPOINT}/{S3_BUCKET}/{path}` |
| `S3_BUCKET` | `reloop-uploads` | Bucket name |
| `S3_REGION` | `us-east-1` | Bucket region |
| `DNS_RESOLVERS` | `8.8.8.8,8.8.4.4` | Resolvers used to verify SPF, DKIM, DMARC and MX records |
| `DKIM_SELECTOR` | `reloop` | Selector for the DKIM keys Reloop generates |
The queue dashboard at `https://reloop.captain.example.com/api/workflow/jobs`
uses basic auth with the user `admin` and the **Queue Dashboard Password**.
---
## Mail ports and TLS
| Port | App | Protocol |
| :--- | :--- | :--- |
| `25` | `reloop-inbound` | SMTP with STARTTLS, for mail your verified domains receive |
| `587` | `reloop-smtp` | Submission with STARTTLS |
| `465` | `reloop-smtp` | The same STARTTLS listener as `587`, not implicit TLS |
Both mail apps use a self-signed certificate. Submit on `587` with STARTTLS,
and tell clients that verify certificates to accept it.
CapRover publishes these ports through Docker Swarm's ingress network, which
replaces the connecting client's address with an internal one. Spam checks on
inbound mail that rely on the sender's IP see that internal address instead.
If you depend on them, use the [VPS installer](/docs/self-host/vps).
---
## Updates and backups
Back up the database before each update:
```bash
docker exec $(docker ps -qf name=reloop-postgres) \
pg_dump -U reloop reloop > reloop-$(date +%F).sql
```
To update, open each Reloop app's **Deployment** tab and deploy its image again
under **Deploy via ImageName**, for example `reloopsh/be-auth:latest`. Update
`reloop-auth` first: it pushes the database schema before it starts.
The proxy routes live in the `reloop` app's image. When a Reloop release changes
them in `reloop.yml`, open the `reloop` app's **Deployment** tab and paste the
new `dockerfileLines` of the proxy service under **Deploy captain-definition
file**.
---
## Things to avoid
- **arm64 servers.** The dashboard and links images are built for amd64 only.
- **Exposing the internal apps.** Only `reloop` and `reloop-links` are web apps.
Exposing any other one puts the internal SMTP injection API, NATS or Rspamd on
the internet.
- **Routing the mail ports through CapRover's nginx.** SMTP authentication
needs the TLS session to reach `reloop-smtp` directly.
---
# self-host/cloudflare.mdx
Source: https://reloop.sh/docs/self-host/cloudflare
Markdown: https://reloop.sh/docs/self-host/cloudflare.md
Coming Soon
---
# self-host/coolify.mdx
Source: https://reloop.sh/docs/self-host/coolify
Markdown: https://reloop.sh/docs/self-host/coolify.md
Reloop ships a Coolify template at
[`install/coolify/reloop.yaml`](https://github.com/reloop-labs/reloop/blob/main/install/coolify/reloop.yaml).
It runs the same stack as the [VPS installer](/docs/self-host/vps), with Coolify
generating every secret and its proxy handling HTTPS.
---
## Before you start
| Requirement | Value |
| :--- | :--- |
| **Coolify** | v4, with the default Traefik proxy |
| **Architecture** | x86_64 (the dashboard and links images are amd64 only) |
| **RAM** | 8 GB for Reloop and Coolify together |
| **Disk** | 50 GB. The container images alone take about 22 GB |
| **Ports** | `25`, `465` and `587` free on the server, inbound and outbound |
| **DNS** | Three hostnames pointing at the server, see [DNS](#dns) |
Many providers block port `25` by default. Reloop delivers to recipient mail
servers on outbound `25`, and receives mail on inbound `25`, so ask your
provider to open it.
---
## Deploy
1. In Coolify, open your project and choose **New Resource** → **Docker Compose**
(called **Docker Compose Empty** in older versions).
2. Paste the contents of
[`reloop.yaml`](https://raw.githubusercontent.com/reloop-labs/reloop/main/install/coolify/reloop.yaml)
and save.
3. Before the first deploy, replace the generated domain of these two services:
| Service | Domain |
| :--- | :--- |
| `proxy` | `https://reloop.example.com` |
| `links` | `https://link.reloop.example.com` |
Coolify warns that `proxy` requires port `80` and `links` requires port
`3005`. Choose **Use this port anyway**: traffic still goes to those ports,
and Reloop's URLs stay without one. **Keep required port** discards the new
domain. Leave every other service without a domain.
4. Click **Deploy**. The first deploy downloads about 5 GB of images, which
unpack to about 22 GB. Coolify stops a deploy after 10 minutes, so on a slow
connection the first one can fail before the stack starts. Click **Deploy**
again: the images already downloaded are kept.
Coolify creates the database, Redis and every Reloop secret for you and stores
them under **Environment Variables**. Change none of the generated values after
the first deploy: the database password is fixed once Postgres initialises,
changing the tracking or preferences secrets breaks links in mail that was
already sent, and changing the webhook key breaks the signature on every
existing webhook.
Coolify passes all of these values to every container in the stack, including
the public dashboard and links services. Keep `RELOOP_VERSION` on `latest` so
those services get security fixes.
Set the real domains before you add any sending domain in the dashboard.
Reloop writes the DNS records it shows you at the moment a domain is created,
so a domain added while the instance still runs on Coolify's generated
`sslip.io` address keeps pointing there.
---
## DNS
```text
reloop.example.com A 203.0.113.10
link.reloop.example.com A 203.0.113.10
inbound.reloop.example.com A 203.0.113.10
reloop.example.com TXT "v=spf1 ip4:203.0.113.10 -all"
```
- The apex record serves the dashboard and the API.
- `link.` serves click and open tracking and the unsubscribe pages.
- `inbound.` is the MX target for mail your verified domains receive. The
template always uses `inbound.` plus the `proxy` domain.
- The SPF record authorises the server to send for the host domain, which is
what the `include:` in each sending domain's SPF resolves to.
Also ask your provider for a PTR record on the server IP that resolves to
`reloop.example.com`. Receiving servers check it.
---
## First sign-in
1. In Coolify, open **Environment Variables** and copy the value of `SERVICE_PASSWORD_64_SETUPKEY`.
2. Open `https://reloop.example.com/dashboard/setup` and paste it as the setup key.
3. Choose the first administrator's name, email and password.
4. Name the first organization. Leave the instance name empty: on Coolify it
has no effect, so set `APP_NAME` under **Environment Variables** instead.
The key works once. If you think it leaked before setup, change
`SERVICE_PASSWORD_64_SETUPKEY` and restart: the old key stops working.
Setup signs you in with a session that lasts 7 days. To sign in again after
that, Reloop emails you a code, so configure system email before it runs out.
---
## System email
Sign-in codes and invitations need a way out. Set these under **Environment
Variables**, then restart the service:
| Variable | Value |
| :--- | :--- |
| `RELOOP_SENDER_DOMAIN` | The domain system mail is sent from. Always required |
| `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `SMTP_SECURE` | Any SMTP provider you already use (Postmark, SES, your own). `SMTP_SECURE=true` means implicit TLS, normally with port `465` |
| `RELOOP_API_KEY` | Instead of SMTP: an API key of the organization that owns `RELOOP_SENDER_DOMAIN`, once that domain is verified in this instance |
---
## Configuration
Everything below is optional. Coolify lists each variable under **Environment
Variables** with its default.
| Variable | Default | Purpose |
| :--- | :--- | :--- |
| `DISABLE_SIGNUP` | `true` | Blocks public registration. Invited addresses can still join. If setup leaves public sign-ups off, it also saves that in `/run/reloop/.env` on the `auth` service's volume, which keeps registration closed even when this is `false`. To open it later, set this to `false`, remove the `DISABLE_SIGNUP` line from that file in the `auth` container, and restart |
| `DISABLE_ORG_CREATION` | `false` | Stops users creating further organizations |
| `APP_NAME` | `Reloop` | Instance name in system email, shown as `Self-hosted Reloop × ` |
| `S3_ENDPOINT`, `S3_ACCESS_KEY`, `S3_SECRET_KEY` | empty | S3-compatible storage for template images and attachments. The bucket must serve objects publicly, because Reloop hands out URLs as `{S3_ENDPOINT}/{S3_BUCKET}/{path}` |
| `S3_BUCKET` | `reloop-uploads` | Bucket name |
| `S3_REGION` | `us-east-1` | Bucket region |
| `DNS_RESOLVERS` | `8.8.8.8,8.8.4.4` | Resolvers used to verify SPF, DKIM, DMARC and MX records |
| `DKIM_SELECTOR` | `reloop` | Selector for the DKIM keys Reloop generates |
| `RELOOP_VERSION` | `latest` | Image tag for every Reloop service |
The queue dashboard at `https://reloop.example.com/api/workflow/jobs` uses
`SERVICE_USER_WORKBENCH` and `SERVICE_PASSWORD_WORKBENCH` for basic auth.
---
## Custom tracking domains
A sending domain with click or open tracking gets a `link.` CNAME pointing at
`link.reloop.example.com`, so tracked links in its mail use the customer's own
hostname. Coolify's proxy only issues certificates for domains it knows about,
so add each tracking hostname to the `links` service after the existing one:
```text
https://link.reloop.example.com,https://link.customer.com
```
Keep `https://link.reloop.example.com` first. Reloop uses the first domain in
the list as the instance's tracking host and as the CNAME target for every new
sending domain. Choose **Use this port anyway** again when Coolify asks, then
save and restart. The VPS installer issues these certificates automatically; on
Coolify this step is manual.
---
## Mail ports and TLS
| Port | Service | Protocol |
| :--- | :--- | :--- |
| `25` | `inbound` | SMTP with STARTTLS, for mail your verified domains receive |
| `587` | `smtp` | Submission with STARTTLS |
| `465` | `smtp` | The same STARTTLS listener as `587`, not implicit TLS |
Both mail services use a self-signed certificate. Submit on `587` with
STARTTLS, and tell clients that verify certificates to accept it.
---
## Updates and backups
Pulling the latest images and restarting reruns the `migrate` service, which
pushes the database schema before the Reloop backend services start. Take a
backup first: open the `postgres` service, go to **Backups** and schedule one,
or run a backup on demand before each update.
Updating only changes the images. Coolify keeps the service list and the
`proxy` Caddyfile from your first install. When a Reloop release changes
`reloop.yaml`, paste the new file under **Edit Compose File**, then replace the
Caddyfile under **Persistent Storage** on the `proxy` service with the one from
the new template, and restart.
Each update leaves the previous images behind, about 4 GB per update. Turn on
Coolify's Docker cleanup for the server, or run `docker image prune` after
updating.
---
## Things to avoid
- **Connecting the stack to Coolify's predefined network.** It puts the
internal SMTP injection API, NATS and Rspamd on a network every other Coolify
resource shares.
- **arm64 servers.** The dashboard and links images are built for amd64 only.
- **Routing the mail ports through Coolify's proxy.** SMTP authentication
needs the TLS session to reach the `smtp` container directly.
Campaign sends, automation email steps, inbox replies and system mail through
`RELOOP_API_KEY` call `https://reloop.example.com` from inside the stack, so
the server must reach its own public address over HTTPS. They fail until
Coolify has issued the certificate. To check, open a terminal on the `auth`
container and run
`wget -qO- https://reloop.example.com/api/auth/v1/get-session`: any response
other than a connection or TLS error means it works.
---
# self-host/dokploy.mdx
Source: https://reloop.sh/docs/self-host/dokploy
Markdown: https://reloop.sh/docs/self-host/dokploy.md
Reloop is available as a Dokploy template. It runs the same stack as the
[VPS installer](/docs/self-host/vps), with Dokploy generating every secret and
Traefik handling HTTPS.
---
## Before you start
| Requirement | Value |
| :--- | :--- |
| **Dokploy** | A recent release, with the default Traefik proxy |
| **Architecture** | x86_64 (the dashboard and links images are amd64 only) |
| **RAM** | 8 GB for Reloop and Dokploy together |
| **Disk** | 50 GB. The container images alone take about 22 GB |
| **Ports** | `25`, `465` and `587` free on the server, inbound and outbound |
| **DNS** | Three hostnames pointing at the server, see [DNS](#dns) |
Many providers block port `25` by default. Reloop delivers to recipient mail
servers on outbound `25`, and receives mail on inbound `25`, so ask your
provider to open it.
---
## Deploy
1. In Dokploy, open your project and choose **Create Service** → **Template**.
2. Search for **Reloop** and create it.
3. Before the first deploy, open **Domains** and replace the generated host of
both domains, keeping HTTPS on:
| Service | Host |
| :--- | :--- |
| `proxy` | `reloop.example.com` |
| `links` | `link.reloop.example.com` |
4. Open **Environment** and set the same hosts in `BASE_URL`, `HOST_DOMAIN`,
`SMTP_HOSTNAME`, `TRACKING_DOMAIN` and `TRACKING_BASE_URL`. The URL variables
start with `https://`, the others are the bare hostname.
5. Click **Deploy**. The first deploy downloads about 5 GB of images, which
unpack to about 22 GB.
Dokploy generates the database password, the Redis password and every Reloop
secret, and stores them under **Environment**. Change none of the generated
values after the first deploy: the database password is fixed once Postgres
initialises, changing the tracking or preferences secrets breaks links in mail
that was already sent, and changing the webhook key breaks the signature on
every existing webhook.
Set the real domains before you add any sending domain in the dashboard.
Reloop writes the DNS records it shows you at the moment a domain is created,
so a domain added while the instance still runs on Dokploy's generated
`sslip.io` address keeps pointing there.
---
## DNS
```text
reloop.example.com A 203.0.113.10
link.reloop.example.com A 203.0.113.10
inbound.reloop.example.com A 203.0.113.10
reloop.example.com TXT "v=spf1 ip4:203.0.113.10 -all"
```
- The apex record serves the dashboard and the API.
- `link.` serves click and open tracking and the unsubscribe pages.
- `inbound.` is the MX target for mail your verified domains receive. The
template always uses `inbound.` plus `HOST_DOMAIN`.
- The SPF record authorises the server to send for the host domain, which is
what the `include:` in each sending domain's SPF resolves to.
Also ask your provider for a PTR record on the server IP that resolves to
`reloop.example.com`. Receiving servers check it.
---
## First sign-in
1. In Dokploy, open **Environment** and copy the value of `ADMIN_SETUP_KEY`.
2. Open `https://reloop.example.com/dashboard/setup` and paste it as the setup key.
3. Choose the first administrator's name, email and password.
4. Name the first organization. Leave the instance name empty: on Dokploy it
has no effect, so set `APP_NAME` under **Environment** instead.
The key works once. If you think it leaked before setup, change
`ADMIN_SETUP_KEY` and redeploy: the old key stops working.
Setup signs you in with a session that lasts 7 days. To sign in again after
that, Reloop emails you a code, so configure system email before it runs out.
---
## System email
Sign-in codes and invitations need a way out. Set these under **Environment**,
then redeploy:
| Variable | Value |
| :--- | :--- |
| `RELOOP_SENDER_DOMAIN` | The domain system mail is sent from. Always required |
| `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `SMTP_SECURE` | Any SMTP provider you already use (Postmark, SES, your own). `SMTP_SECURE=true` means implicit TLS, normally with port `465` |
| `RELOOP_API_KEY` | Instead of SMTP: an API key of the organization that owns `RELOOP_SENDER_DOMAIN`, once that domain is verified in this instance |
---
## Configuration
Everything below is optional and listed under **Environment** with its default.
| Variable | Default | Purpose |
| :--- | :--- | :--- |
| `DISABLE_SIGNUP` | `true` | Blocks public registration. Invited addresses can still join. If setup leaves public sign-ups off, it also saves that in `/run/reloop/.env` on the `auth` service's volume, which keeps registration closed even when this is `false`. To open it later, set this to `false`, remove the `DISABLE_SIGNUP` line from that file in the `auth` container, and redeploy |
| `DISABLE_ORG_CREATION` | `false` | Stops users creating further organizations |
| `APP_NAME` | `Reloop` | Instance name in system email, shown as `Self-hosted Reloop × ` |
| `S3_ENDPOINT`, `S3_ACCESS_KEY`, `S3_SECRET_KEY` | empty | S3-compatible storage for template images and attachments. The bucket must serve objects publicly, because Reloop hands out URLs as `{S3_ENDPOINT}/{S3_BUCKET}/{path}` |
| `S3_BUCKET` | `reloop-uploads` | Bucket name |
| `S3_REGION` | `us-east-1` | Bucket region |
| `RELOOP_VERSION` | `latest` | Image tag for every Reloop service |
The queue dashboard at `https://reloop.example.com/api/workflow/jobs` uses
`WORKBENCH_USER` and `WORKBENCH_PASS` for basic auth.
---
## Custom tracking domains
A sending domain with click or open tracking gets a `link.` CNAME pointing at
`link.reloop.example.com`, so tracked links in its mail use the customer's own
hostname. Traefik only issues certificates for domains it knows about, so add
each tracking hostname as another domain on the `links` service, port `3005`,
with HTTPS on. Keep `TRACKING_DOMAIN` set to `link.reloop.example.com`: Reloop
uses it as the CNAME target for every new sending domain.
---
## Mail ports and TLS
| Port | Service | Protocol |
| :--- | :--- | :--- |
| `25` | `inbound` | SMTP with STARTTLS, for mail your verified domains receive |
| `587` | `smtp` | Submission with STARTTLS |
| `465` | `smtp` | The same STARTTLS listener as `587`, not implicit TLS |
The template publishes these ports on the host directly, outside Traefik, so
the deploy fails if another mail server already binds them. Both mail services
use a self-signed certificate. Submit on `587` with STARTTLS, and tell clients
that verify certificates to accept it.
---
## Updates and backups
Dokploy only pulls new images on redeploy when pulling images is enabled in the
service's settings. With it on, each redeploy fetches the tag in
`RELOOP_VERSION` and reruns the `migrate` service, which pushes the database
schema before the Reloop backend services start. Take a backup of the
`postgres-data` volume first.
Each update leaves the previous images behind, about 4 GB per update. Run
`docker image prune` on the server after updating.
---
# self-host/index.mdx
Source: https://reloop.sh/docs/self-host
Markdown: https://reloop.sh/docs/self-host.md
Reloop is fully open-source and designed to be easily self-hosted on your own servers or cloud providers. By hosting Reloop yourself, you maintain complete ownership of your email data, templates, and analytics logs.
---
## Deployment Options
You can self-host Reloop in production using several approaches:
- **Docker Compose**: Recommended for small to medium deployments, staging environments, and single-instance setups.
- **Kubernetes**: Recommended for high-availability, auto-scaling production deployments.
- **Bare Metal / Virtual Machines**: For running the services directly using Bun/Node.js.
---
## Platform Deployments
Guides for Coolify, CapRover and a plain VPS are ready. Templates for the other platforms below are coming soon.
Deploy the full stack from a Docker Compose template on your Coolify server.
Deploy the full stack from a one-click template on your CapRover server.
Easily deploy on your self-hosted Dokploy instance. (Coming Soon)
One-command installer for a fresh Ubuntu or Debian server.
Deploy the frontend and serverless endpoints on Vercel. (Coming Soon)
Deploy the entire Reloop stack on Railway with one click. (Coming Soon)
Enterprise-grade cloud deployment on Microsoft Azure. (Coming Soon)
Run the frontend and edge worker endpoints on Cloudflare. (Coming Soon)
Deploy and host the web application on Netlify. (Coming Soon)
---
# self-host/netlify.mdx
Source: https://reloop.sh/docs/self-host/netlify
Markdown: https://reloop.sh/docs/self-host/netlify.md
Coming Soon
---
# self-host/railway.mdx
Source: https://reloop.sh/docs/self-host/railway
Markdown: https://reloop.sh/docs/self-host/railway.md
Coming Soon
---
# self-host/requirements.mdx
Source: https://reloop.sh/docs/self-host/requirements
Markdown: https://reloop.sh/docs/self-host/requirements.md
Before deploying the Reloop stack, ensure your server environment meets the hardware and software specifications outlined below.
## Hardware Requirements
Reloop's performance is highly dependent on database throughput and the speed of processing background queues.
| Resource | Minimum Requirement | Recommended (Production) |
| :--- | :--- | :--- |
| **CPU** | 2 vCPUs | 4+ vCPUs |
| **RAM** | 4 GB | 8 GB+ |
| **Storage** | 20 GB SSD | 50 GB+ NVMe SSD |
> [!NOTE]
> The Docker Compose deployment needs more disk than the numbers above: the container images alone are about 22 GB, so plan for 35 GB minimum and 50 GB comfortably. See [Deploy on VPS](/docs/self-host/vps).
> [!NOTE]
> SSD/NVMe storage is highly recommended. PostgreSQL stores relational data and telemetry logs, which benefit from fast disk I/O speeds to prevent latency.
---
## Software Requirements
Verify the following software and runtimes are installed on your hosting server:
* **Docker**: Version `24.0.0+`
* **Docker Compose**: Version `2.20.0+`
* **Bun**: Version `1.3.0+` (Only required if you run the microservices natively outside of Docker containers, or during the initial environment variables setup).
* **Git**: For cloning the repository and checking out configurations.
---
## Network Requirements
Domain verification reads your published SPF, DKIM, DMARC and MX records from
public DNS. It queries upstream resolvers directly rather than the host's
resolver, because a host resolver is frequently a link-local or VPN address that
is unreachable from inside the container network. The symptom is every record
failing with `ENOTFOUND` while the same lookup succeeds from the host shell.
The `domain` and `workflow` services need:
* **Outbound UDP port 53** to the resolvers they are configured to use. Allow **TCP port 53** as well; responses larger than 512 bytes fall back to TCP, which DKIM keys routinely exceed.
### Choosing resolvers
`DNS_RESOLVERS` in your `.env` sets the upstream resolvers, as a comma-separated
list of IP addresses. It defaults to `8.8.8.8,8.8.4.4`, and falls back to that
default if the value is empty or is not a valid address.
```bash
DNS_RESOLVERS=1.1.1.1,9.9.9.9
```
Point this at a reachable resolver when outbound 53 to the public resolvers is
blocked, or when you run an internal resolver you would rather use. Restart the
two services afterwards:
```bash
reloop restart domain workflow
```
> [!NOTE]
> Use resolvers that answer for the public internet. An internal split-horizon
> resolver may return private answers for your sending domain, or none at all,
> and verification will fail even though the records are published correctly.
### Docker resolver configuration
`docker-compose.yml` also sets `dns` on every backend service, so containers
that fall back to the system resolver still reach a working one:
```yaml
x-backend: &backend
dns:
- 1.1.1.1
- 8.8.8.8
```
This does not affect service discovery. Docker keeps its embedded resolver at
`127.0.0.11` inside the container and uses the `dns` list only as upstream
forwarders, so `postgres`, `redis` and `nats` continue to resolve by name.
---
# self-host/services.mdx
Source: https://reloop.sh/docs/self-host/services
Markdown: https://reloop.sh/docs/self-host/services.md
Reloop is built using a highly modular microservice architecture. This separation allows you to scale specific components (such as mail queues or logs processing) depending on your server workload.
## Component Breakdown
The Reloop stack consists of **20 application services** (3 frontends, 17 backends) and **9 external backing systems**.
### 1. Frontend Services (3)
* **`web`**: The main public landing page and landing website.
* **`dashboard`**: The developer console where users manage domains, write templates, view delivery metrics, configure webhooks, and generate API keys.
* **`docs`**: The documentation portal you are viewing right now.
### 2. Backend Services (16)
* **`auth`**: Handles user registrations, authentications, and organization-scoped permission checks.
* **`api-key`**: Validates developer API tokens for programmatic integrations.
* **`domain`**: Verifies and manages domain records configurations (SPF, DKIM, MX, DMARC).
* **`mail`**: Manages template assembly, parsing, queuing, and delivery job dispatching.
* **`webhook`**: Dispatches delivery events (delivered, bounced, opened, clicked) to external webhooks.
* **`contacts`**: Handles subscriber lists, contact profiles, segments, and unsubscribe requests.
* **`logs`**: Centralizes activity streams and debug logs.
* **`workflow`**: Manages campaign automation workflows, timing wait steps, and drip actions.
* **`upload`**: Manages file storage attachments and static image uploads.
* **`template`**: Compiles dynamic template variables before mailing dispatch.
* **`campaigns`**: Broadcast campaigns with audience snapshot, schedule, send, and engagement stats.
* **`credits`**: Tracks account credit balances, billing cycles, and quotas.
* **`email`**: The core API service mapping outbound HTTP sending requests to the queue processor.
* **`inbox`**: Manages receiving inbox threads and incoming messages.
* **`smtp`**: Outbound SMTP submission server using KumoMTA.
* **`inbound`**: Inbound MX SMTP processing receiver using KumoMTA.
* **`spam`**: Anti-spam scoring backend using Rspamd (called by inbound over HTTP).
### 3. Supporting Infrastructure (6)
* **PostgreSQL**: Stores persistent relational data, email delivery logs, and activity logs.
* **Redis**: Coordinates task queues for BullMQ worker threads.
* **MinIO**: S3-compliant file storage.
* **NATS JetStream**: Real-time event broker.
* **Mailpit**: SMTP sandbox mail catcher.
* **Caddy**: High-performance reverse proxy and automatic SSL manager.
---
## What the VPS installer deploys
The [one-command VPS installer](/docs/self-host/vps) deploys a reduced set: 20 containers instead of the full platform. It leaves out the pieces that only exist for the hosted service:
| Left out | Why |
| :--- | :--- |
| `web`, `docs` | Public marketing site and documentation; `/docs` redirects to reloop.sh |
| `credits` | Billing and quota metering; self-hosted sending is not metered |
| `tools` | Public email-validation tools |
| `admin`, `console` | Operator tooling for the hosted platform |
| MinIO | Point `upload` at any S3-compatible provider instead |
| Mailpit | Development mail catcher, never used in production |
`upload` is deployed only when you configure S3 storage during install.
---
# self-host/vercel.mdx
Source: https://reloop.sh/docs/self-host/vercel
Markdown: https://reloop.sh/docs/self-host/vercel.md
Coming Soon
---
# self-host/vps.mdx
Source: https://reloop.sh/docs/self-host/vps
Markdown: https://reloop.sh/docs/self-host/vps.md
The Reloop self-host installer takes a fresh VPS and turns it into a running Reloop deployment: it installs Docker, generates production secrets, writes the Compose stack, applies the database schema, starts every service, verifies it is healthy, and prints the DNS records you need to add.
```bash
curl -fsSL https://reloop.sh/install.sh | sudo bash
```
`https://reloop.sh/install.sh` is the canonical address. It serves
[`install/install.sh`](https://github.com/reloop-labs/reloop/blob/main/install/install.sh)
from the Reloop repository, and the script downloads the rest of the installer
(`install/lib` and `install/templates`) from the same place. To read it before
running it as root:
```bash
curl -fsSL https://reloop.sh/install.sh -o install.sh
less install.sh
sudo bash install.sh
```
---
## Before you start
| Requirement | Value |
| :--- | :--- |
| **Operating system** | Ubuntu 22.04 LTS, Ubuntu 24.04 LTS, Debian 12, Debian 13 |
| **Architecture** | x86_64 |
| **CPU** | 2 vCPUs minimum, 4+ recommended |
| **RAM** | 4 GB minimum, 8 GB recommended |
| **Disk** | 35 GB minimum, 50 GB recommended |
| **Ports** | `25`, `465`, `587` free, plus `80` and `443` unless you run [behind your own reverse proxy](#behind-an-existing-reverse-proxy) |
| **Access** | root or sudo |
> [!NOTE]
> The container images are roughly 22 GB in total, which is why the disk floor is higher than for a source install. The installer refuses to continue on a smaller disk rather than failing halfway through the download.
Many providers block outbound port `25` by default. Reloop still sends through the submission ports, but inbound mail needs `25` open in both directions. Ask your provider to unblock it if you plan to receive mail.
---
## What the installer asks
| Prompt | Purpose | Default |
| :--- | :--- | :--- |
| Primary Reloop domain | The hostname the dashboard and API are served on | - |
| TLS contact email | Let's Encrypt expiry and account notices | - |
| Database name | PostgreSQL database | `reloop` |
| Database user | PostgreSQL role | `reloop` |
| Run behind an existing reverse proxy | Serve plain HTTP on a local port for your own Caddy, Nginx or Traefik instead of taking `80` and `443`. See [Behind an existing reverse proxy](#behind-an-existing-reverse-proxy) | `no` |
| Local port for your reverse proxy | Only asked behind a proxy. Reloop listens on `127.0.0.1` at this port | `8080` |
| Configure automatic HTTPS | Let Caddy obtain and renew certificates. Behind your own proxy it asks whether that proxy serves Reloop over HTTPS instead | `yes` |
| Configure object storage | S3-compatible storage for file uploads | `no` |
Everything else is generated: database and Redis passwords, the Better Auth secret, the internal service secret, the tracking and preferences secrets, and the webhook encryption key. They are written to `/opt/reloop/.env` with mode `0600`.
Enter the domain as a bare hostname: `reloop.example.com`, not `https://reloop.example.com` and not `reloop.example.com/admin`.
---
## What gets installed
```text
/opt/reloop/
├── .env production configuration and secrets (0600, root)
├── admin-setup.key one-time first-run setup key (0600, emptied after setup)
├── docker-compose.yml the production stack
├── Caddyfile reverse proxy and TLS
└── backups/ automatic database dumps taken before schema changes
```
The stack runs 20 containers: 14 Reloop backend services, 2 frontends (dashboard, links), PostgreSQL, Redis, NATS and Caddy. Only Caddy (`80` and `443`, or `127.0.0.1:` behind your own proxy) and the two mail transports (`25` inbound, `465`/`587` submission) publish host ports. The database and event bus stay on the internal Docker network.
A self-hosted install deliberately leaves out the parts of Reloop that only make sense for the hosted service: the public documentation site (`/docs` redirects to reloop.sh), the credits and quota service, the public email-validation tools, the operator console, and bundled object storage. Sending is not metered.
The installer writes `SELF_HOSTED=true` to `.env`, which turns off every plan limit: sending quotas, the inbox, domain and organization caps, and dedicated IP entitlements. Installations from before this setting existed need it added by hand: put `SELF_HOSTED=true` in `/opt/reloop/.env` and run `reloop restart`.
A `reloop` command is installed alongside it:
```bash
reloop status # container health
reloop logs [service] # follow logs
reloop restart [service] # recreate the stack or one service, applying .env changes
reloop update # pull the configured version and redeploy
```
---
## File uploads
Reloop stores template images and attachments in S3-compatible object storage. The installer does not bundle a storage server. Point it at whichever provider you already use (AWS S3, Cloudflare R2, Hetzner Object Storage, Backblaze B2, a MinIO box of your own).
Answer yes to the storage prompt and supply the endpoint, access key, secret key, bucket and region. The `upload` service is then deployed alongside the rest of the stack.
Answer no and file uploads stay off. To turn them on later, set the `S3_*` values and `COMPOSE_PROFILES=storage` in `/opt/reloop/.env`, then run `reloop restart`.
> [!NOTE]
> The bucket must serve objects publicly over HTTP. Reloop hands out file URLs as `{S3_ENDPOINT}/{S3_BUCKET}/{path}`, so anything the dashboard uploads has to be readable at that address.
---
## DNS
When the installer finishes it prints the records for the installation itself:
```text
reloop.example.com A 203.0.113.10
link.reloop.example.com A 203.0.113.10
inbound.reloop.example.com A 203.0.113.10
reloop.example.com TXT "v=spf1 ip4:203.0.113.10 -all"
```
- The apex record serves the dashboard, the API and the Let's Encrypt HTTP challenge.
- `link.` serves click and open tracking plus the unsubscribe pages, and is the CNAME target for customer tracking domains.
- `inbound.` is the MX target for mail your verified domains receive.
- The SPF record authorises the server to send for the host domain, which is what the `include:` in each sending domain's SPF resolves to.
These are separate from the SPF, DKIM, DMARC and MX records for the domains you send from. Those are generated per domain by Reloop. Add a domain under **Domains** in the dashboard and it shows the exact records. See [Connect a domain](/docs/guides/connect-domain). **Auto populate** only works on Reloop Cloud, because the Domain Connect template is tied to it, so add these records by hand with [manual setup](/docs/guides/connect-domain/manual-setup).
You can run the installer before adding DNS. Caddy keeps retrying certificate issuance in the background and HTTPS starts working on its own once the records resolve. Follow it with `reloop logs proxy`.
---
## Behind an existing reverse proxy
Use this when the server already runs Caddy, Nginx or Traefik on `80` and
`443`, for example alongside other sites. Answer yes to **Run behind an
existing reverse proxy**, or set `RELOOP_EXTERNAL_PROXY=true` for an
unattended install. The installer then:
- publishes the bundled Caddy on `127.0.0.1:` only, over plain HTTP, and no longer needs `80` or `443`
- keeps routing every Reloop path itself, so your proxy forwards whole hostnames
- trusts `X-Forwarded-For` and `X-Forwarded-Proto` from private addresses, so Reloop sees the visitor's IP and the `https` scheme
Answer yes to **Does your proxy serve Reloop over HTTPS** if it does. That sets
the `https://` URLs Reloop puts in links and emails.
To move the port later, change `RELOOP_PROXY_PORT` in `/opt/reloop/.env` and
run `reloop restart proxy`.
### What your proxy needs to do
| Hostname | Forward to | TLS certificate |
| :--- | :--- | :--- |
| `reloop.example.com` | `http://127.0.0.1:8080` | Issued and renewed by your proxy |
| `link.reloop.example.com` | `http://127.0.0.1:8080` | Issued and renewed by your proxy |
| Customer tracking domains | `http://127.0.0.1:8080` | Issued on demand, see below |
- **Keep the original `Host` header.** Reloop decides between the dashboard and the tracking site by hostname.
- **Pass WebSocket upgrades.** Live email logs, the inbox and template collaboration use them.
- **Set `X-Forwarded-For` and `X-Forwarded-Proto`.**
`inbound.reloop.example.com` needs no proxy route. It is only an MX target,
and Reloop serves port `25` directly.
**Custom tracking domains.** A customer can point their own hostname, such as
`track.customer.com`, at `link.reloop.example.com` with a CNAME, and it needs a
certificate as soon as it is added. Reloop answers Caddy's on-demand TLS check
at `https://reloop.example.com/api/domain/v1/caddy/ask?domain=`,
returning `200` for an active tracking domain and `404` otherwise, so Caddy
issues these automatically. Nginx and Traefik have no equivalent: add a route
and certificate for each tracking domain yourself, or don't offer custom
tracking domains.
### Caddy
```caddyfile
{
on_demand_tls {
ask https://reloop.example.com/api/domain/v1/caddy/ask
}
}
reloop.example.com, link.reloop.example.com {
reverse_proxy 127.0.0.1:8080
}
https:// {
tls {
on_demand
}
reverse_proxy 127.0.0.1:8080
}
```
Merge `on_demand_tls` into your existing global options block. The `https://`
block catches every hostname your other sites don't claim, which is what lets
tracking domains work; if you already have a catch-all site, route tracking
domains from there instead. Caddy keeps the `Host` header, sets the forwarded
headers and passes WebSockets without extra configuration, and renews its
certificates itself.
### Nginx
```nginx
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name reloop.example.com link.reloop.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name reloop.example.com link.reloop.example.com;
ssl_certificate /etc/letsencrypt/live/reloop.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/reloop.example.com/privkey.pem;
client_max_body_size 25m;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
```
The certificate must cover both hostnames, for example
`certbot certonly --nginx -d reloop.example.com -d link.reloop.example.com`.
Certbot's timer renews it. `client_max_body_size` raises Nginx's 1 MB default:
uploads and email attachments go up to 10 MB, and attachments grow by about a
third when base64-encoded in an API request.
### Traefik
Traefik usually runs in a container, where `127.0.0.1` is the Traefik container
itself. Put Traefik on Reloop's Docker network and forward to the Reloop proxy
container by name. In Traefik's compose file:
```yaml
services:
traefik:
networks:
- reloop
networks:
reloop:
external: true
```
Then add a router and service, here with the file provider:
```yaml
http:
routers:
reloop:
rule: Host(`reloop.example.com`) || Host(`link.reloop.example.com`)
entryPoints:
- websecure
service: reloop
tls:
certResolver: letsencrypt
services:
reloop:
loadBalancer:
servers:
- url: http://reloop-proxy:80
```
Replace `websecure` and `letsencrypt` with your own entry point and certificate
resolver. Traefik passes the `Host` header and WebSockets by default and renews
certificates through the resolver.
The same applies to Caddy or Nginx running in a container: join the `reloop`
network and forward to `http://reloop-proxy:80` instead of `127.0.0.1`.
---
## TLS certificates and mail ports
| Hostname or port | With the bundled Caddy | Behind your own proxy |
| :--- | :--- | :--- |
| `reloop.example.com` | Let's Encrypt, issued and renewed by Caddy | Your proxy |
| `link.reloop.example.com` | Let's Encrypt, issued and renewed by Caddy | Your proxy |
| Customer tracking domains | Issued on demand and renewed by Caddy | Caddy on demand, or by hand |
| Port `25`, `587`, `465` | Self-signed certificate, see below | Same |
The mail ports are published by Docker directly and never pass through the
HTTP proxy, in either setup.
| Port | Used by | Protocol |
| :--- | :--- | :--- |
| `25` | Other mail servers delivering to your verified domains | SMTP with STARTTLS |
| `587` | Your applications submitting mail to Reloop | SMTP submission with STARTTLS |
| `465` | Your applications submitting mail to Reloop | The same STARTTLS listener as `587`, not implicit TLS |
The SMTP services generate a self-signed certificate, since no certificate is
mounted into them. Most sending servers delivering to port `25` accept that
for opportunistic TLS. For submission, use port `587` with STARTTLS: a client set to
implicit TLS (often labelled SSL/TLS or SMTPS) on `465` will fail, and a client
that verifies certificates has to be told to accept this one.
---
## First sign-in
The installer prints a one-time administrator setup key when a new installation
finishes. It also saves the key to `/opt/reloop/admin-setup.key`, a root-only
file, in case you need to retrieve it before completing setup.
1. Open `https://your-domain/dashboard/setup`
2. Enter the setup key
3. Choose the first administrator's name, email and password
4. Name the instance and first organization
5. Leave **Turn off public sign-ups** selected for a private instance, or clear
it if anyone should be able to register
The first account is created as a `super-admin`. Completing setup consumes the
key, empties `admin-setup.key`, sets `SETUP_MODE=false` and closes the setup
page. The installer only prints the key once.
To supply your own key, download the installer before running it:
```bash
curl -fsSL https://reloop.sh/install.sh -o install.sh
sudo bash install.sh --admin-key='your-long-random-key'
```
`RELOOP_ADMIN_EMAIL` is only the TLS contact address; it does not preselect or
create the administrator account. Self-host setup uses a password, not an
emailed code. Reloop Cloud's one-time-code sign-in is unrelated and unchanged.
If the setup flow is unavailable after an account already exists,
`apps/backend/admin/scripts/promote-admin.ts` remains available as a
break-glass recovery tool. It is not the normal first-run setup path.
To change registration or organization creation later, edit these values in
`/opt/reloop/.env` and run `reloop restart auth`:
| Variable | Effect |
| :--- | :--- |
| `DISABLE_SIGNUP=true` | No new accounts on any sign-up path. Existing users still sign in, and invited addresses can still join |
| `DISABLE_ORG_CREATION=true` | Nobody can create further organizations |
The installer keeps both values across re-runs, so an update will not reopen
registration. See [Closing registration](/docs/setup/backend/auth#closing-registration).
If sign-in succeeds but the dashboard then answers `401` on every protected
page, the services cannot reach the auth service over your public origin. Set
`AUTH_INTERNAL_BASE_URL=http://auth:8000` in `/opt/reloop/.env` and run
`reloop restart`. See [session validation between
services](/docs/setup/backend/auth#session-validation-between-services).
---
## Configuration reference
Everything lives in `/opt/reloop/.env`. The installer writes it and keeps your
edits across re-runs. After changing a value, run `reloop restart`, or
`reloop restart ` for one service.
The `reloop` command from earlier installers ran a plain `docker compose
restart`, which does not re-read `.env`. If `reloop restart` does not pick up a
change, run `reloop start` instead, or re-run the installer to update the
command.
| Variable | Purpose |
| :--- | :--- |
| `RELOOP_VERSION` | Image tag deployed for every service |
| `RELOOP_DOMAIN` | Dashboard and API hostname |
| `RELOOP_TRACKING_HOST` / `RELOOP_INBOUND_HOST` | `link.` and `inbound.` hostnames, derived from `RELOOP_DOMAIN` |
| `RELOOP_ADMIN_EMAIL` | Let's Encrypt contact address only |
| `RELOOP_PUBLIC_IP` | Address printed in the DNS records and SPF |
| `RELOOP_HTTPS` | Whether Reloop is reached over `https://` |
| `RELOOP_EXTERNAL_PROXY` / `RELOOP_PROXY_PORT` | Run behind your own proxy, and the local port it forwards to |
| `BASE_URL` / `TRACKING_BASE_URL` | Public origins used in links and emails, generated from the values above |
| `RELOOP_SITE_ADDRESS` / `RELOOP_TRACKING_SITE_ADDRESS` | Site addresses for the bundled Caddy, generated |
| `POSTGRES_*`, `REDIS_PASSWORD`, `BETTER_AUTH_SECRET`, `RELOOP_INTERNAL_SECRET`, `TRACKING_SECRET`, `PREFERENCES_SECRET`, `WEBHOOK_ENCRYPTION_KEY` | Generated once and kept across re-runs. Back them up with the file and don't replace them by hand |
| `SETUP_MODE` / `ADMIN_SETUP_KEY_FILE` | First-run setup state and the setup key path inside the auth container |
| `DISABLE_SIGNUP` / `DISABLE_ORG_CREATION` | Close registration and organization creation |
| `AUTH_INTERNAL_BASE_URL` | Internal origin for session checks between services |
| `APP_NAME` | Name used in system email. See [Branding system email](/docs/setup/backend/email#branding-system-email) |
| `RELOOP_API_KEY`, `RELOOP_SENDER_DOMAIN`, `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `SMTP_SECURE` | How system email is delivered. See [How system email is delivered](/docs/setup/backend/email#how-system-email-is-delivered) |
| `SMTP_HOSTNAME` | Hostname senders connect to for submission, shown in the dashboard |
| `COMPOSE_PROFILES`, `S3_*` | File uploads, see [File uploads](#file-uploads) |
| `DNS_RESOLVERS` | Resolvers used to verify DKIM, SPF, DMARC and MX records |
Per-service variables are documented under [Backend services](/docs/setup/backend/auth).
---
## Running it again
Re-running the installer is safe. It detects the existing installation and offers to abort, reconfigure (re-ask the domain and HTTPS settings while keeping data and secrets), or redeploy with the existing configuration. Secrets, volumes and the database are never regenerated or dropped, and a database dump is taken before any schema change.
---
## Updating and rolling back
```bash
reloop update
```
This pulls the images for `RELOOP_VERSION`, dumps the database to
`/opt/reloop/backups/pre-migration-.sql.gz`, applies the schema and
recreates the containers that changed. It does not touch
`docker-compose.yml`, the `Caddyfile` or the `reloop` command. To pick up
changes to those as well, re-run the install command and choose to redeploy with
the existing configuration.
Reloop images are published as `latest`, and not every image has a per-release
tag yet, so rolling back to an earlier image version is not supported. What you
can roll back is the database, to the dump taken before an update:
```bash
cd /opt/reloop
ls -t backups/
reloop stop
docker compose up -d postgres
docker compose exec -T postgres psql -U reloop -d reloop \
-c 'drop schema public cascade; create schema public authorization reloop'
gunzip -c backups/pre-migration-20260917101500.sql.gz |
docker compose exec -T postgres psql -U reloop -d reloop -v ON_ERROR_STOP=1
reloop start
```
Run these as root, use the file name `ls` shows, and replace `reloop` after
`-U` and `-d` if you changed the database user or name. This discards everything
written since the dump was taken.
---
## Unattended installs
Every prompt has an environment variable, so the installer can run without a terminal:
```bash
curl -fsSL https://reloop.sh/install.sh | sudo \
RELOOP_NONINTERACTIVE=true \
RELOOP_DOMAIN=reloop.example.com \
RELOOP_ADMIN_EMAIL=admin@example.com \
RELOOP_PUBLIC_IP=203.0.113.10 \
bash
```
| Variable | Purpose |
| :--- | :--- |
| `RELOOP_NONINTERACTIVE` | Never prompt; fail if a required value is missing |
| `RELOOP_DOMAIN` | Primary hostname |
| `RELOOP_ADMIN_EMAIL` | TLS contact email |
| `RELOOP_ADMIN_SETUP_KEY` | Optional administrator setup key override; equivalent to `--admin-key=` |
| `RELOOP_DB_NAME` / `RELOOP_DB_USER` | Database name and role |
| `RELOOP_HTTPS` | `yes` or `no`. Behind your own proxy, whether that proxy serves HTTPS |
| `RELOOP_EXTERNAL_PROXY` | `yes` to run behind your own reverse proxy instead of taking `80` and `443` |
| `RELOOP_PROXY_PORT` | Local port your proxy forwards to (default `8080`) |
| `RELOOP_S3` | `yes` to configure object storage, `no` to disable file uploads |
| `RELOOP_S3_ENDPOINT` / `RELOOP_S3_ACCESS_KEY` / `RELOOP_S3_SECRET_KEY` | Object storage credentials |
| `RELOOP_S3_BUCKET` / `RELOOP_S3_REGION` | Bucket and region |
| `RELOOP_PUBLIC_IP` | Skip public address detection |
| `RELOOP_VERSION` | Image tag to deploy (default `latest`) |
| `RELOOP_EXISTING` | `abort`, `reconfigure` or `continue` for a re-run |
| `RELOOP_INSTALL_DIR` | Installation directory (default `/opt/reloop`) |
---
# setup/backend/api-key.mdx
Source: https://reloop.sh/docs/setup/backend/api-key
Markdown: https://reloop.sh/docs/setup/backend/api-key.md
Generation, hashing, validation, and revocation of developer API keys.
Run [`bun setup`](/docs/setup) once from the monorepo root. Env files come from `bun setup` / `bun env:setup`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/api-key` |
| **Port** | `8012` |
| **Local URL** | `https://local.reloop.sh/api/api-key` |
| **Swagger UI** | `https://local.reloop.sh/api/api-key/openapi` |
| **Stack** | ElysiaJS · Better Auth API Key · PostgreSQL · Redis · NATS |
## Quick start
```bash
bun be:api-key:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/api-key/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
PORT=8012
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
NODE_ENV=development
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://...` |
| `REDIS_URL` | **YES** | `redis://...` |
| `PORT` | **YES** | `8012` |
| `BASE_URL` | **YES** | `https://local.reloop.sh` |
| `NATS_URL` | **YES** | `nats://localhost:4222` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:api-key:dev` | Start dev server with hot reloading |
| `bun run --filter=be-api-key build` | Compile production bundle |
| `bun run --filter=be-api-key start` | Run compiled production build |
| `bun run --filter=be-api-key check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Auth** | `@better-auth/api-key` handles SHA-256 hashing and key formatting |
| **Caching** | Validated keys are cached in Redis for fast lookups |
| **Events** | Key lifecycle events published to NATS under `api-key.*` |
| **Schema** | `apikey` table in `packages/db/src/schema/api-key.ts` |
## Next
One-command local bootstrap
Gateway routes and ports
Sessions and organizations
API that validates keys on send
---
# setup/backend/auth.mdx
Source: https://reloop.sh/docs/setup/backend/auth
Markdown: https://reloop.sh/docs/setup/backend/auth.md
User signup, login, sessions, organizations, and workspace membership via **Better Auth**.
Run [`bun setup`](/docs/setup) once from the monorepo root (Postgres + Redis via Docker). Env files come from `bun setup` / `bun env:setup`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/auth` |
| **Port** | `8000` |
| **Local URL** | `https://local.reloop.sh/api/auth` |
| **Swagger UI** | `https://local.reloop.sh/api/auth/openapi` |
| **Stack** | ElysiaJS · Better Auth · PostgreSQL · Redis · NATS |
## Quick start
```bash
bun be:auth:dev
```
Or start every backend: `bun backend:dev` / `bun dev`.
Open the dashboard at `https://local.reloop.sh/dashboard`, not `localhost:3001`. Session cookies are scoped to `local.reloop.sh`. Local OTP is `888888`.
## Environment
`apps/backend/auth/.env` (created/merged by `bun setup` / `bun env:setup` from `.env.dev`).
```bash
# Database & Cache
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
# Better Auth
BETTER_AUTH_SECRET=tENkVU4GrhckuRw4Bcfh93EWgXOFcszn
BASE_URL="https://local.reloop.sh"
PORT=8000
NODE_ENV=development
# Event Bus
NATS_URL=nats://localhost:4222
# Local dev helpers
DEFAULT_OTP=888888
# Registration controls
DISABLE_SIGNUP=false
DISABLE_ORG_CREATION=false
# OAuth (optional)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
```
| Variable | Required | Default | Notes |
| :--- | :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://...` | Shared Postgres from Docker |
| `REDIS_URL` | **YES** | `redis://...` | Sessions and rate limits |
| `BETTER_AUTH_SECRET` | **YES** | - | Session encryption key |
| `BASE_URL` | **YES** | `https://local.reloop.sh` | Public origin for OAuth / invites |
| `PORT` | **YES** | `8000` | Host port |
| `NATS_URL` | **YES** | `nats://localhost:4222` | Event bus |
| `DEFAULT_OTP` | No | `888888` | Dev OTP bypass |
| `DISABLE_SIGNUP` | No | `false` | `true` closes registration on every sign-up path, except pending invitations |
| `DISABLE_ORG_CREATION` | No | `false` | `true` stops users creating further organizations |
| `AUTH_INTERNAL_BASE_URL` | No | `BASE_URL` | Origin the services use to validate sessions with each other |
## Closing registration
`DISABLE_SIGNUP=true` refuses **every** path that would create a new account
(password sign-up, email code, and social providers) because it is enforced once
where the user record is written rather than per method. Existing users keep
signing in normally.
The one exception is an address holding a **pending, unexpired organization
invitation**. That keeps an invite-only instance usable: admins go on adding
teammates from **Members → Invite** without reopening registration. An expired
invitation does not grant access.
| Who | Result with `DISABLE_SIGNUP=true` |
| :--- | :--- |
| Existing user signing in | Allowed |
| Address with a pending invitation | Allowed |
| Address with an expired invitation | Refused |
| Anyone else, any method | Refused with `403` |
`DISABLE_ORG_CREATION=true` is separate, and stops users creating further
organizations. Existing organizations, their members and their invitations are
untouched, so this is safe to turn on once your organizations exist.
Both flags are read at startup, so restart the service after changing them:
```bash
reloop restart auth
```
> [!WARNING]
> Set `DISABLE_SIGNUP=true` only after your own account exists. There is no
> bootstrap exemption: with registration closed and no users, nobody can sign
> up and no invitation can be issued.
## Session validation between services
Every backend validates a session by calling `GET /api/auth/v1/get-session` on
the auth service. It uses `BASE_URL` for that, which is your public origin, so
the call leaves the container, goes back through the reverse proxy and comes
in again.
That round trip fails in some proxy setups: TLS terminates at the proxy, the
public hostname does not resolve from inside the container network, or the
certificate is not trusted internally. `fetch` throws, the session cannot be
read, and **protected routes answer `401` straight after a successful login**
while the login itself worked.
Point the services at the auth container instead:
```bash
AUTH_INTERNAL_BASE_URL=http://auth:8000
```
Then restart the backends. Leave it empty and everything keeps using
`BASE_URL`, so existing deployments are unaffected.
> [!NOTE]
> The path is appended exactly as it is today, so `http://auth:8000` becomes
> `http://auth:8000/api/auth/v1/get-session`, the same path the proxy forwards
> to that container. Give the origin only: no trailing path, no trailing slash
> needed.
This changes only service-to-service validation. `BASE_URL` stays the public
origin and is still what invite links and OAuth callbacks use, so it must
remain correct.
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:auth:dev` | Start dev server with hot reloading |
| `bun run --filter=be-auth build` | Compile production bundle |
| `bun run --filter=be-auth start` | Run compiled production build |
| `bun run --filter=be-auth check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Sessions** | Better Auth manages login, OAuth, and cookie sessions via Redis + Postgres |
| **Multi-tenancy** | Users belong to organizations through `member` (`packages/db/src/schema/auth.ts`) |
| **Events** | Auth lifecycle events published to NATS under `auth.*` |
| **First login** | Use OTP `888888` locally when `DEFAULT_OTP` is set |
## Next
One-command local bootstrap
Gateway routes and ports
Product UI that consumes auth
Developer API keys
---
# setup/backend/campaigns.mdx
Source: https://reloop.sh/docs/setup/backend/campaigns
Markdown: https://reloop.sh/docs/setup/backend/campaigns.md
Broadcast email campaigns: audience targeting, scheduling, send, and engagement stats.
Run [`bun setup`](/docs/setup) once from the monorepo root. Env files come from `bun setup` / `bun env:setup`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/campaigns` |
| **Port** | `8027` |
| **Local URL** | `https://local.reloop.sh/api/campaigns` |
| **Swagger UI** | `https://local.reloop.sh/api/campaigns/openapi` |
| **Stack** | ElysiaJS · PostgreSQL · Redis · NATS · BullMQ |
## Quick start
```bash
bun be:campaigns:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/campaigns/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
PORT=8027
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
NODE_ENV=development
```
---
# setup/backend/contacts.mdx
Source: https://reloop.sh/docs/setup/backend/contacts
Markdown: https://reloop.sh/docs/setup/backend/contacts.md
Subscriber profiles, groups, channels, custom properties, and suppression lists.
Run [`bun setup`](/docs/setup) once from the monorepo root. Env files come from `bun setup` / `bun env:setup`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/contacts` |
| **Port** | `8014` |
| **Local URL** | `https://local.reloop.sh/api/contacts` |
| **Swagger UI** | `https://local.reloop.sh/api/contacts/openapi` |
| **Stack** | ElysiaJS · PostgreSQL · Redis · NATS |
## Quick start
```bash
bun be:contacts:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/contacts/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
PORT=8014
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
PREFERENCES_SECRET="reloop-preferences-secret-key-change-in-prod"
NODE_ENV=development
```
`PREFERENCES_SECRET` signs preference-center tokens for [Links](/docs/setup/frontend/links). The service **refuses to start in production** if left at the default.
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://...` |
| `REDIS_URL` | **YES** | `redis://...` |
| `PORT` | **YES** | `8014` |
| `BASE_URL` | **YES** | `https://local.reloop.sh` |
| `NATS_URL` | **YES** | `nats://localhost:4222` |
| `PREFERENCES_SECRET` | Prod only | Dev default in `.env.dev` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:contacts:dev` | Start dev server with hot reloading |
| `bun run --filter=be-contacts build` | Compile production bundle |
| `bun run --filter=be-contacts start` | Run compiled production build |
| `bun run --filter=be-contacts check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Audience** | Contacts, groups, channels, and custom properties per organization |
| **Preferences** | Powers the [Links](/docs/setup/frontend/links) preference center |
| **Events** | Lifecycle events published to NATS under `contacts.*` |
| **Schema** | `contact`, `channel`, `group` in `packages/db/src/schema/` |
## Next
One-command local bootstrap
Gateway routes and ports
Preference center UI
Automations triggered by contacts
---
# setup/backend/credits.mdx
Source: https://reloop.sh/docs/setup/backend/credits
Markdown: https://reloop.sh/docs/setup/backend/credits.md
Subscription tiers, credit balances, usage tracking, and Stripe billing integration.
Run [`bun setup`](/docs/setup) once from the monorepo root. Env files come from `bun setup` / `bun env:setup`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/credits` |
| **Port** | `8023` |
| **Local URL** | `https://local.reloop.sh/api/credits` |
| **Swagger UI** | `https://local.reloop.sh/api/credits/openapi` |
| **Stack** | ElysiaJS · PostgreSQL · NATS |
## Quick start
```bash
bun be:credits:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/credits/.env` (from `bun setup` / `bun env:setup`).
```bash
PORT=8023
NODE_ENV=development
INITIAL_CREDITS=100
NATS_URL=nats://localhost:4222
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PORT` | **YES** | `8023` |
| `INITIAL_CREDITS` | **YES** | `100` |
| `NATS_URL` | **YES** | `nats://localhost:4222` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:credits:dev` | Start dev server with hot reloading |
| `bun run --filter=be-credits build` | Compile production bundle |
| `bun run --filter=be-credits start` | Run compiled production build |
| `bun run --filter=be-credits check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Credits** | Tracks balances and blocks sends when credits reach zero |
| **Stripe** | Manages subscriptions, invoices, and billing webhooks |
| **Events** | Publishes `billing.quota-warning` and related NATS subjects |
| **Schema** | `plan`, `subscription`, `credit_ledger` in `packages/db/src/schema/billing.ts` |
## Next
One-command local bootstrap
Gateway routes and ports
Sends that consume credits
Platform billing notifications
---
# setup/backend/domain.mdx
Source: https://reloop.sh/docs/setup/backend/domain
Markdown: https://reloop.sh/docs/setup/backend/domain.md
Custom sending domains, DKIM keys, DNS verification, and tracking subdomain setup.
Run [`bun setup`](/docs/setup) once from the monorepo root. Env files come from `bun setup` / `bun env:setup`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/domain` |
| **Port** | `8011` |
| **Local URL** | `https://local.reloop.sh/api/domain` |
| **Swagger UI** | `https://local.reloop.sh/api/domain/openapi` |
| **Stack** | ElysiaJS · PostgreSQL · Redis · NATS |
## Quick start
```bash
bun be:domain:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/domain/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
PORT=8011
BASE_URL="https://local.reloop.sh"
HOST_DOMAIN=reloop.sh
DKIM_SELECTOR=reloop
NATS_URL=nats://localhost:4222
NODE_ENV=development
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://...` |
| `REDIS_URL` | **YES** | `redis://...` |
| `PORT` | **YES** | `8011` |
| `BASE_URL` | **YES** | `https://local.reloop.sh` |
| `HOST_DOMAIN` | **YES** | `reloop.sh` |
| `DKIM_SELECTOR` | **YES** | `reloop` |
| `NATS_URL` | **YES** | `nats://localhost:4222` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:domain:dev` | Start dev server with hot reloading |
| `bun run --filter=be-domain build` | Compile production bundle |
| `bun run --filter=be-domain start` | Run compiled production build |
| `bun run --filter=be-domain check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **DNS verification** | Validates DKIM, SPF, DMARC, MX, and CNAME records per domain capability |
| **Inbound validation** | Used by [inbound](/docs/setup/backend/inbound) to verify recipient addresses |
| **Schema** | `domain` and `domain_dns_record` in `packages/db/src/schema/domain.ts` |
| **Events** | Publishes domain lifecycle events to NATS |
## Next
One-command local bootstrap
Gateway routes and ports
MX receiver that checks recipients
Outbound send API
---
# setup/backend/email.mdx
Source: https://reloop.sh/docs/setup/backend/email
Markdown: https://reloop.sh/docs/setup/backend/email.md
Platform transactional emails triggered by NATS events (invites, OTPs, billing alerts).
Run [`bun setup`](/docs/setup) once from the monorepo root. Env files come from `bun setup` / `bun env:setup`.
**Email** ≠ **Mail**. Email sends platform notifications to Reloop users (invites, password resets). [Mail](/docs/setup/backend/mail) is the customer-facing send API for their audiences.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/email` |
| **Port** | `8022` |
| **Local URL** | `https://local.reloop.sh/api/email` |
| **Swagger UI** | `https://local.reloop.sh/api/email/openapi` |
| **Stack** | ElysiaJS · Redis · NATS |
## Quick start
```bash
bun be:email:dev
```
Or `bun backend:dev` / `bun dev`. In development, when neither `RELOOP_API_KEY` nor `SMTP_HOST` is set, mail falls back to Mailpit SMTP (`localhost:1025`). That fallback is development-only: see [How system email is delivered](#how-system-email-is-delivered).
## Environment
`apps/backend/email/.env` (from `bun setup` / `bun env:setup`).
```bash
EMAIL_PORT=8022
PORT=8022
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
RELOOP_API_KEY=
RELOOP_SENDER_DOMAIN=
ONBOARDING_TEST_DOMAIN=
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
SMTP_SECURE=false
NODE_ENV=development
```
| Variable | Required | Default | Notes |
| :--- | :--- | :--- | :--- |
| `EMAIL_PORT` / `PORT` | **YES** | `8022` | Listening port |
| `BASE_URL` | **YES** | `https://local.reloop.sh` | Platform base URL |
| `NATS_URL` | **YES** | `nats://localhost:4222` | Event subscriptions |
| `RELOOP_API_KEY` | One of | - | API key for the org that owns the sender domain(s); used by `reloop-email` |
| `RELOOP_SENDER_DOMAIN` | With API key | - | System product mail From domain (auth, billing, invites) |
| `ONBOARDING_TEST_DOMAIN` | Prod | - | Onboarding “Send email” From domain only (can differ) |
| `SMTP_HOST` | One of | - | Relay system mail through your own SMTP server instead of Reloop |
| `SMTP_PORT` | No | `587` | Use `465` with `SMTP_SECURE=true` |
| `SMTP_USER` | No | - | Omit for an unauthenticated relay |
| `SMTP_PASSWORD` | No | - | Paired with `SMTP_USER` |
| `SMTP_SECURE` | No | `false` | `true` for implicit TLS |
| `MAILPIT_HOST` | No | `localhost` | Development capture host |
| `MAILPIT_PORT` | No | `1025` | Development capture port |
| `APP_NAME` | No | `Reloop` | Company name shown as `Self-hosted Reloop × ` in system email senders, subjects and bodies |
## How system email is delivered
System email is anything Reloop sends to **your own users**: sign-in codes,
invitations, billing alerts. It is separate from customer mail, which always
goes through [mail](/docs/setup/backend/mail).
The service picks a transport at startup, in this order:
| Condition | Transport |
| :--- | :--- |
| `RELOOP_API_KEY` is set | Reloop API, sending from `RELOOP_SENDER_DOMAIN` |
| otherwise `SMTP_HOST` is set | That SMTP server directly |
| otherwise, `NODE_ENV` is not `production` | Mailpit on `MAILPIT_HOST:MAILPIT_PORT` |
| otherwise | **Nothing. The send fails and logs why.** |
The chosen transport is logged once on boot, so `reloop logs email` tells you
which one is live:
```
System email transport: SMTP smtp.acme.test:587 (authenticated)
```
In production, Mailpit is never used. Earlier versions fell back to it
silently, so an install without `RELOOP_API_KEY` accepted sign-ins and then
dropped every one-time code into a Mailpit that was not running. The service
now refuses to send and logs the reason instead.
### Choosing a transport
**Through Reloop.** Best once you have a verified sending domain. Create an API
key in the organization that owns it, then set `RELOOP_API_KEY` and
`RELOOP_SENDER_DOMAIN`. Your platform mail then gets the same delivery
pipeline, tracking and logs as customer mail.
**Through your own SMTP server.** Set `SMTP_HOST`, plus `SMTP_USER` and
`SMTP_PASSWORD` if it requires auth. Useful on day one, before any domain is
verified, and for shops that already relay everything through Postmark, SES or
an internal server. Leave `RELOOP_API_KEY` empty.
```bash
SMTP_HOST=smtp.postmarkapp.com
SMTP_PORT=587
SMTP_USER=your-token
SMTP_PASSWORD=your-token
```
### Signing in before email works
A fresh install has no sending domain, so it cannot email you a code to log in
and verify one. Self-host breaks that loop at `/dashboard/setup`: the installer
prints a one-time setup key, and the wizard creates the first administrator
with a password, so no email is needed. See
[VPS install](/docs/self-host/vps).
`DEFAULT_OTP` is a development helper and is left empty on new installations.
Installations upgraded from an older installer keep the fixed code they were
given; remove it and restart once a transport above works.
## Branding system email
`APP_NAME` sets the name used across every system email: the sender name, the
subject line and the message body. Any value other than `Reloop` is shown as
`Self-hosted Reloop × `, so mail carries both your name and the fact
that it runs on Reloop.
```bash
APP_NAME=Contoso Mail
```
| Email | Sender with `APP_NAME=Contoso Mail` |
| :--- | :--- |
| Login code | `Self-hosted Reloop × Contoso Mail ` |
| New sign-in | `Self-hosted Reloop × Contoso Mail Security ` |
| Billing | `Self-hosted Reloop × Contoso Mail Billing ` |
| Quota warning | `Self-hosted Reloop × Contoso Mail Support ` |
| Invitation | `Dana via Self-hosted Reloop × Contoso Mail ` |
Leave it unset, or set it to `Reloop`, and everything stays plain `Reloop`, so
existing deployments are unaffected. The installer defaults it to `Reloop` and preserves your value
across re-runs.
The sign-in alert previously sent as `Security Alert`, which was the only
sender that did not carry the product name. It is now ` Security`,
matching the Billing and Support senders.
> [!NOTE]
> The email logo is loaded from `BASE_URL`, not from Reloop, so replace that
> asset in your own deployment if you rebrand.
> [!NOTE]
> The Reloop attribution cannot be configured away. The shared footer also
> keeps Reloop's help link, copyright line and postal address regardless of
> `APP_NAME`.
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:email:dev` | Start dev server with hot reloading |
| `bun run --filter=be-email build` | Compile production bundle |
| `bun run --filter=be-email start` | Run compiled production build |
| `bun run --filter=be-email check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Triggers** | Subscribes to NATS subjects (`auth.*`, `billing.*`, etc.) |
| **Delivery** | Sends via the [mail](/docs/setup/backend/mail) pipeline using `RELOOP_API_KEY` when set |
| **Schema** | `email_log` in `packages/db/src/schema/email.ts` |
## Next
One-command local bootstrap
Customer send API (different service)
Triggers invites and OTP emails
Gateway routes and ports
---
# setup/backend/inbound.mdx
Source: https://reloop.sh/docs/setup/backend/inbound
Markdown: https://reloop.sh/docs/setup/backend/inbound.md
Inbound KumoMTA MX receiver. It accepts external mail, scans with [spam](/docs/setup/backend/spam) (Rspamd), and forwards to [inbox](/docs/setup/backend/inbox) via NATS.
Run [`bun setup`](/docs/setup) or `bun docker:up` from the monorepo root. Inbound needs free port `25`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/inbound` |
| **Docker service** | `inbound` (container `reloop-inbound`) |
| **SMTP port** | `25` |
| **HTTP port** | `8030` (health check) |
| **Stack** | KumoMTA · Lua · [spam](/docs/setup/backend/spam) |
## Quick start
```bash
bun docker:up
```
Health check: `http://localhost:8030/health`.
## Configuration
Key variables in `local/docker-compose.yml`:
| Variable | Default |
| :--- | :--- |
| `INBOUND_HOSTNAME` | `inbound.reloop.sh` |
| `NATS_URL` | `reloop-nats:4222` |
| `KUMOMTA_MIN_FREE_SPACE` | `1%` |
| `KUMOMTA_MIN_FREE_INODES` | `0` |
| `KUMOMTA_RSPAMD_URL` | `http://reloop-spam:11333/checkv2` |
| `KUMOMTA_CHECK_RECIPIENT_URL` | `http://host.docker.internal:8011/api/domain` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun docker:up` | Start full Docker stack (includes inbound) |
| `docker compose -f local/docker-compose.yml up -d inbound` | Start inbound only |
| `docker logs -f reloop-inbound` | Stream logs |
| `curl http://localhost:8030/health` | Health check |
## Architecture
```
External Sender → Port 25 → Recipient Check → Spam (Rspamd) → NATS → Inbox
↘ discard (no SMTP egress)
```
| Layer | Detail |
| :--- | :--- |
| **MX reception** | Accepts inbound mail on port `25` |
| **Validation** | [domain](/docs/setup/backend/domain) service verifies recipients |
| **Spam scan** | [spam](/docs/setup/backend/spam) backend scores mail via `/checkv2` |
| **Forwarding** | Accepted messages published to NATS for inbox, then assigned to the `null` queue |
| **Receive-only** | No SMTP AUTH, no HTTP inject, no outbound delivery; submission is [smtp](/docs/setup/backend/smtp) only |
## Next
One-command local bootstrap
Port `25` and edge conflicts
Persists messages from NATS
Recipient validation API
Rspamd scan backend for inbound
---
# setup/backend/inbox.mdx
Source: https://reloop.sh/docs/setup/backend/inbox
Markdown: https://reloop.sh/docs/setup/backend/inbox.md
Agent Inbox API: mailboxes, threads, and messages from inbound email via NATS.
Run [`bun setup`](/docs/setup) once from the monorepo root. That starts Docker including [inbound](/docs/setup/backend/inbound) for live MX on port `25`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/inbox` |
| **Port** | `8028` |
| **Local URL** | `https://local.reloop.sh/api/inbox` |
| **Swagger UI** | `https://local.reloop.sh/api/inbox/openapi` |
| **Stack** | ElysiaJS · PostgreSQL · Redis · NATS |
## Quick start
```bash
bun be:inbox:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/inbox/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
PORT=8028
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
NODE_ENV=development
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://...` |
| `REDIS_URL` | **YES** | `redis://...` |
| `PORT` | **YES** | `8028` |
| `NATS_URL` | **YES** | `nats://localhost:4222` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:inbox:dev` | Start dev server with hot reloading |
| `bun run --filter=be-inbox build` | Compile production bundle |
| `bun run --filter=be-inbox start` | Run compiled production build |
| `bun run --filter=be-inbox check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Inbound flow** | [inbound](/docs/setup/backend/inbound) MTA → NATS → inbox persistence |
| **Attachments** | File storage via [upload](/docs/setup/backend/upload) (no direct S3 config on inbox) |
| **Dashboard API** | Mailboxes, threads, and messages at `/api/inbox` |
## Next
One-command local bootstrap
MX edge that feeds this service
Attachment storage
Agent Inbox UI
---
# setup/backend/logs.mdx
Source: https://reloop.sh/docs/setup/backend/logs
Markdown: https://reloop.sh/docs/setup/backend/logs.md
Event logging and analytics: activity logs and email delivery data in PostgreSQL.
Run [`bun setup`](/docs/setup) once so Postgres is up (`bun docker:up`). Env files come from `bun setup` / `bun env:setup`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/logs` |
| **Port** | `8016` |
| **Local URL** | `https://local.reloop.sh/api/logs` |
| **Swagger UI** | `https://local.reloop.sh/api/logs/openapi` |
| **Stack** | ElysiaJS · PostgreSQL · Redis · NATS |
## Quick start
```bash
bun run --filter=be-logs dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/logs/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
PORT=8016
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
NODE_ENV=development
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://reloop:reloop123@localhost:5432/reloop` |
| `REDIS_URL` | **YES** | `redis://...` |
| `PORT` | **YES** | `8016` |
| `NATS_URL` | **YES** | `nats://localhost:4222` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun run --filter=be-logs dev` | Start dev server with hot reloading |
| `bun run --filter=be-logs build` | Compile production bundle |
| `bun run --filter=be-logs start` | Run compiled production build |
| `bun run --filter=be-logs check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Ingestion** | Subscribes to NATS delivery and activity events |
| **Storage** | Writes activity logs to Postgres `activity_log`; email delivery to `email_log` |
| **Query API** | Powers dashboard activity logs and email delivery analytics |
| **Schema** | Defined in `packages/db/src/schema/activity-log.ts` |
## Next
One-command local bootstrap
Postgres and gateway ports
Source of delivery events
Parallel event delivery path
---
# setup/backend/mail.mdx
Source: https://reloop.sh/docs/setup/backend/mail
Markdown: https://reloop.sh/docs/setup/backend/mail.md
Outbound transactional email API: send requests, template compilation, tracking, and SMTP handoff.
Run [`bun setup`](/docs/setup) once from the monorepo root. That starts Docker including [SMTP](/docs/setup/backend/smtp). Mailpit captures local outbound mail at `http://localhost:8025`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/mail` |
| **Port** | `8015` |
| **Local URL** | `https://local.reloop.sh/api/mail` |
| **Swagger UI** | `https://local.reloop.sh/api/mail/openapi` |
| **Stack** | ElysiaJS · PostgreSQL · Redis · NATS · BullMQ |
## Quick start
```bash
bun be:mail:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/mail/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
PORT=8015
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
KUMOMTA_HTTP_URL=http://localhost:8020
TRACKING_SECRET=reloop_tracking_secret_default_123
NODE_ENV=development
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://...` |
| `REDIS_URL` | **YES** | `redis://...` |
| `PORT` | **YES** | `8015` |
| `KUMOMTA_HTTP_URL` | **YES** | `http://localhost:8020` |
| `TRACKING_SECRET` | No | Signs tracked link and open-pixel tokens |
| `NATS_URL` | **YES** | `nats://localhost:4222` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:mail:dev` | Start dev server with hot reloading |
| `bun run --filter=be-mail build` | Compile production bundle |
| `bun run --filter=be-mail start` | Run compiled production build |
| `bun run --filter=be-mail check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Send pipeline** | Validates API keys, compiles templates, injects tracking links |
| **Queues** | BullMQ workers dispatch via Redis (`mail-queue`, `mail-retry-queue`) |
| **MTA handoff** | Delivers to the [SMTP](/docs/setup/backend/smtp) engine on port `8020` |
| **Schema** | `email_log` in `packages/db/src/schema/email.ts` |
## Next
One-command local bootstrap
Outbound KumoMTA edge
HTML compilation source
Click and open tracking
---
# setup/backend/smtp.mdx
Source: https://reloop.sh/docs/setup/backend/smtp
Markdown: https://reloop.sh/docs/setup/backend/smtp.md
Outbound KumoMTA engine that accepts mail from [mail](/docs/setup/backend/mail) and delivers via SMTP (Mailpit locally).
Run [`bun setup`](/docs/setup) or `bun docker:up` from the monorepo root. SMTP needs free ports `465` / `587`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/smtp` |
| **Docker service** | `smtp` (container `reloop-smtp`) |
| **SMTP ports** | `465`, `587`, `2025`, `2465`, `2587` |
| **HTTP port** | `8020` |
| **Stack** | KumoMTA · Lua |
## Quick start
```bash
bun docker:up
```
View captured mail at `http://localhost:8025` (Mailpit).
## Configuration
Key variables in `local/docker-compose.yml`:
| Variable | Default |
| :--- | :--- |
| `BASE_URL` | `https://local.reloop.sh` |
| `NATS_URL` | `reloop-nats:4222` |
| `SMTP_HOSTNAME` | `smtp.reloop.sh` |
### SMTP_HOSTNAME
The hostname senders connect to for submission. It is the relay's own EHLO
name, and the dashboard reads it to fill the **SMTP → Credentials** panel and
every code example on that page, so self-hosted users are shown their own
endpoint instead of Reloop's.
The installer defaults it to your Reloop domain and preserves it across
re-runs. Set it explicitly when submission runs on a different host:
```bash
SMTP_HOSTNAME=mail.example.com
```
It is read at request time, so both services pick up a change on restart:
```bash
reloop restart smtp dashboard
```
Leave it unset and the dashboard falls back to `smtp.reloop.sh`, which is
correct for Reloop Cloud and wrong for every self-hosted install, so set it.
## Commands
| Command | Description |
| :--- | :--- |
| `bun docker:up` | Start full Docker stack (includes SMTP) |
| `docker compose -f local/docker-compose.yml up -d smtp` | Start SMTP only |
| `docker logs -f reloop-smtp` | Stream logs |
| `docker compose -f local/docker-compose.yml stop smtp` | Stop container |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Submission** | Accepts outbound mail on ports `465` / `587` / `2025` / `2465` / `2587` |
| **Policies** | Lua scripts in `apps/backend/smtp/policy/` handle routing and DKIM |
| **Local delivery** | Captured by Mailpit at `http://localhost:8025` |
| **Inbound** | Port `25` MX is handled by [inbound](/docs/setup/backend/inbound), not this service |
## Next
One-command local bootstrap
Edge ports and conflicts
HTTP send API that hands off here
MX receiver on port `25`
---
# setup/backend/spam.mdx
Source: https://reloop.sh/docs/setup/backend/spam
Markdown: https://reloop.sh/docs/setup/backend/spam.md
Rspamd spam scanner that scores inbound mail for the [inbound](/docs/setup/backend/inbound) MX receiver via HTTP `/checkv2`.
Run [`bun setup`](/docs/setup) or `bun docker:up` from the monorepo root. Spam starts with the Compose stack because inbound depends on it.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/spam` |
| **Docker service** | `reloop-spam` (container `reloop-spam`) |
| **Scan ports** | `11332`, `11333` |
| **Web UI** | `11334` |
| **Stack** | Rspamd · Redis |
## Quick start
```bash
bun docker:up
```
Or rebuild after config changes:
```bash
docker compose -f local/docker-compose.yml up -d --build reloop-spam
```
Controller / scan endpoint: `http://localhost:11333`.
## Configuration
Local overrides live in `apps/backend/spam/local.d/` (mounted into the container).
Spam Redis (set on the **spam** container; native Rspamd `RSPAMD_*` env):
| Variable | Default |
| :--- | :--- |
| `RSPAMD_REDIS_SERVERS` | `reloop-redis:6379` |
| `RSPAMD_REDIS_PASSWORD` | `reloop123` |
Inbound wires to this service with:
| Variable | Default |
| :--- | :--- |
| `KUMOMTA_RSPAMD_URL` | `http://reloop-spam:11333/checkv2` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun docker:up` | Start core stack including spam |
| `docker compose -f local/docker-compose.yml up -d --build reloop-spam` | Rebuild and start spam only |
| `docker logs -f reloop-spam` | Stream logs |
| `docker compose -f local/docker-compose.yml stop reloop-spam` | Stop container |
## Architecture
```
Inbound (KumoMTA) → HTTP POST /checkv2 → Spam (Rspamd) → score / action / symbols
```
| Layer | Detail |
| :--- | :--- |
| **Scan API** | Accepts raw message bodies on `/checkv2` (port `11333`) |
| **Headers** | Inbound injects `X-Spam-*` from the response for [inbox](/docs/setup/backend/inbox) |
| **State** | Uses [Redis](/docs/setup) (`reloop-redis`) for learning / fuzzy storage |
| **Separate deploy** | Not baked into the inbound image; own Dockerfile and CI (`be-spam`) |
## Next
One-command local bootstrap
Scan ports and conflicts
MX receiver that calls this service
Persists `X-Spam-*` scores from NATS
---
# setup/backend/template.mdx
Source: https://reloop.sh/docs/setup/backend/template
Markdown: https://reloop.sh/docs/setup/backend/template.md
Email template CRUD, visual builder storage, and HTML compilation for outbound sends.
Run [`bun setup`](/docs/setup) once from the monorepo root. Env files come from `bun setup` / `bun env:setup`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/template` |
| **Port** | `8019` |
| **Local URL** | `https://local.reloop.sh/api/template` |
| **Swagger UI** | `https://local.reloop.sh/api/template/openapi` |
| **Stack** | ElysiaJS · PostgreSQL · Redis · NATS |
## Quick start
```bash
bun be:template:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/template/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
PORT=8019
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
NODE_ENV=development
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://...` |
| `REDIS_URL` | **YES** | `redis://...` |
| `PORT` | **YES** | `8019` |
| `BASE_URL` | **YES** | `https://local.reloop.sh` |
| `NATS_URL` | **YES** | `nats://localhost:4222` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:template:dev` | Start dev server with hot reloading |
| `bun run --filter=be-template build` | Compile production bundle |
| `bun run --filter=be-template start` | Run compiled production build |
| `bun run --filter=be-template check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Builder** | Stores drag-and-drop block JSON from the dashboard editor |
| **Compilation** | Renders HTML with merge tags for the [mail](/docs/setup/backend/mail) service |
| **Schema** | `template` and revisions in `packages/db/src/schema/template.ts` |
## Next
One-command local bootstrap
Gateway routes and ports
Consumes compiled HTML
Template editor UI
---
# setup/backend/upload.mdx
Source: https://reloop.sh/docs/setup/backend/upload
Markdown: https://reloop.sh/docs/setup/backend/upload.md
File uploads: logos, attachments, and assets stored in MinIO/S3.
Run [`bun setup`](/docs/setup) once so MinIO is up (`bun docker:up`). Env files come from `bun setup` / `bun env:setup`. Console: `http://localhost:9001` (`reloop` / `reloop123`).
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/upload` |
| **Port** | `8018` |
| **Local URL** | `https://local.reloop.sh/api/upload` |
| **Swagger UI** | `https://local.reloop.sh/api/upload/openapi` |
| **Stack** | ElysiaJS · PostgreSQL · MinIO/S3 · NATS |
## Quick start
```bash
bun be:upload:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/upload/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
PORT=8018
BASE_URL="https://local.reloop.sh"
S3_ENDPOINT=http://localhost:9010
S3_ACCESS_KEY=reloop
S3_SECRET_KEY=reloop123
S3_BUCKET=reloop-uploads
S3_REGION=us-east-1
S3_FORCE_PATH_STYLE=true
NATS_URL=nats://localhost:4222
NODE_ENV=development
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://...` |
| `S3_ENDPOINT` | **YES** | `http://localhost:9010` |
| `S3_ACCESS_KEY` | **YES** | `reloop` |
| `S3_SECRET_KEY` | **YES** | `reloop123` |
| `S3_BUCKET` | **YES** | `reloop-uploads` |
| `PORT` | **YES** | `8018` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:upload:dev` | Start dev server with hot reloading |
| `bun run --filter=be-upload build` | Compile production bundle |
| `bun run --filter=be-upload start` | Run compiled production build |
| `bun run --filter=be-upload check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Storage** | Files uploaded to MinIO locally (`localhost:9010`), S3 in production |
| **Metadata** | File index stored in Postgres (`upload` table) |
| **Signed URLs** | Generates presigned upload/download URLs for the dashboard |
## Next
One-command local bootstrap
MinIO ports `9010` / `9001`
Attachments via upload
Assets used in templates
---
# setup/backend/webhook.mdx
Source: https://reloop.sh/docs/setup/backend/webhook
Markdown: https://reloop.sh/docs/setup/backend/webhook.md
Outbound event webhooks: subscription management, HMAC signing, delivery, and retries via BullMQ.
Run [`bun setup`](/docs/setup) once from the monorepo root. Env files come from `bun setup` / `bun env:setup`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/webhook` |
| **Port** | `8013` |
| **Local URL** | `https://local.reloop.sh/api/webhook` |
| **Swagger UI** | `https://local.reloop.sh/api/webhook/openapi` |
| **Stack** | ElysiaJS · PostgreSQL · Redis · NATS · BullMQ |
## Quick start
```bash
bun be:webhook:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/webhook/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
WEBHOOK_ENCRYPTION_KEY=your-64-char-hex-key
PORT=8013
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
NODE_ENV=development
```
`WEBHOOK_ENCRYPTION_KEY` encrypts stored webhook signing secrets. If unset, secrets are stored in plaintext (local only). The workflow service decrypts them to sign deliveries, so it needs the same value.
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://...` |
| `REDIS_URL` | **YES** | `redis://...` |
| `WEBHOOK_ENCRYPTION_KEY` | Prod only | Dev key in `.env.dev` |
| `PORT` | **YES** | `8013` |
| `BASE_URL` | **YES** | `https://local.reloop.sh` |
| `NATS_URL` | **YES** | `nats://localhost:4222` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:webhook:dev` | Start dev server with hot reloading |
| `bun run --filter=be-webhook build` | Compile production bundle |
| `bun run --filter=be-webhook start` | Run compiled production build |
| `bun run --filter=be-webhook check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Control plane** | This service: CRUD endpoints, list deliveries, manual replay |
| **Dispatcher** | `be-workflow` NATS queue group `webhook-dispatcher` (email lifecycle + manual trigger) |
| **Delivery** | Dedicated BullMQ queue `webhook-delivery-queue`; signs with `Reloop-Signature` |
| **Retries** | Fixed 7-attempt schedule (5s → 5m → 30m → 2h → 5h → 10h) |
| **Helpers** | `@reloop/webhook-delivery` (envelope, HMAC, SSRF client) |
| **Schema** | `packages/db/src/schema/webhook.ts` |
## Next
One-command local bootstrap
Gateway routes and ports
Source of delivery events
Analytics event sink
---
# setup/backend/workflow.mdx
Source: https://reloop.sh/docs/setup/backend/workflow
Markdown: https://reloop.sh/docs/setup/backend/workflow.md
Marketing automation: drip campaigns, delays, and multi-step journeys via BullMQ.
Run [`bun setup`](/docs/setup) once from the monorepo root. Env files come from `bun setup` / `bun env:setup`.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/backend/workflow` |
| **Port** | `8017` |
| **Local URL** | `https://local.reloop.sh/api/workflow` |
| **Swagger UI** | `https://local.reloop.sh/api/workflow/openapi` |
| **Workbench** | `https://local.reloop.sh/api/workflow/jobs` |
| **Stack** | ElysiaJS · PostgreSQL · Redis · NATS · BullMQ |
## Quick start
```bash
bun be:workflow:dev
```
Or `bun backend:dev` / `bun dev`.
## Environment
`apps/backend/workflow/.env` (from `bun setup` / `bun env:setup`).
```bash
PG_URL=postgresql://reloop:reloop123@localhost:5432/reloop
REDIS_URL=redis://:reloop123@localhost:6379
PORT=8017
BASE_URL="https://local.reloop.sh"
NATS_URL=nats://localhost:4222
NODE_ENV=development
WEBHOOK_ENCRYPTION_KEY=your-64-char-hex-key
# Optional basic-auth for Workbench (/api/workflow/jobs)
# WORKBENCH_USER=admin
# WORKBENCH_PASS=secret
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `PG_URL` | **YES** | `postgresql://...` |
| `REDIS_URL` | **YES** | `redis://...` |
| `PORT` | **YES** | `8017` |
| `BASE_URL` | **YES** | `https://local.reloop.sh` |
| `NATS_URL` | **YES** | `nats://localhost:4222` |
| `WEBHOOK_ENCRYPTION_KEY` | Same value as the webhook service | Dev key in `.env.dev` |
| `WORKBENCH_USER` | Outside development | empty (dashboard disabled) |
| `WORKBENCH_PASS` | Outside development | empty (dashboard disabled) |
Outside development the dashboard at `/api/workflow/jobs` only mounts when both values are set, and then sits behind HTTP basic auth. The self-host installer generates them.
This service signs outbound webhook deliveries, so it decrypts the secrets the webhook service stored. Both services must use the same `WEBHOOK_ENCRYPTION_KEY`, or every signature will be computed from the ciphertext and fail verification.
With `NODE_ENV=development`, deliveries may target `http://` URLs and private or loopback addresses, so a receiver on `localhost` works. In production only public HTTPS endpoints are allowed.
## Commands
| Command | Description |
| :--- | :--- |
| `bun be:workflow:dev` | Start dev server with hot reloading |
| `bun run --filter=be-workflow build` | Compile production bundle |
| `bun run --filter=be-workflow start` | Run compiled production build |
| `bun run --filter=be-workflow check-types` | TypeScript type-check |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **Triggers** | NATS events (e.g. `contacts.created`) start automation journeys |
| **Execution** | BullMQ `workflow-queue` handles delayed steps and transitions |
| **Integrations** | Reads contacts/templates and triggers sends via [mail](/docs/setup/backend/mail) |
| **Monitoring** | [Workbench](https://getworkbench.dev) UI at `/api/workflow/jobs` |
## Next
One-command local bootstrap
Gateway routes and ports
Trigger source for journeys
Sends from workflow steps
---
# setup/frontend/dashboard.mdx
Source: https://reloop.sh/docs/setup/frontend/dashboard
Markdown: https://reloop.sh/docs/setup/frontend/dashboard.md
Main workspace app: campaigns, contacts, templates, analytics, and settings.
Run [`bun setup`](/docs/setup) once from the monorepo root before starting individual apps. The dashboard needs [auth](/docs/setup/backend/auth) (and usually the rest of `bun backend:dev` or `bun dev`).
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/frontend/dashboard` |
| **Port** | `3001` |
| **Local URL** | `https://local.reloop.sh/dashboard` |
| **Direct URL** | `http://localhost:3001/dashboard` |
| **Stack** | TanStack Start · Vite · React 19 · Tailwind CSS |
## Quick start
From the monorepo root (after [`bun setup`](/docs/setup)):
```bash
bun fe:dashboard:dev
```
Or start every frontend together:
```bash
bun frontend:dev
```
Vite HMR serves the app on port `3001` with base path `/dashboard`.
Open `https://local.reloop.sh/dashboard`, not `localhost:3001`, so session cookies work with [auth](/docs/setup/backend/auth). Local OTP is `888888`.
## Environment
No committed `.env` is required for local same-origin use via Caddy. The auth client falls back to the browser origin when public URL vars are unset.
Optional overrides:
```bash
# Optional locally; browser origin is used when unset (via Caddy at local.reloop.sh).
# Required at Docker/image *build* time for production if you cannot rely on same-origin.
# NEXT_PUBLIC_URL=https://local.reloop.sh
# VITE_PUBLIC_URL=https://local.reloop.sh
NEXT_PUBLIC_WS_URL="wss://local.reloop.sh"
```
API calls use relative paths (`/api/*`) through Caddy, so no public API URL is required.
Auth (`@reloop/auth/client`) resolves the public origin as: `NEXT_PUBLIC_URL` / `VITE_PUBLIC_URL` → `window.location.origin`. For production Docker images you can pass `--build-arg NEXT_PUBLIC_URL=https://your-domain.com`; if omitted, the browser origin is used so same-host reverse-proxy deploys work without a rebuild.
| Variable | Required | Default |
| :--- | :--- | :--- |
| `NEXT_PUBLIC_URL` / `VITE_PUBLIC_URL` | No | Browser origin (`window.location.origin`) |
| `NEXT_PUBLIC_WS_URL` | No | Current host (template collaboration WebSocket) |
## Commands
| Command | Description |
| :--- | :--- |
| `bun fe:dashboard:dev` | Start Vite + TanStack Start dev server |
| `bun run --filter=fe-dashboard build` | Compile production bundle (Nitro) |
| `bun run --filter=fe-dashboard preview` | Preview production build |
| `bun run --filter=fe-dashboard typecheck` | Run TypeScript checks |
| `bun run --filter=fe-dashboard test` | Run Vitest |
## Next
One-command local bootstrap
Gateway routes and ports
Sessions and local OTP
Marketing site setup
---
# setup/frontend/docs.mdx
Source: https://reloop.sh/docs/setup/frontend/docs
Markdown: https://reloop.sh/docs/setup/frontend/docs.md
This documentation site: MDX guides, API reference, and local setup docs.
Run [`bun setup`](/docs/setup) once from the monorepo root before starting individual apps.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/frontend/docs` |
| **Port** | `3003` |
| **Local URL** | `https://local.reloop.sh/docs` |
| **Direct URL** | `http://localhost:3003` |
| **Stack** | Next.js · MDX · Tailwind CSS |
## Quick start
From the monorepo root (after [`bun setup`](/docs/setup)):
```bash
bun fe:docs:dev
```
Or start every frontend together:
```bash
bun frontend:dev
```
Turbopack serves the app on port `3003`. Prefer `https://local.reloop.sh/docs` when Caddy is running.
## Environment
Committed local defaults live in `apps/frontend/docs/.env` (and `.env.dev`). Fresh clones already have them; `bun env:setup` / `bun setup` recreate or merge missing keys.
```bash
NEXT_PUBLIC_URL=https://local.reloop.sh
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `NEXT_PUBLIC_URL` | No | `https://reloop.sh` (canonical URLs, OG metadata, and public origin) |
## Content structure
| Path | Purpose |
| :--- | :--- |
| `content/docs/setup/` | Local development setup guides (this section) |
| `content/docs/api/` | Auto-generated API reference |
| `content/docs/**/meta.json` | Sidebar navigation and ordering |
## Commands
| Command | Description |
| :--- | :--- |
| `bun fe:docs:dev` | Start dev server with Turbopack |
| `bun run --filter=fe-docs build` | Build and validate all MDX pages |
| `bun run --filter=fe-docs start` | Run production build |
| `bun run --filter=fe-docs generate:api-docs` | Regenerate API docs from OpenAPI specs |
## Next
One-command local bootstrap
Gateway routes and ports
Monorepo layout and stack
Marketing site setup
---
# setup/frontend/links.mdx
Source: https://reloop.sh/docs/setup/frontend/links
Markdown: https://reloop.sh/docs/setup/frontend/links.md
Contact preference center, tracked link redirects, and open-tracking pixel proxy for outbound email.
Run [`bun setup`](/docs/setup) once from the monorepo root before starting individual apps. Preferences need [contacts](/docs/setup/backend/contacts) and [mail](/docs/setup/backend/mail) running (`bun dev` or `bun backend:dev`).
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/frontend/links` |
| **Port** | `3005` |
| **Preferences** | `https://local.reloop.sh/preferences` |
| **Redirect** | `https://local.reloop.sh/redirect` |
| **Open tracking** | `https://local.reloop.sh/api/mail/v1/track/open` |
| **Stack** | Next.js · React 19 · Tailwind CSS |
## Quick start
From the monorepo root (after [`bun setup`](/docs/setup)):
```bash
bun fe:links:dev
```
Or start every frontend together:
```bash
bun frontend:dev
```
Turbopack serves the app on port `3005`. Prefer the `local.reloop.sh` URLs above when Caddy is running.
Use `https://local.reloop.sh/...`, not `localhost:3005`, so redirects and cookies match production-style hosts.
## Environment
Committed local defaults live in `apps/frontend/links/.env` (and `.env.dev`). Fresh clones already have them; `bun env:setup` / `bun setup` recreate or merge missing keys.
```bash
NEXT_PUBLIC_URL=https://local.reloop.sh
# Must match TRACKING_SECRET in the mail backend and REDIRECT_SECRET in auth/email backends
REDIRECT_SECRET=reloop_tracking_secret_default_123
# Server-side contacts API (preferences page)
INTERNAL_API_URL=http://localhost:8014/api/contacts
# Server-side mail API (open-tracking pixel proxy)
INTERNAL_MAIL_API_URL=http://localhost:8015/api/mail
```
Preferences call [contacts](/docs/setup/backend/contacts) via `INTERNAL_API_URL`. Open-tracking proxies to [mail](/docs/setup/backend/mail) via `INTERNAL_MAIL_API_URL` (or `NEXT_PUBLIC_URL` as a fallback).
| Variable | Required | Default |
| :--- | :--- | :--- |
| `NEXT_PUBLIC_URL` | No | `https://link.reloop.sh` (base URL for redirect and preference links) |
| `REDIRECT_SECRET` | No | Shared secret with mail / auth / email backends |
| `INTERNAL_API_URL` | No | `http://localhost:8014/api/contacts` |
| `INTERNAL_MAIL_API_URL` | No | Falls back to `{NEXT_PUBLIC_URL}/api/mail` |
## Routes
| Route | Purpose |
| :--- | :--- |
| `/preferences/[token]` | Manage subscription topics (public channels) |
| `/preferences/unsubscribe/[token]` | Main-list unsubscribe from campaign emails |
| `/redirect/[token]` | Track clicks and redirect to destination URL |
| `/api/mail/v1/track/open/[token]` | Proxy open-tracking pixels to the mail service |
## Architecture
| Layer | Detail |
| :--- | :--- |
| **[Mail](/docs/setup/backend/mail)** | Injects `/redirect/[token]` click links and open pixels in outbound HTML |
| **[Contacts](/docs/setup/backend/contacts)** | Powers preference page data |
| **Open-tracking proxy** | Custom tracking domains CNAME here; pixels proxy to mail when `/api/mail` is not on the tracking host |
| **Caddy** | Routes `/preferences*` and `/redirect*` to port `3005` |
## Commands
| Command | Description |
| :--- | :--- |
| `bun fe:links:dev` | Start dev server with Turbopack |
| `bun run --filter=fe-links build` | Compile production bundle |
| `bun run --filter=fe-links start` | Run production build |
| `bun run --filter=fe-links lint` | Run ESLint checks |
## Next
One-command local bootstrap
Gateway routes and ports
Preferences data API
Tracking links and open pixels
---
# setup/frontend/web.mdx
Source: https://reloop.sh/docs/setup/frontend/web
Markdown: https://reloop.sh/docs/setup/frontend/web.md
Public marketing site: landing pages, pricing, and product content.
Run [`bun setup`](/docs/setup) once from the monorepo root before starting individual apps.
## Overview
| Property | Value |
| :--- | :--- |
| **Directory** | `apps/frontend/web` |
| **Port** | `3000` |
| **Local URL** | `https://local.reloop.sh` |
| **Direct URL** | `http://localhost:3000` |
| **Stack** | Next.js · React 19 · Tailwind CSS |
## Quick start
From the monorepo root (after [`bun setup`](/docs/setup)):
```bash
bun fe:web:dev
```
Or start every frontend together:
```bash
bun frontend:dev
```
Turbopack serves the app on port `3000`. Prefer `https://local.reloop.sh` when Caddy is running.
Use `https://local.reloop.sh`, not `localhost:3000`, when testing flows that share cookies or link into the dashboard.
## Environment
Committed local defaults live in `apps/frontend/web/.env` (and `.env.dev`). Fresh clones already have them; `bun env:setup` / `bun setup` recreate or merge missing keys.
```bash
NEXT_PUBLIC_URL=https://local.reloop.sh
# Must match TRACKING_SECRET in the mail backend and REDIRECT_SECRET in auth/email backends
REDIRECT_SECRET=reloop_tracking_secret_default_123
```
| Variable | Required | Default |
| :--- | :--- | :--- |
| `NEXT_PUBLIC_URL` | No | `https://reloop.sh` (canonical site URL for metadata and OG tags) |
| `REDIRECT_SECRET` | No | Shared redirect/tracking secret for local mail links |
## Commands
| Command | Description |
| :--- | :--- |
| `bun fe:web:dev` | Start dev server with Turbopack |
| `bun run --filter=fe-web build` | Compile production bundle |
| `bun run --filter=fe-web start` | Run production build |
| `bun run --filter=fe-web lint` | Run ESLint checks |
## Next
One-command local bootstrap
Gateway routes and ports
---
# setup/index.mdx
Source: https://reloop.sh/docs/setup
Markdown: https://reloop.sh/docs/setup.md
One command from the monorepo root. Full stack at `https://local.reloop.sh`.
## Prerequisites
| Tool | Notes |
| :--- | :--- |
| [Bun](https://bun.sh/get) | v1.3.0+ |
| [Docker Desktop](https://www.docker.com/products/docker-desktop/) | Postgres, Redis, Caddy, NATS, Mailpit |
| [mkcert](https://github.com/FiloSottile/mkcert) | Local TLS (`brew install mkcert` on macOS) |
| Git | Clone the repository |
## Quick start
```bash
git clone git@github.com:reloop-labs/reloop.git
cd reloop
bun setup --start
```
```bash
git clone git@github.com:reloop-labs/reloop.git
cd reloop
bun setup
bun dev
```
`bun setup` only pauses for sudo (hosts / mkcert trust) or a missing tool.
## After setup
Marketing site and entry point at `https://local.reloop.sh`
Product UI at `https://local.reloop.sh/dashboard`, not `localhost:3001`
This documentation, served locally at `https://local.reloop.sh/docs`
Captured outbound email at `http://localhost:8025`
Sign up with any email. Local OTP is always `888888`.
Open `https://local.reloop.sh` (not `localhost`) so auth cookies work across services.
## What `bun setup` does
Verifies Bun v1.3+, Docker CLI, and a running daemon. Checks mkcert unless you pass `--skip-tls`.
Adds `127.0.0.1 local.reloop.sh` to `/etc/hosts` (sudo once if needed).
Creates trusted PEMs in `local/certs/` with mkcert for Caddy HTTPS.
Runs `bun install` when `node_modules` is missing (or always with `--force`).
Copies `.env.dev` → `.env` for backends, `packages/db`, and frontends.
Brings up core infra, then waits until Postgres is healthy.
Runs `bun db:push`.
## Flags
| Flag | Effect |
| :--- | :--- |
| `--start` | Run `bun dev` when setup finishes |
| `--force` | Re-run `bun install` even if `node_modules` exists |
| `--skip-docker` | Skip Docker and `db:push` |
| `--skip-hosts` | Skip the `/etc/hosts` entry |
| `--skip-tls` | Skip cert generation (existing PEMs required) |
## Manual setup
Use this only when debugging a specific stage. Run steps in order from the monorepo root.
Skipping or reordering steps can cause database connection errors or broken routing.
```bash
git clone git@github.com:reloop-labs/reloop.git
cd reloop
bun install
```
```bash
echo "127.0.0.1 local.reloop.sh" | sudo tee -a /etc/hosts
```
Windows (PowerShell as Administrator):
```powershell
Add-Content -Path $env:windir\System32\drivers\etc\hosts -Value "127.0.0.1`tlocal.reloop.sh"
```
Caddy serves HTTPS from `local/certs/` (gitignored). Generate once:
```bash
brew install mkcert # macOS
mkcert -install
mkdir -p local/certs
mkcert -cert-file local/certs/local.reloop.sh.pem \
-key-file local/certs/local.reloop.sh-key.pem \
local.reloop.sh "*.local.reloop.sh"
```
```bash
bun docker:up
```
Free **80**, **443**, **5432**, **6379**, **25**, **465**, and **587** first. See [Port](/docs/setup/port).
Wait for Postgres, then:
```bash
bun db:push
```
```bash
bun env:setup
```
Copies `.env.dev` → `.env` for backends, `packages/db`, and `web` / `docs` / `links`. Frontends also ship committed `.env` files with local defaults.
One service manually:
```bash
cp apps/backend/auth/.env.dev apps/backend/auth/.env
```
```bash
bun dev
```
Or a subset:
```bash
bun frontend:dev # frontends only
bun backend:dev # backends only
```
## Commands
| Command | Description |
| :--- | :--- |
| `bun setup` | One-command local bootstrap |
| `bun setup --start` | Bootstrap, then `bun dev` |
| `bun dev` | All frontend and backend dev servers |
| `bun frontend:dev` | Frontends only |
| `bun backend:dev` | Backends only |
| `bun docker:up` | Start Docker infra (including SMTP/inbound) |
| `bun docker:down` | Stop containers |
| `bun env:setup` | Generate `.env` files |
| `bun db:push` | Apply Drizzle schema |
| `bun db:studio` | Open Drizzle Studio |
| `bun run check` | Lint and format |
## Next
Local ports and Caddy gateway routes
Monorepo layout and tech stack
Per-service setup guides
Web, dashboard, docs, links
---
# setup/overview.mdx
Source: https://reloop.sh/docs/setup/overview
Markdown: https://reloop.sh/docs/setup/overview.md
Reloop is a Bun monorepo with Next.js frontends, ElysiaJS microservices, shared packages, and Docker infrastructure.
Local URL: `https://local.reloop.sh`. Bootstrap with [`bun setup`](/docs/setup).
## Structure
```
reloop/
├── apps/
│ ├── backend/ # ElysiaJS APIs + KumoMTA (smtp, inbound)
│ └── frontend/ # web, dashboard, docs, links, console
├── packages/ # @reloop/ui, @reloop/db, @reloop/bus, …
└── local/ # Docker Compose, Caddy, TLS, bun setup
```
| Path | Role |
| :--- | :--- |
| `apps/frontend/*` | Product UI and docs sites |
| `apps/backend/*` | HTTP APIs and mail edge |
| `packages/*` | Shared libraries |
| `local/` | Compose stack, Caddy, [`bun setup`](/docs/setup) |
## Frontend apps
Marketing site (port `3000`)
Product UI (port `3001`)
This documentation (port `3003`)
Preferences and redirects (port `3005`)
Stack: [Next.js](https://nextjs.org/), [Tailwind CSS](https://tailwindcss.com/), [Radix UI](https://www.radix-ui.com/) via `@reloop/ui`, [SWR](https://swr.vercel.app/).
## Backend services
Sessions and orgs (`8000`)
Sending domains (`8011`)
API keys (`8012`)
Event delivery (`8013`)
Audience (`8014`)
Send API (`8015`)
Analytics (`8016`)
Automations (`8017`)
Attachments (`8018`)
Templates (`8019`)
Broadcasts (`8027`)
Inbound mail (`8028`)
Platform email (`8022`)
Usage / billing (`8023`)
Outbound edge (Docker)
MX edge (Docker)
Stack: [ElysiaJS](https://elysiajs.com/), [Better Auth](https://www.better-auth.com/), [KumoMTA](https://kumomta.com/) for smtp/inbound.
## Data & infrastructure
Started by `bun docker:up` (Postgres, Redis, Caddy, Mailpit, MinIO, NATS, spam, SMTP, inbound).
Primary DB (port `5432`)
Schema in `@reloop/db`
Sessions and cache (`6379`)
Object storage (`9010` / console `9001`)
Event bus (`4222`)
TLS proxy for `local.reloop.sh`
Captured mail UI at `http://localhost:8025`
## Tooling
Runtime and package manager
Task runner and build cache
Local infra via `local/docker-compose.yml`
Lint and format
## Shared packages
| Package | Purpose |
| :--- | :--- |
| `@reloop/ui` | Design system and components |
| `@reloop/db` | Drizzle schema and Postgres client |
| `@reloop/auth` | Shared auth client utilities |
| `@reloop/bus` | NATS messaging wrapper |
| `@reloop/cache` | Redis caching helpers |
| `@reloop/apikey` | API key hashing and verification |
| `@reloop/webhook-events` | Webhook event types |
| `@reloop/tailwind` | Shared Tailwind config |
| `@reloop/tsconfig` | Shared TypeScript config |
## Next
One-command local bootstrap with `bun setup`
Gateway routes and every local port
---
# setup/port.mdx
Source: https://reloop.sh/docs/setup/port
Markdown: https://reloop.sh/docs/setup/port.md
Use `https://local.reloop.sh` for day-to-day work. Caddy routes traffic and keeps auth cookies scoped correctly.
## Free these ports first
Stop anything already bound before `bun docker:up`.
| Required for | Ports | Service |
| :--- | :--- | :--- |
| `bun docker:up` | `80`, `443` | Caddy |
| | `5432` | PostgreSQL |
| | `6379` | Redis |
| | `9010` | MinIO (API) |
| | `25`, `465`, `587` | SMTP / inbound |
Free mail ports (`25`, `465`, `587`) if host mail or another MTA is already bound there.
## Unified Gateway
| Route | Service | Port |
| :--- | :--- | :--- |
| `https://local.reloop.sh/` | [Web](/docs/setup/frontend/web) | `3000` |
| `https://local.reloop.sh/dashboard` | [Dashboard](/docs/setup/frontend/dashboard) | `3001` |
| `https://local.reloop.sh/console` | Console | `3002` |
| `https://local.reloop.sh/docs` | [Docs](/docs/setup/frontend/docs) | `3003` |
| `https://local.reloop.sh/preferences` | [Links](/docs/setup/frontend/links) | `3005` |
| `https://local.reloop.sh/api/auth` | [Auth](/docs/setup/backend/auth) | `8000` |
| `https://local.reloop.sh/api/domain` | [Domain](/docs/setup/backend/domain) | `8011` |
| `https://local.reloop.sh/api/api-key` | [API Key](/docs/setup/backend/api-key) | `8012` |
| `https://local.reloop.sh/api/webhook` | [Webhook](/docs/setup/backend/webhook) | `8013` |
| `https://local.reloop.sh/api/contacts` | [Contacts](/docs/setup/backend/contacts) | `8014` |
| `https://local.reloop.sh/api/mail` | [Mail](/docs/setup/backend/mail) | `8015` |
| `https://local.reloop.sh/api/logs` | [Logs](/docs/setup/backend/logs) | `8016` |
| `https://local.reloop.sh/api/workflow` | [Workflow](/docs/setup/backend/workflow) | `8017` |
| `https://local.reloop.sh/api/upload` | [Upload](/docs/setup/backend/upload) | `8018` |
| `https://local.reloop.sh/api/template` | [Template](/docs/setup/backend/template) | `8019` |
| `https://local.reloop.sh/api/campaigns` | [Campaigns](/docs/setup/backend/campaigns) | `8027` |
| `https://local.reloop.sh/api/inbox` | [Inbox](/docs/setup/backend/inbox) | `8028` |
| `https://local.reloop.sh/api/email` | [Email](/docs/setup/backend/email) | `8022` |
| `https://local.reloop.sh/api/credits` | [Credits](/docs/setup/backend/credits) | `8023` |
| `https://local.reloop.sh/api/admin` | Admin | `8024` |
## Frontend
| App | Direct URL | Port |
| :--- | :--- | :--- |
| [Web](/docs/setup/frontend/web) | `http://localhost:3000` | `3000` |
| [Dashboard](/docs/setup/frontend/dashboard) | `http://localhost:3001` | `3001` |
| Console | `http://localhost:3002` | `3002` |
| [Docs](/docs/setup/frontend/docs) | `http://localhost:3003` | `3003` |
| [Links](/docs/setup/frontend/links) | `http://localhost:3005` | `3005` |
## Backend APIs
| Service | Caddy route | Port |
| :--- | :--- | :--- |
| [Auth](/docs/setup/backend/auth) | `/api/auth` | `8000` |
| [Domain](/docs/setup/backend/domain) | `/api/domain` | `8011` |
| [API Key](/docs/setup/backend/api-key) | `/api/api-key` | `8012` |
| [Webhook](/docs/setup/backend/webhook) | `/api/webhook` | `8013` |
| [Contacts](/docs/setup/backend/contacts) | `/api/contacts` | `8014` |
| [Mail](/docs/setup/backend/mail) | `/api/mail` | `8015` |
| [Logs](/docs/setup/backend/logs) | `/api/logs` | `8016` |
| [Workflow](/docs/setup/backend/workflow) | `/api/workflow` | `8017` |
| [Upload](/docs/setup/backend/upload) | `/api/upload` | `8018` |
| [Template](/docs/setup/backend/template) | `/api/template` | `8019` |
| [Campaigns](/docs/setup/backend/campaigns) | `/api/campaigns` | `8027` |
| [Inbox](/docs/setup/backend/inbox) | `/api/inbox` | `8028` |
| [Email](/docs/setup/backend/email) | `/api/email` | `8022` |
| [Credits](/docs/setup/backend/credits) | `/api/credits` | `8023` |
| Admin | `/api/admin` | `8024` |
## Docker & Mail
| Service | Port | Notes |
| :--- | :--- | :--- |
| Caddy | `80`, `443` | Reverse proxy / TLS |
| Mailpit | `8025` | `http://localhost:8025` (captured outbound mail) |
| [SMTP](/docs/setup/backend/smtp) (outbound) | `465`, `587`, `2025`, `2465`, `2587`, `8020` | KumoMTA |
| [Inbound](/docs/setup/backend/inbound) (MX) | `25`, `8030` | KumoMTA |
## Infrastructure
| Service | Port | Notes |
| :--- | :--- | :--- |
| PostgreSQL | `5432` | Shared DB for all backends (including activity logs) |
| Redis | `6379` | Sessions, OTP, caches |
| NATS | `4222` | Event bus between services |
| MinIO | `9010` (API), `9001` (console) | [Upload](/docs/setup/backend/upload) object storage (console at `http://localhost:9001`) |
| Spam (Rspamd) | `11332` / `11333` / `11334` | [spam](/docs/setup/backend/spam) scan + UI for [inbound](/docs/setup/backend/inbound) |