> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trypost.it/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> Configure TryPost environment variables

# Configuration

TryPost is configured through environment variables in the `.env` file.

## Basic Configuration

```env theme={null}
APP_NAME="TryPost"
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost
```

| Variable    | Description                                      |
| ----------- | ------------------------------------------------ |
| `APP_NAME`  | Your application name                            |
| `APP_ENV`   | Environment: `local`, `staging`, `production`    |
| `APP_DEBUG` | Enable debug mode (set to `false` in production) |
| `APP_URL`   | Your application URL                             |

## Self-Hosted Mode

```env theme={null}
SELF_HOSTED=true
```

`SELF_HOSTED=true` is the default. It bypasses everything billing-related so you never have to touch Stripe or Cashier, and it locks down public sign-ups:

* **Public registration is disabled** — `/register` is closed so random users can't create accounts on your instance. Bootstrap the first admin via the `UserSeeder` (see [Create the admin user](/self-hosting/installation#3-create-the-admin-user)); after that, every account comes from a workspace invite (Settings → Members).
* No Stripe, Cashier, or `CASHIER_TRIAL_DAYS` configuration required
* The `LoadWorkspaceFromToken` middleware skips the `402 Payment Required` gate
* Subscription gating is not enforced, so workspaces, social accounts, members, and AI credits are unlimited per account
* AI calls go straight to your configured provider; you pay the provider directly (no credit accounting)

There's no reason to flip this to `false` on a self-hosted install. The Cloud SaaS deployment is the only place that runs with billing enabled.

## Database

TryPost supports PostgreSQL and MySQL.

### PostgreSQL (recommended)

```env theme={null}
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=trypost
DB_USERNAME=postgres
DB_PASSWORD=your_password
```

### MySQL

```env theme={null}
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=trypost
DB_USERNAME=root
DB_PASSWORD=your_password
```

## Redis

Redis is required for queues and caching.

```env theme={null}
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
```

## Media size limits

```env theme={null}
MEDIA_IMAGE_MAX_SIZE_MB=10
MEDIA_VIDEO_MAX_SIZE_MB=1024
MCP_UPLOAD_MAX_SIZE_MB=50
MCP_UPLOAD_URL_TTL_MINUTES=15
```

