Skip to content

Contracts reference

All contracts live in the Integrations\Contracts namespace.

IntegrationProvider (required)

Every provider must implement this interface.

php
interface IntegrationProvider
{
    public function name(): string;
    public function credentialRules(): array;
    public function metadataRules(): array;
    public function credentialDataClass(): ?string;
    public function metadataDataClass(): ?string;
}
MethodReturnsDescription
name()stringHuman-readable provider name
credentialRules()arrayLaravel validation rules for credentials
metadataRules()arrayLaravel validation rules for metadata
credentialDataClass()?stringSpatie Data class-string or null for plain array
metadataDataClass()?stringSpatie Data class-string or null for plain array

HasScheduledSync

Adds automated sync scheduling.

php
interface HasScheduledSync
{
    public function sync(Integration $integration, SyncSession $session): void;
    public function defaultSyncInterval(): int;
    public function defaultRateLimit(): ?RateLimit;
    public function reduceCheckpoints(array $checkpoints): mixed;
}
MethodReturnsDescription
sync()voidEnumerate items and hand each to $session->dispatch()
defaultSyncInterval()intDefault interval in minutes
defaultRateLimit()?RateLimitAPI rate budget, e.g. RateLimit::perHour(5000) (fixed window) or RateLimit::perMinute(700)->sliding(). null = unlimited
reduceCheckpoints()mixedReduce a run's completed checkpoint values into the next sync_cursor

sync() doesn't process items or return a result. It hands each item to $session->dispatch($event, $checkpointValue, $externalId), and the framework wraps each one in a queued ProcessSyncItem job, batches them, runs the listeners, and advances the cursor once every job has succeeded. See Scheduled syncs.

reduceCheckpoints() turns the run's completed checkpoint values into the next cursor. use Integrations\Concerns\ReducesCheckpointsByMax for the common "max wins" reduction (correct for ISO-8601 timestamps and lexicographic ids), or implement it directly for non-comparable cursors.

HasIncrementalSync

Extends HasScheduledSync with delta sync support.

php
interface HasIncrementalSync extends HasScheduledSync
{
    public function syncIncremental(Integration $integration, SyncSession $session): void;
}
MethodReturnsDescription
syncIncremental()voidLike sync(), but fetches only items changed since $session->cursor()

Read the previous cursor with $session->cursor() (null on the first run) and scope the upstream request by it. When a provider implements HasIncrementalSync, the sync job calls syncIncremental() instead of sync().

HandlesWebhooks

Adds inbound webhook handling.

php
interface HandlesWebhooks
{
    public function handleWebhook(Integration $integration, Request $request): mixed;
    public function verifyWebhookSignature(Integration $integration, Request $request): bool;
    public function resolveWebhookEvent(Request $request): ?string;
    public function webhookHandlers(): array;
    public function webhookDeliveryId(Request $request): ?string;
}
MethodReturnsDescription
handleWebhook()mixedProcess the webhook payload
verifyWebhookSignature()boolValidate the webhook signature
resolveWebhookEvent()?stringExtract event type from payload
webhookHandlers()arrayMap of event type to handler class
webhookDeliveryId()?stringDeduplication key

HasOAuth2

Adds OAuth2 authorization flow.

php
interface HasOAuth2
{
    public function authorizationUrl(Integration $integration, string $redirectUri, string $state): string;
    public function exchangeCode(Integration $integration, string $code, string $redirectUri): array;
    public function refreshToken(Integration $integration): array;
    public function revokeToken(Integration $integration): void;
    public function refreshThreshold(): int;
}
MethodReturnsDescription
authorizationUrl()stringBuild the provider's OAuth consent URL
exchangeCode()arrayExchange authorization code for tokens
refreshToken()arrayRefresh an expired access token
revokeToken()voidRevoke the OAuth authorization
refreshThreshold()intSeconds before expiry to trigger refresh

Both exchangeCode() and refreshToken() return ['access_token' => ..., 'refresh_token' => ..., 'token_expires_at' => ...].

HasHealthCheck

Adds lightweight connection testing.

php
interface HasHealthCheck
{
    public function healthCheck(Integration $integration): bool;
}
MethodReturnsDescription
healthCheck()boolTest the connection without a full sync

RedactsRequestData

Declares sensitive fields to redact before persistence.

php
interface RedactsRequestData
{
    public function sensitiveRequestFields(): array;
    public function sensitiveResponseFields(): array;
}
MethodReturnsDescription
sensitiveRequestFields()arrayDot-notation field paths in request data
sensitiveResponseFields()arrayDot-notation field paths in response data

SupportsIdempotency

Marker interface declaring that the upstream API natively dedupes requests by idempotency key. No methods.

php
interface SupportsIdempotency {}

Implement on a provider when the underlying API recognises an idempotency key (Stripe's Idempotency-Key header, for example) and dedupes server-side. Without this marker, core logs a warning when a caller attaches a key. The key still persists to integration_requests.idempotency_key for searchability, but it's a no-op upstream.

See Idempotency for how the key gets onto the wire.

CustomizesRetry

Provider-specific retry decisions for SDKs with custom exceptions.

php
interface CustomizesRetry
{
    public function isRetryable(\Throwable $e): ?bool;
    public function retryDelayMs(\Throwable $e, int $attempt, ?int $statusCode): ?int;
}
MethodReturnsDescription
isRetryable()?boolWhether the exception is retryable, null = fall back to core
retryDelayMs()?intDelay in milliseconds before retry, null = fall back to core

Both methods return null to fall back to the default retry logic.