Source path: app/Http/Middleware/SetCurrentOrganization.php
Extracted from supplied specification. Review before copying into application.

<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class SetCurrentOrganization
{
    public function handle(Request $r, Closure $next)
    {
        $user = $r->user();
        $orgId = $r->header('X-Org-Id') ?: $r->session()->get('org_id') ?: $user->current_organization_id;

        $org = $user->organizations()->where('organizations.id', $orgId)->first()
            ?? $user->organizations()->first();
        abort_if(!$org, 403, 'No organization.');

        app()->instance('currentOrg', $org);
        $r->attributes->set('role', $org->pivot->role);
        return $next($r);
    }
}

Register it in bootstrap/app.php:

php
->withMiddleware(function (Middleware $m) {
    $m->alias(['org' => \App\Http\Middleware\SetCurrentOrganization::class]);
})

Change the route group to Route::middleware(['auth', 'org'])->group(...).

Role helper (app/Support/Roles.php):

php
<?php
namespace App\Support;

class Roles
{
    const LEVEL = ['viewer' => 1, 'editor' => 2, 'admin' => 3, 'owner' => 4];

    public static function require(\Illuminate\Http\Request $r, string $min): void
    {
        abort_if(self::LEVEL[$r->attributes->get('role')] < self::LEVEL[$min], 403, 'Insufficient role.');
    }
}

PostController changes. Replace the user-scoped queries with org-scoped ones:

php
public function index(Request $r)
{
    return Post::with(['targets.socialAccount', 'media'])->latest()->paginate(20); // scope applies automatically
}

public function store(Request $r)
{
    Roles::require($r, 'editor');
    $data = $r->validate([
        'title'        => 'nullable|string|max:150',
        'caption'      => 'required|string|max:5000',
        'accounts'     => 'required|array|min:1',
        'accounts.*'   => 'integer',
        'media_ids'    => 'array',
        'media_ids.*'  => 'integer',
        'scheduled_at' => 'nullable|date|after:now',
    ]);

    $accounts = SocialAccount::whereIn('id', $data['accounts'])->pluck('id'); // org-scoped
    abort_if($accounts->count() !== count($data['accounts']), 403);

    // editors need approval, admins/owners schedule directly
    $needsApproval = $r->attributes->get('role') === 'editor' && config('app.require_approval', false);

    $post = Post::create([
        'user_id' => $r->user()->id,
        'title' => $data['title'] ?? null,
        'caption' => $data['caption'],
        'status' => !isset($data['scheduled_at']) ? 'draft' : ($needsApproval ? 'pending_approval' : 'scheduled'),
        'scheduled_at' => isset($data['scheduled_at']) ? \Carbon\Carbon::parse($data['scheduled_at'])->utc() : null,
    ]);

    foreach ($accounts as $id) $post->targets()->create(['social_account_id' => $id]);
    if (!empty($data['media_ids'])) Media::whereIn('id', $data['media_ids'])->update(['post_id' => $post->id]);

    return response()->json($post->load('targets', 'media'), 201);
}

public function approve(Request $r, Post $post)
{
    Roles::require($r, 'admin');
    abort_if($post->status !== 'pending_approval', 409);
    $post->update(['status' => 'scheduled', 'approved_by' => $r->user()->id]);
    return $post;
}

Add the route Route::post('/posts/{post}/approve', [PostController::class, 'approve']);. In schedule() and destroy(), drop the user_id check, since the global scope handles tenancy, and add Roles::require($r, 'editor').