| Variable                     | Default | Description                                                                                                                                                                                                                                                    |
| ---------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MEDIA_IMAGE_MAX_SIZE_MB`    | `10`    | Max image upload size, in MB. Applied to direct uploads and URL fetches.                                                                                                                                                                                       |
| `MEDIA_VIDEO_MAX_SIZE_MB`    | `1024`  | Max video upload size, in MB. Applied to direct uploads and URL fetches.                                                                                                                                                                                       |
| `MCP_UPLOAD_MAX_SIZE_MB`     | `50`    | Hard cap (in MB) for files uploaded through the MCP `request-media-upload-tool` signed-URL flow. Intentionally smaller than the per-type caps because uploads stream through the app server. Raise it if your operators / nginx are sized for larger payloads. |
| `MCP_UPLOAD_URL_TTL_MINUTES` | `15`    | Lifetime of the signed upload URL returned by `request-media-upload-tool`.                                                                                                                                                                                     |

If you raise the video limit, also bump PHP's `upload_max_filesize` and `post_max_size` and any reverse-proxy body-size cap to match. The MCP upload endpoint is additionally rate-limited at **10 requests / minute / IP** — this is a hard-coded defense; tune the surrounding nginx/Cloudflare limits if you need a different floor.

## File Storage

TryPost supports local public storage and S3-compatible cloud storage.

The default disk is **Cloudflare R2** (`FILESYSTEM_DISK=r2`). For self-hosted installs without object storage configured, switch to `public`.

### Public Disk

Stores files in `storage/app/public` and serves them directly from `${APP_URL}/storage`.

```env theme={null}
FILESYSTEM_DISK=public
```

After switching, create the symlink from `public/storage` to `storage/app/public`:

```bash theme={null}
php artisan storage:link
```

See Laravel's [public disk documentation](https://laravel.com/docs/13.x/filesystem#the-public-disk) for more details.

### AWS S3

```env theme={null}
FILESYSTEM_DISK=s3
AWS_ACCESS_KEY_ID=your_key
AWS_SECRET_ACCESS_KEY=your_secret
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=your_bucket
AWS_URL=https://your-bucket.s3.amazonaws.com
```

### Cloudflare R2

```env theme={null}
FILESYSTEM_DISK=r2
R2_ACCESS_KEY_ID=your_key
R2_SECRET_ACCESS_KEY=your_secret
R2_ENDPOINT=https://your-account.r2.cloudflarestorage.com
R2_REGION=auto
R2_BUCKET=your_bucket
R2_URL=https://your-custom-domain.com
```

### DigitalOcean Spaces

```env theme={null}
FILESYSTEM_DISK=spaces
SPACES_ACCESS_KEY_ID=your_key
SPACES_SECRET_ACCESS_KEY=your_secret
SPACES_ENDPOINT=https://nyc3.digitaloceanspaces.com
SPACES_REGION=nyc3
SPACES_BUCKET=your_bucket
```

### Other S3-Compatible Storage

Any other S3-compatible storage (MinIO, Backblaze B2, etc.) can be used with the `s3` disk configuration.

## Mail

TryPost sends transactional emails for post notifications, team invites, account alerts, and authentication (email verification, password reset). A working mail configuration is required.

### SendKit (recommended)

[SendKit](https://sendkit.dev?utm_source=trypost\&utm_medium=docs\&utm_campaign=self-hosted) is TryPost's official mailer — built by the same team and wired in as the default. Every new SendKit account is free and includes **3,000 transactional emails per month**, which is plenty for a self-hosted TryPost instance handling post notifications, team invites, and account alerts.

1. Create a free account at [sendkit.dev](https://sendkit.dev?utm_source=trypost\&utm_medium=docs\&utm_campaign=self-hosted)
2. Copy your API key from the SendKit dashboard
3. Drop it into your `.env`:

```env theme={null}
MAIL_MAILER=sendkit
SENDKIT_API_KEY=sk_your_sendkit_api_key
MAIL_FROM_ADDRESS="notifications@yourdomain.com"
MAIL_FROM_NAME="${APP_NAME}"
```

That's it — no DNS warm-up, no domain verification dance to get the first emails out, and the free tier doesn't expire.

### SMTP

If you'd rather use your own SMTP server:

```env theme={null}
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your_username
MAIL_PASSWORD=your_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="notifications@yourdomain.com"
MAIL_FROM_NAME="${APP_NAME}"
```

### Emails sent by TryPost

| Email                    | When it's sent                                                 |
| ------------------------ | -------------------------------------------------------------- |
| **Post published**       | A scheduled post was successfully published                    |
| **Post failed**          | A post failed to publish on one or more platforms              |
| **Account disconnected** | A social account lost its authorization and needs reconnection |
| **Connections check**    | Multiple accounts in a workspace were found disconnected       |
| **Workspace invite**     | A team member is invited to join a workspace                   |
| **Email verification**   | A new user needs to verify their email address                 |
| **Password reset**       | A user requests a password reset                               |

## WebSockets (Reverb)

TryPost uses [Laravel Reverb](https://laravel.com/docs/reverb) for real-time updates (live post status, notifications).

```env theme={null}
REVERB_APP_ID=1001
REVERB_APP_KEY=your-reverb-key
REVERB_APP_SECRET=your-reverb-secret

# Public hostname / port the browser will connect to
REVERB_HOST="localhost"
REVERB_PORT=8080
REVERB_SCHEME=http

# Bind address the Reverb server listens on (defaults to 0.0.0.0:8080)
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
```

| Variable             | Description                                                             |
| -------------------- | ----------------------------------------------------------------------- |
| `REVERB_APP_ID`      | Unique application ID                                                   |
| `REVERB_APP_KEY`     | Application key for client connections                                  |
| `REVERB_APP_SECRET`  | Secret key for server-side authentication                               |
| `REVERB_HOST`        | Public hostname the **browser** uses to connect (advertised to clients) |
| `REVERB_PORT`        | Public port the browser connects to                                     |
| `REVERB_SCHEME`      | Protocol: `http` or `https`                                             |
| `REVERB_SERVER_HOST` | Bind address the Reverb **server** listens on. Defaults to `0.0.0.0`.   |
| `REVERB_SERVER_PORT` | Port the Reverb server listens on. Defaults to `8080`.                  |

<Note>
  In production, set `REVERB_SCHEME=https`, set `REVERB_HOST` to your real public domain (e.g. `app.example.com`), and use a reverse proxy to handle SSL termination for WebSocket connections. `REVERB_SERVER_HOST` stays `0.0.0.0` so the daemon listens on all interfaces inside the box.
</Note>

## Social Platforms

Each social platform requires API credentials from its developer portal. See each [platform's documentation](/platforms/overview) for step-by-step setup.

<Note>
  **Bluesky** and **Mastodon** don't require API credentials — they work out of the box. **Telegram** uses a single shared bot you create with [@BotFather](https://t.me/BotFather), and **Discord** uses a single shared bot application from the [Discord Developer Portal](https://discord.com/developers/applications) — neither is a per-user OAuth app. See their credentials below.
</Note>

### All platform credentials

```env theme={null}
# LinkedIn (https://developer.linkedin.com)
LINKEDIN_CLIENT_ID=
LINKEDIN_CLIENT_SECRET=
LINKEDIN_CLIENT_REDIRECT="${APP_URL}/accounts/linkedin/callback"
LINKEDIN_PAGE_CLIENT_REDIRECT="${APP_URL}/accounts/linkedin-page/callback"

