raw() method returning a RawResource that can send arbitrary requests to the API). This allows consumers to call any API endpoint even when no dedicated resource method exists. All other endpoints and resources are optional and can be added as needed.
💡 Pro Tip for AI Agents: Always start by examining the closest existing implementation to your target API. All SDKs live under src/Connectors/. For the shared GET-request pattern (one GetRequest per connector, URL and headers in the resource), study Telavox or MicrosoftGraph. For simple token-based APIs, study HubSpot. For complex OAuth APIs, study Business Central. For multi-credential APIs, study Harvest.
Table of Contents
- Architecture Overview
- Resources Pattern
- Step-by-Step Implementation
- Templates
- Best Practices
- Workspace search (connectors)
Architecture Overview
Our SDK architecture follows a consistent pattern across all connectors:Core Components
- Raw endpoint (required): Every connector must provide
raw()returning aRawResourcewith arequest($url, $method, $data, $query)method so any API endpoint can be called. ImplementRawRequest(accepting endpoint, method, optional body, query) andRawResource(building full URL from connector base when a path is passed). See HubSpot or Brevo for the pattern. - Service Class: Main entry point using WithResources trait
- Resource Classes (optional): Group related functionality (e.g., DealsResource, ItemsResource)
- Connector: Handles HTTP connection and authentication
- Request Classes: One shared
GetRequestper connector (endpoint + query built in the resource); separate classes for POST/PUT/DELETE or custom behaviour (e.g. Search, Update) - Raw JSON: Direct API responses for maximum performance
- DataCollection: Collections using
collect($arrays)
Resources Pattern
Why Use Resources?
Resources organize related API methods into logical groups, preventing the main service class from becoming unwieldy.Before vs After
Resource Implementation (shared GetRequest)
Resources build the endpoint path and query (and optional headers) and pass them to a singleGetRequest per connector. This keeps the request layer thin and avoids many small GET request classes.
Step-by-Step Implementation
1. Create Configuration
config/{servicename}.php:
2. Create Connector
src/Connectors/{Service}/{Service}Connector.php:
3. Create a shared GetRequest (and other request types as needed)
Use oneGetRequest per connector. The resource builds the endpoint and query and passes them in. For list endpoints that use Saloon pagination, the connector’s paginator will merge pagination params (e.g. after, limit) into the request.
src/Connectors/{Service}/Requests/GetRequest.php:
PostRequest, PatchRequest, PutRequest, DeleteRequest. Each takes endpoint and (where applicable) body and query; the resource builds the body and passes it in. Do not create per-action classes like SearchDealsRequest or UpdateDealRequest—build the request body in the resource and call e.g. new PostRequest('/crm/v3/objects/deals/search', $body). Add a dedicated request class only for genuine edge cases (e.g. multipart, custom pagination). See HubSpot in src/Connectors/ for reference.
4. Create Resource Classes
Resources build the endpoint path and query and pass them toGetRequest. No per-endpoint GET request classes are needed.
src/Connectors/{Service}/Resources/{Resource}Resource.php:
5. Create WithResources Trait
src/Connectors/{Service}/WithResources.php:
6. Create Main Service Class
src/Connectors/{Service}/{Service}.php:
7. Create Temporary test command
Do this while developing to verify that your SDK actually works. Ask the user to configure the .env variables correctly so that you can hit the live API. After you’re done, delete the test command.app/Console/Commands/Tests/Test{Service}Command.php:
Templates
Directory Structure
SDKs live undersrc/Connectors/{ServiceName}/. Use one shared GetRequest per connector and one request class per HTTP method for non-GET: PostRequest, PatchRequest, PutRequest, DeleteRequest. The resource is responsible for building the full body and passing it as an argument (e.g. new PostRequest($endpoint, $body)). Do not create multiple request classes per operation (e.g. no SearchDealsRequest, UpdateDealRequest). Add a dedicated request class only for genuine edge cases (e.g. multipart uploads, custom pagination behaviour).
BaseResource Class
Best Practices
Naming Conventions
- Service classes:
{ServiceName}(e.g.,HubSpot,BusinessCentral) - Connectors:
{ServiceName}Connector - Resources:
{Resource}Resource(e.g.,DealsResource,ItemsResource) - Requests: One
GetRequestper connector; onePostRequest,PatchRequest,PutRequest,DeleteRequestper connector (resource builds endpoint and body). No per-action request classes (e.g. noSearchDealsRequest,UpdateDealRequest) unless a genuine edge case.
Method Naming
- List multiple:
list()→ returnsDataCollection<array> - Get single:
get($id)→ returnsarray - Search:
search($filters)→ returnsDataCollection<array> - Manual pagination:
paginate()→ returnsOffsetPaginator
Performance Patterns
Error Handling
All connectors must use theThrowsConnectorExceptions trait so failed requests throw the shared Inly\Core\Connectors\Exceptions types (e.g. UnauthorizedException, NotFoundException, RequestException) instead of Saloon’s default. Use AlwaysThrowOnErrors so 4xx/5xx responses trigger this behaviour.
Configuration
Workspace search (connectors)
Core provides an Artisan command to control whether connector code undersrc/Connectors/ is included in your editor’s workspace search. This keeps search results focused when you only use a subset of connectors.
Enable/disable all connectors
-
Exclude all connectors from search (recommended starting point if you don’t use them):
This adds
"**/Connectors/*": truetosearch.excludein your.code-workspacefile. -
Include all connectors in search again:
This removes the Connectors exclude pattern from the workspace file.
-
Exclude one connector (e.g. Brevo) from search:
-
Include one connector in search again:
*.code-workspace files in the project root; it does not change code or Composer.
Suggested workflow: Run php artisan core:connector-workspace enable <Name> for each connector you actually use so only unused SDKs are hidden from search.
Implementation Checklist
- Raw endpoint (required): Add
RawRequest,RawResource, andraw()onWithResourcesso consumers can call any API endpoint. All other endpoints are optional. - Configuration: Create
config/{service}.php - Connector: Create connector with authentication; use
ThrowsConnectorExceptions(andAlwaysThrowOnErrorswhen errors should throw) - Requests: Create one
GetRequest(endpoint + query from resource); add other request classes only for non-GET or custom behaviour - Resources (optional): Create resource classes extending
BaseResourceas needed - WithResources: Create trait organizing resource accessors (include
raw()) - Service: Create main service class using
WithResources - BaseResource (optional): Create base resource with connector injection when using resources
- Testing: Create test command
- Performance: Use
array_merge($items, $rawData)pattern - DataCollection: Use
collect($arrays)