IntegrationDriver contract
Every driver implementsBjanczak\FilamentFlexForms\Integrations\Contracts\IntegrationDriver:
Extend
AbstractIntegrationDriver for shared HTTP helpers, or start from:
GenericApiIntegrationDriver— JSON POST + bearer + SSRF guard + redacted delivery metaGenericOAuthIntegrationDriver— OAuth authorize/callback with PKCE S256 + ConnectionsGenericSignedWebhookDriver— HMAC-signed outbound webhooks
Register drivers (3 ways)
1. Config (explicit)
config/filament-flex-forms.php
2. Discovery folder
3. Plugin boot()
Artisan scaffold
app/FlexForms/Integrations/{Name}Driver.phptests/Feature/FlexForms/{Name}DriverTest.php(skip with--no-test)
IntegrationDriverTestCase (key slug, credential mode, normalize helpers).
Cookbook 1 — REST list API (end-to-end)
1
Scaffold
php artisan flex-forms:make-integration AcmeList --api2
Implement endpoint + payload
Override
endpoint() (HTTPS public URL only — SSRF guarded) and buildPayload() from FieldMapper output. Optionally override testConnectionRequest() to return a health URL so Test connection performs a real GET ping.3
Register
Add the class to
extensibility.integrations.drivers (or discovery).4
Credentials
Open Integrations, paste the API key (encrypted at rest). For Connection-tier drivers, create a Connection instead.
5
Map fields on a form
Enable the feed → Advanced field mapping (
email → email, transforms like trim / phone_e164). Save + publish.6
Send test
Use Send test — delivery ledger shows
source=sandbox with redacted secrets. Submit a live response and confirm a succeeded delivery row.7
Pest
Extend
IntegrationDriverTestCase and add Http::fake() coverage for a 200 dispatch and a 429 retryable failure if your driver surfaces that status.app/FlexForms/Integrations/AcmeListDriver.php
Cookbook 2 — OAuth CRM with PKCE (end-to-end)
Flex Forms OAuth Connections use authorization code + PKCE (S256) by default.1
Scaffold
php artisan flex-forms:make-integration AcmeHub --oauth2
Wire provider URLs
Implement
getOAuthAuthorizeUrl(), getTokenUrl(), getClientId(), getClientSecret(), getOAuthScopes(). Keep usesPkce() at default true unless the provider rejects PKCE.3
Register callback
In the provider console set redirect URI exactly to
https://your-domain/flex-forms/connections/oauth/callback.4
Connect account
Integrations → Connections → Create → Connect. Redirect builds
code_challenge; callback exchanges code + code_verifier (one-time cache, bound to admin user) and returns to the Connections tab.5
Attach to a form
Set
connection_id on the feed (Studio Connections picker). Tokens refresh via OAuthTokenManager (mutex + needs_reauth on failure).6
Verify
Test connection, then Send test. Confirm deliveries never store raw
client_secret / bearer tokens.usesPkce(): false only for legacy providers. Prefer fixing the provider registration over disabling PKCE.
Cookbook 3 — Signed partner webhook (end-to-end)
1
Scaffold
php artisan flex-forms:make-integration AcmeHook --webhook2
Secret
Store
webhook_secret on the Connection (or global config). Outbound body is HMAC-SHA256 signed.3
Feed URL
Set
request_url to an HTTPS public endpoint. Private / metadata hosts are rejected (SSRF guard).4
Map + Send test
Map nested fields, enable the feed, Send test, inspect the submission ledger (
source=sandbox, redacted context).Field mapping + transforms
Host transforms:trim, uppercase, merge_template, split_name, phone_e164, file_url, and more — see Field mapping.
Isolation & safety checklist
- Use
AbstractIntegrationDriver::httpClient()(timeouts, no open redirects) - Expect delivery
contextto be redacted (Authorization,api_key, tokens) - Scope Connections in multi-tenant apps via
modifyConnectionsEloquentQueryUsing()— Multi-tenancy - Run
php artisan flex-forms:upgradeafter package updates