DEVELOPER DOCS

Connect your own website.

Install the Calmify package on your Laravel, PHP or Next.js site, show the posts where you want them, and pair it with a one-time code. No keys or addresses to copy between dashboards.

You need: Laravel 10, 11 or 12 · PHP 8.1+ · a database Laravel can migrate

  1. Install the package

    From your Laravel project folder:

    Terminal
    composer require calmify/laravel
  2. Set up the receiver

    Make sure APP_URL in .env is the exact public address of your site, including https:// and www if you use it. Then run the installer. It writes a private CALMIFY_API_KEY to .env, creates the calmify_posts table and opens the route POST /api/calmify/posts.

    Covers are saved on the public disk, so link it once if you haven’t already.

    Terminal
    php artisan calmify:install
    php artisan storage:link
  3. Show the posts on your site

    Posts are saved to calmify_posts with sanitized HTML. Calmify links to them at /blog/{slug} by default (change it with CALMIFY_POST_URL). Add the routes and a view:

    routes/web.php
    use Illuminate\Support\Facades\DB;
    
    Route::get('/blog', fn () => view('blog.index', [
        'posts' => DB::table('calmify_posts')->whereNotNull('published_at')->latest('published_at')->paginate(12),
    ]));
    
    Route::get('/blog/{slug}', fn (string $slug) => view('blog.show', [
        'post' => DB::table('calmify_posts')->where('slug', $slug)->whereNotNull('published_at')->firstOrFail(),
    ]));
    resources/views/blog/show.blade.php
    <article>
        <h1>{{ $post->title }}</h1>
        @if ($post->image_path)
            <img src="{{ Storage::url($post->image_path) }}" alt="{{ $post->image_alt }}">
        @endif
        {!! $post->content_html !!}
    </article>
  4. Or copy posts into your own model

    Already have a blog? Listen for the PostReceived event and save each post where your site expects it. Laravel 11+ finds listeners in app/Listeners automatically. Copy by slug (updateOrCreate) so an update replaces your copy instead of adding a second one. To change settings, run php artisan vendor:publish --tag=calmify-config.

    app/Listeners/CopyCalmifyPost.php
    <?php
    
    namespace App\Listeners;
    
    use App\Models\Post;
    use Calmify\Laravel\Events\PostReceived;
    
    class CopyCalmifyPost
    {
        public function handle(PostReceived $event): void
        {
            $p = $event->post;
            Post::updateOrCreate(['slug' => $p->slug], [
                'title' => $p->title,
                'body' => $p->content_html,
                'excerpt' => $p->meta_description,
                'cover' => $p->image_path,
            ]);
        }
    }
  5. Updates and unpublishing

    Update on site in Calmify replaces the same row in calmify_posts (title, content, meta, cover; the slug and first publish date stay) and fires PostReceived again with $event->updated set to true. The previous cover file is deleted, so copy image_path again on every event.

    Unpublish from site sets published_at to null (the row and cover stay) and fires PostUnpublished. If you show calmify_posts directly with the whereNotNull('published_at') filter above, there's nothing to add. If you copy posts into your own model, hide your copy in a listener. Publishing again from Calmify brings the post back through PostReceived.

    Images inside articles are downloaded like the cover and saved on the same disk (calmify/<slug>_inline-…); content_html already points to your copies, so there's nothing to add.

    app/Listeners/HideCalmifyPost.php
    <?php
    
    namespace AppListeners;
    
    use AppModelsPost;
    use CalmifyLaravelEventsPostUnpublished;
    
    class HideCalmifyPost
    {
        public function handle(PostUnpublished $event): void
        {
            Post::where('slug', $event->post->slug)->update(['published_at' => null]);
        }
    }
  6. Get a pairing code in Calmify

    In your workspace, open Websites, choose your site and set the publishing destination to Custom API. Save, then click Connect under “Connect with a pairing code”.

    You get a code like CALM-XXXX-XXXX. It works once and expires after 10 minutes. If it runs out, click New code.

  7. Pair your site

    Deploy first so the route is live, then run this on the server (or anywhere with the same .env). If you cache config, run php artisan config:cache afterwards.

    Back in Calmify, click I ran it, check the connection. Calmify tests your site and shows the connected address. From now on, articles you publish or schedule for this website arrive on your site, edits you send with Update on site replace the same post, and Unpublish from site hides it.

    Terminal
    php artisan calmify:pair CALM-XXXX-XXXX