graystackit/laravel-helpspace-api

Laravel package for the HelpSpace API, built on Saloon 4

Maintainers

Package info

github.com/GraystackIT/laravel-helpspace-api

pkg:composer/graystackit/laravel-helpspace-api

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-04-21 08:11 UTC

This package is auto-updated.

Last update: 2026-05-05 18:47:25 UTC


README

A Laravel package for the HelpSpace API, built on Saloon 4.

Supports Tickets, Messages, Reports, and Webhooks.

Requirements

  • PHP 8.3+
  • Laravel 11, 12, or 13

Installation

composer require graystackit/laravel-helpspace-api

The service provider is auto-discovered by Laravel.

Publish the config file:

php artisan vendor:publish --tag=helpspace-config

Add your credentials to .env:

HELPSPACE_API_KEY=your-bearer-token
HELPSPACE_CLIENT_ID=your-client-id
HELPSPACE_BASE_URL=https://api.helpspace.com

Configuration

After publishing, the config file lives at config/helpspace.php:

return [
    'api_key'  => env('HELPSPACE_API_KEY'),
    'client_id' => env('HELPSPACE_CLIENT_ID'),
    'base_url' => env('HELPSPACE_BASE_URL', 'https://api.helpspace.com'),

    'defaults' => [
        'per_page' => 20,
    ],
];

Usage

Resolve HelpSpaceClient from the container or inject it via the constructor:

use GraystackIT\HelpSpace\HelpSpaceClient;

class SupportController extends Controller
{
    public function __construct(private HelpSpaceClient $helpspace) {}
}

Tickets

API reference: https://documentation.helpspace.com/api-tickets

List tickets

$result = $this->helpspace->listTickets(
    perPage:  25,
    statuses: ['open', 'escalated'],
    contacts: ['customer@example.com'],
    tags:     ['billing'],
    createdBetween: '2024-01-01/2024-01-31',
);

foreach ($result['data'] as $ticket) {
    echo $ticket->id;           // int
    echo $ticket->subject;      // string
    echo $ticket->status;       // string (open|closed|waiting|escalated|spam|unassigned)
    echo $ticket->contactEmail; // string|null
    echo $ticket->contactName;  // string|null
    echo $ticket->assigneeName; // string|null
    echo $ticket->teamName;     // string|null
    print_r($ticket->tags);     // array
    echo $ticket->createdAt;    // ISO 8601 string
}

// Pagination metadata
$result['meta'];   // ['current_page' => 1, 'total' => 120, ...]
$result['links'];  // ['next' => '...', 'prev' => null, ...]

listTickets() parameters

Parameter Type Default Description
$perPage int 20 (max 50) Results per page
$statuses string[] [] Filter by status values
$contacts string[] [] Filter by contact email
$assignees string[] [] Filter by assignee name or ID
$tags string[] [] Filter by tag names
$subjects string[] [] Filter by subject text
$bodies string[] [] Filter by body text
$subjectOrBody string[] [] Filter by subject or body
$organizations string[] [] Filter by organization
$teams string[] [] Filter by team
$createdBetween string|null null Date range YYYY-MM-DD/YYYY-MM-DD
$lastContactBetween string|null null Date range YYYY-MM-DD/YYYY-MM-DD

TicketStatus enum values:

Case API value
Unassigned unassigned
Open open
Escalated escalated
Spam spam
Waiting waiting
Closed closed

Get a ticket

$ticket = $this->helpspace->getTicket(101);

Create a ticket

$ticket = $this->helpspace->createTicket([
    'subject'      => 'Cannot access my account',
    'channel'      => ['id' => 'channel-uuid'],
    'from_contact' => ['email' => 'user@example.com', 'name' => 'John Doe'],
    'message'      => ['body' => '<p>I cannot log in since yesterday.</p>'],
    'tags'         => ['access', 'urgent'],
    // Optional:
    'skip_autoreply'      => false,
    'skip_notifications'  => false,
]);

Update a ticket

$ticket = $this->helpspace->updateTicket(101, [
    'status'   => 'closed',
    'assignee' => ['id' => 'agent-uuid'],
]);

Delete a ticket

Soft-deleted tickets are permanently removed after 30 days.

$this->helpspace->deleteTicket(101); // returns true

Messages

API reference: https://documentation.helpspace.com/api-message

List messages

Default types returned: external, widget, forward.

// Default message types
$messages = $this->helpspace->listMessages(101);

// Include internal notes as well
$messages = $this->helpspace->listMessages(101, additionalTypes: ['internal']);

// Only return internal notes
$messages = $this->helpspace->listMessages(101, types: ['internal']);

foreach ($messages as $message) {
    echo $message->id;               // int
    echo $message->type;             // external|internal|forward|widget|...
    echo $message->fromContactEmail; // string
    echo $message->fromContactName;  // string
    echo $message->body;             // HTML string
    echo $message->createdAt;        // ISO 8601 string
    print_r($message->attachments);  // array
    print_r($message->to);           // array of recipient addresses
}

MessageType enum values:

Case API value Default returned
External external Yes
Widget widget Yes
Forward forward Yes
Internal internal No
Error error No
Bounce bounce No
Event event No
AiSummary ai-summary No