# X / Twitter (https://developer.twitter.com)
X_CLIENT_ID=
X_CLIENT_SECRET=
X_CLIENT_REDIRECT="${APP_URL}/accounts/x/callback"

# Facebook (https://developers.facebook.com)
FACEBOOK_CLIENT_ID=
FACEBOOK_CLIENT_SECRET=
FACEBOOK_CLIENT_REDIRECT="${APP_URL}/accounts/facebook/callback"

# Instagram (https://developers.facebook.com)
INSTAGRAM_CLIENT_ID=
INSTAGRAM_CLIENT_SECRET=
INSTAGRAM_CLIENT_REDIRECT="${APP_URL}/accounts/instagram/callback"

# Threads (https://developers.facebook.com)
THREADS_CLIENT_ID=
THREADS_CLIENT_SECRET=
THREADS_CLIENT_REDIRECT="${APP_URL}/accounts/threads/callback"

# TikTok (https://developers.tiktok.com)
TIKTOK_CLIENT_ID=
TIKTOK_CLIENT_SECRET=
TIKTOK_CLIENT_REDIRECT="${APP_URL}/accounts/tiktok/callback"

# Google / YouTube (https://console.cloud.google.com)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_CLIENT_REDIRECT="${APP_URL}/accounts/youtube/callback"

# Pinterest (https://developers.pinterest.com)
PINTEREST_CLIENT_ID=
PINTEREST_CLIENT_SECRET=
PINTEREST_CLIENT_REDIRECT="${APP_URL}/accounts/pinterest/callback"

# Telegram (single shared bot — create one with https://t.me/BotFather)
TELEGRAM_BOT_TOKEN=
TELEGRAM_BOT_USERNAME=
TELEGRAM_WEBHOOK_SECRET=

# Discord (single shared bot app — https://discord.com/developers/applications)
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
DISCORD_BOT_TOKEN=
DISCORD_CLIENT_REDIRECT="${APP_URL}/accounts/discord/callback"
```

<Note>
  Telegram delivers `/connect` commands and reactions over a webhook. After setting the variables above, register it with `php artisan telegram:set-webhook`. The webhook URL is `{APP_URL}/telegram/webhook`, so `APP_URL` must be a public HTTPS URL Telegram can reach — re-run the command whenever it changes. The bot must be added as an **administrator** of each channel or group it publishes to.
</Note>

<Note>
  Discord uses a single shared bot application (create one in the [Discord Developer Portal](https://discord.com/developers/applications)). Use the OAuth2 **Client Secret** — not the application's **Public Key** — for `DISCORD_CLIENT_SECRET`, and add the redirect URL above under **OAuth2 → Redirects**. The bot invite permissions and scopes are configurable via `DISCORD_PERMISSIONS` (default `248832`) and `DISCORD_SCOPES` (default `bot,identify,guilds`). See the [Discord platform guide](/platforms/discord) for the full setup.
</Note>

### Enabling/disabling platforms

You can selectively enable or disable platforms without removing their credentials. This is useful when API credentials are pending approval or temporarily revoked.

```env theme={null}
LINKEDIN_ENABLED=true
LINKEDIN_PAGE_ENABLED=true
X_ENABLED=true
TIKTOK_ENABLED=true
YOUTUBE_ENABLED=true
FACEBOOK_ENABLED=true
INSTAGRAM_ENABLED=true
INSTAGRAM_FACEBOOK_ENABLED=true
THREADS_ENABLED=true
PINTEREST_ENABLED=true
BLUESKY_ENABLED=true
MASTODON_ENABLED=true
TELEGRAM_ENABLED=true
DISCORD_ENABLED=true
```

Set any platform to `false` to hide it from the UI. All platforms are enabled by default.

## Social login

TryPost supports sign in with **Google** and **GitHub**. Both providers are off by default — set the corresponding `*_AUTH_ENABLED` flag and provide credentials to surface the buttons on the login/register pages.

### Google

Google login reuses the OAuth credentials from the YouTube integration, with a separate callback URL.

```env theme={null}
GOOGLE_AUTH_ENABLED=true
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_AUTH_CALLBACK="${APP_URL}/auth/google/callback"
```

In the [Google Cloud Console](https://console.cloud.google.com), add both callback URLs to your OAuth 2.0 client:

* `https://your-domain.com/accounts/youtube/callback` (YouTube connection)
* `https://your-domain.com/auth/google/callback` (Google login)

