jeffersongoncalves/laravel-webflow
A Laravel package for integrating Webflow into your application, offering configuration, service bindings, and helpers to fetch and manage Webflow CMS content and API data from within your Laravel projects.
The package is a thin, opinionated wrapper around Laravel's Http client for the Webflow REST API v2. There is no SDK magic to learn — if you know the Webflow v2 endpoints, you know what the package does.
composer require the package, then publish its config (php artisan vendor:publish --tag=webflow-config or similar — check the README for the exact tag).WEBFLOW_TOKEN in .env; never commit it. Token scopes are selected at creation time (cms:read, cms:write, forms:read, sites:read, etc.) — decide these up front, because re-scoping means minting a new token.config/app.php.First use case: a read-only smoke test. Resolve the client (via facade or container), call the sites endpoint, and dump the response. This validates token, network, and package wiring in one shot before you touch CMS data. Then move to listing collections for a site and paginating items in one collection — that's the 80% path for most real integrations.
Wrap the client, don't sprinkle it. Put the package behind your own WebflowService or a small set of repositories. The Webflow API is stable but not static; a single seam means a breaking package upgrade touches one file instead of forty.
Cache collection schemas aggressively. Collection field definitions change rarely and are needed to build forms, map DTOs, and validate payloads. Cache::remember("webflow.collection.{$id}.schema", now()->addDay(), ...) saves a request per operation and keeps you well under rate limits.
Treat CMS writes as two-step. Creating or updating an item does not publish it. You create/update, then publish the item explicitly. Model this in your own API ($service->upsertAndPublish($collection, $data)) so callers can't accidentally leave content in draft — or, if drafts are the point, make that explicit in the method name.
Queue anything bulk. Migrating 2,000 items is a job, not a request. Use a queued job with RateLimited middleware or Redis::throttle, chunk the item list, and make each job idempotent by keying on slug or an external ID you control. Webflow has no upsert-by-external-id, so your job needs a lookup step.
Ingest form submissions via webhook, not polling. Configure a webhook in Webflow pointing at a route in your app, verify and persist the payload, then dispatch. Polling form submissions on a schedule works but burns rate limit and lags. Either way, dedupe on submission ID — webhooks retry.
Test with Http::fake(). The package rides on Laravel's HTTP client, so Http::fake([...]) and Http::assertSent() work as normal. Record real responses once into tests/Fixtures/webflow/*.json and replay them; hand-written fakes drift from the real shape fast, especially for rich-text and reference fields.
Multi-tenant: if you're building for multiple client sites, don't rely on the single configured token. Resolve credentials per tenant (a WebflowAccount model holding an encrypted token) and construct a client instance with that token rather than the singleton from the container.
429. The wrapper likely does not retry for you — add ->retry(3, 200, throw: false) at your seam, or a job-level backoff. A silent 429 inside a loop looks exactly like "the API is broken."total in the response. Loop until collected >= total and guard against total shifting mid-loop on large, actively-edited collections. Set an explicit limit (100 is the usual max) rather than accepting defaults.401 = bad/missing token. 403 = valid token, insufficient scope, or the site isn't accessible to that token. Check scopes before rewriting code.json/longText and render with a dedicated renderer. Naively json_encode-ing an entire item into a log line will flood your logs.composer update, and lock the constraint (^1.0) rather than floating.How can I help you explore Laravel packages today?