Get a message

$message = $this->helpspace->getMessage(ticketId: 101, messageId: 77);

Create a message

$message = $this->helpspace->createMessage(101, [
    'from_contact' => ['email' => 'agent@example.com', 'name' => 'Support Agent'],
    'subject'      => 'Re: Cannot access my account',
    'body'         => '<p>We have reset your password. Please check your email.</p>',
    // Optional:
    'type'                  => 'external',      // default
    'to'                    => ['user@example.com'],
    'cc'                    => [],
    'bcc'                   => [],
    'skip_notifications'    => false,
    'send_mail_to_recipients' => true,
    // Attachments as base64 (max 5 MB combined)
    'attachments' => [
        ['file_name' => 'guide.pdf', 'mime_type' => 'application/pdf', 'data' => base64_encode($pdfBytes)],
    ],
]);

Reports

API reference: https://documentation.helpspace.com/api-reports

Channels report

$report = $this->helpspace->getChannelsReport('2024-01-01', '2024-01-31');

print_r($report->dailyCounts);  // per-day opened/closed ticket counts
print_r($report->metrics);      // new, closed, open, waiting, escalated counts
print_r($report->channels);     // ticket volume by channel
print_r($report->tags);         // ticket volume by tag
print_r($report->topCustomers); // customers with most tickets
print_r($report->raw);          // full raw API response

Performance report

$report = $this->helpspace->getPerformanceReport('2024-01-01', '2024-01-31');

print_r($report->dailyCounts);  // per-day opened/closed counts
print_r($report->metrics);      // avg resolution time breakdown (days/hours/minutes)
print_r($report->topAgents);    // agents with most resolved tickets
print_r($report->raw);          // full raw API response

Both methods accept date strings in YYYY-MM-DD format. An InvalidArgumentException is thrown if either date is empty.

Webhooks

API reference: https://documentation.helpspace.com/article/340/webhook

Get webhook configuration

$config = $this->helpspace->getWebhook();

echo $config->enabled;     // bool
echo $config->url;         // destination URL
echo $config->secret;      // validation hash (string|null)
print_r($config->headers); // custom headers [['key' => '...', 'value' => '...']]
print_r($config->trigger); // enabled event types per resource
echo $config->failedCount; // consecutive failure count (auto-disables on too many)

Update webhook configuration

$config = $this->helpspace->updateWebhook([
    'enabled' => true,
    'url'     => 'https://myapp.example.com/webhooks/helpspace',
    'secret'  => 'my-validation-secret',
    'headers' => [
        ['key' => 'X-App-Key', 'value' => 'my-app-key'],
    ],
    'trigger' => [
        'ticket'   => ['created' => true, 'updated' => true, 'deleted' => false],
        'customer' => ['created' => false],
        'tag'      => ['created' => false],
    ],
]);

Get webhook error logs

$logs = $this->helpspace->getWebhookLogs();

foreach ($logs as $log) {
    echo $log['status'];     // HTTP status code returned by your endpoint
    echo $log['response'];   // response body
    echo $log['created_at']; // when the failure occurred
}

Exceptions

Exception When thrown
HelpSpaceApiException API returned 4xx/5xx, network failure, or non-JSON response
InvalidArgumentException Empty subject on createTicket(), empty body on createMessage(), or empty dates on report methods

Testing

This package uses Saloon's MockClient so you can test without real HTTP calls:

use GraystackIT\HelpSpace\HelpSpaceClient;
use GraystackIT\HelpSpace\Connectors\HelpSpaceConnector;
use GraystackIT\HelpSpace\Requests\Tickets\ListTicketsRequest;
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;

$mockClient = new MockClient([
    ListTicketsRequest::class => MockResponse::make([
        'data'  => [
            [
                'id'           => 1,
                'subject'      => 'Test ticket',
                'status'       => 'open',
                'from_contact' => ['email' => 'user@example.com', 'name' => 'User'],
                'created_at'   => '2024-01-01T00:00:00Z',
            ],
        ],
        'meta'  => [],
        'links' => [],
    ], 200),
]);

$connector = app(HelpSpaceConnector::class);
$connector->withMockClient($mockClient);

$result = (new HelpSpaceClient($connector))->listTickets();

Run the package test suite:

composer install
vendor/bin/pest

API Endpoints Reference

Method HTTP HelpSpace API Endpoint
listTickets() GET /api/v1/tickets
getTicket() GET /api/v1/tickets/{id}
createTicket() POST /api/v1/tickets
updateTicket() PATCH /api/v1/tickets/{id}
deleteTicket() DELETE /api/v1/tickets/{id}
listMessages() GET /api/v1/tickets/{id}/messages
getMessage() GET /api/v1/tickets/{id}/messages/{messageId}
createMessage() POST /api/v1/tickets/{id}/messages
getChannelsReport() POST /api/v1/reports/channels
getPerformanceReport() POST /api/v1/reports/performance
getWebhook() GET /api/v1/webhook
updateWebhook() POST /api/v1/webhook
getWebhookLogs() GET /api/v1/webhook/logs

License

MIT