### GitHub

```env theme={null}
GITHUB_AUTH_ENABLED=true
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GITHUB_AUTH_CALLBACK="${APP_URL}/auth/github/callback"
```

Register a new OAuth App at [github.com/settings/developers](https://github.com/settings/developers) and set the **Authorization callback URL** to `${APP_URL}/auth/github/callback`.

## AI features (optional)

The Generate / Review / Create AI flows in the post editor need a configured text-generation provider. Without one, the AI buttons stay disabled.

```env theme={null}
AI_TEXT_PROVIDER=openai
AI_TEXT_MODEL=gpt-5.4
OPENAI_API_KEY=sk-...

# Optional — image generation for the AI Create wizard
AI_IMAGE_PROVIDER=openai

# Optional — voiceover / TTS (used by future video flows)
AI_AUDIO_PROVIDER=eleven
ELEVENLABS_API_KEY=
```

| Variable            | Description                                                                                                                                                                                                                                                                                                          |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AI_TEXT_PROVIDER`  | `openai` (default), `anthropic`, `gemini`, `azure`, `groq`, `xai`, `deepseek`, `mistral`, `ollama`, `openrouter`, `cohere`, `jina`, or `voyageai`.                                                                                                                                                                   |
| `AI_TEXT_MODEL`     | Model identifier for the text provider. Defaults to `gpt-5.4`.                                                                                                                                                                                                                                                       |
| `AI_IMAGE_PROVIDER` | Provider used for AI image generation in the Create wizard. Defaults to `openai`. The active OpenAI image model is hardcoded to `gpt-image-2`.                                                                                                                                                                       |
| `AI_AUDIO_PROVIDER` | TTS provider. `openai` (default) or `eleven`. Note the key is `eleven`, not `elevenlabs` — setting `elevenlabs` will not resolve.                                                                                                                                                                                    |
| Provider API keys   | Each provider reads its own env: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `XAI_API_KEY`, `DEEPSEEK_API_KEY`, `GROQ_API_KEY`, `MISTRAL_API_KEY`, `OPENROUTER_API_KEY`, `COHERE_API_KEY`, `JINA_API_KEY`, `VOYAGEAI_API_KEY`, `ELEVENLABS_API_KEY`. Ollama uses `OLLAMA_BASE_URL` (no key by default). |

Self-hosted instances skip the credit/quota check entirely (`SELF_HOSTED=true`), so AI calls go straight to the configured provider — you pay the provider directly.

## Asset library (Unsplash & Giphy)

The **Assets** page exposes optional Unsplash and Giphy tabs. Configure these to enable them:

```env theme={null}
UNSPLASH_ACCESS_KEY=
UNSPLASH_SECRET_KEY=

GIPHY_API_KEY=
```

Without these keys, the Library tab still works — only the stock-photo and GIF tabs are hidden.

## Analytics (optional)

### PostHog

```env theme={null}
POSTHOG_ENABLED=true
POSTHOG_API_KEY=phc_your_key
POSTHOG_HOST=https://us.i.posthog.com

VITE_POSTHOG_API_KEY="${POSTHOG_API_KEY}"
VITE_POSTHOG_HOST="${POSTHOG_HOST}"
```

`POSTHOG_ENABLED` gates the entire integration — without it set to `true`, the keys are ignored.

### Google Tag Manager

```env theme={null}
GTM_ID=GTM-XXXXXXX
```

Both are optional and only needed if you want to track usage analytics.

## Horizon (Queue Dashboard)

[Laravel Horizon](https://laravel.com/docs/horizon) provides a dashboard to monitor your queues. Access it at `/horizon`.

```env theme={null}
HORIZON_ALLOWED_EMAILS=admin@example.com,dev@example.com
```

| Variable                 | Description                                                            |
| ------------------------ | ---------------------------------------------------------------------- |
| `HORIZON_ALLOWED_EMAILS` | Comma-separated list of emails allowed to access Horizon in production |

If not set, Horizon dashboard will be inaccessible in non-local environments.

## Next Steps

* [Connect your social accounts](/platforms/overview)
* [Create your first post](/getting-started/quickstart)
