PHP Wrapper for the Help Scout API

Overview

Help Scout API PHP Client

Build Status Maintainability Test Coverage

This is the official Help Scout PHP client. This client contains methods for easily interacting with the Help Scout Mailbox API.

SDK Version PHP Version Branch Documentation
3.* >= 7.3 master This page
2.* >= 7.1 v2 v2 branch

Table of Contents

Installation

The recommended way to install the client is by using Composer.

composer require helpscout/api "^3.0"

For Laravel projects, check out helpscout/api-laravel

Usage

You should always use Composer's autoloader in your application to autoload classes. All examples below assume you've already included this in your code:

require_once 'vendor/autoload.php';

Authentication

Use the factory to create a client. Once created, you can set the various credentials to make requests.

use HelpScout\Api\ApiClientFactory;

$client = ApiClientFactory::createClient();

// Set Client credentials if using that grant type.  Using this approach will lazily fetch an access token on-demand
// when needed.
$client->useClientCredentials($appId, $appSecret);

// Set Access token directly if you have it
$client->setAccessToken('abc123');

// Use a refresh token to get a new access token
$client->useRefreshToken($appId, $appSecret, $refreshToken);

Note

All credential types will trigger a pre-flight request to get an access token (HTTP 'POST' request). To avoid this, set the access token on the client before making a request using the setAccessToken method on the client.

$client = ApiClientFactory::createClient();
$client->setAccessToken('asdfasdf');

The access token will always be used if available, regardless of whether you have other credentials set or not.

Automatically Refreshing Expired Tokens

When a request fails due to an authentication error, the SDK can automatically try to obtain a new refresh token and then retry the given request automatically. To enable this, you can provide a callback when creating the ApiClient which can be used to persist the token for other processes to use depending on your needs:

$client = ApiClientFactory::createClient([], function (Authenticator $authenticator) {
    // This $authenticator contains the refreshed token
    echo 'New token: '.$authenticator->accessToken().PHP_EOL;
});

The callback can also be any class instance that implements HelpScout\Api\Http\Auth\HandlesTokenRefreshes:

use HelpScout\Api\Http\Auth\HandlesTokenRefreshes;
use HelpScout\Api\Http\Authenticator;

$callback = new class implements HandlesTokenRefreshes {
    public function whenTokenRefreshed(Authenticator $authenticator)
    {
        // @todo Persist the token
    }
};
$client = ApiClientFactory::createClient([], $callback);

Authorization Code Flow

Because the authorization code is only good for a single use, you'll need to exchange the code for an access token and refresh token prior to making additional api calls. You'll also need to persist the tokens for reuse later.

$client = ApiClientFactory::createClient();
$client = $client->swapAuthorizationCodeForReusableTokens(
    $appId,
    $appSecret,
    $authorizationCode
);

$credentials = $client->getAuthenticator()->getTokens();

echo $credentials['access_token'].PHP_EOL;
echo $credentials['refresh_token'].PHP_EOL;
echo $credentials['expires_in'].PHP_EOL;

In addition to providing the access/refresh tokens this will set the current auth to use those tokens, so you can freely make subsequent requests without reinitializing the client.

// uses the one-time authorization code for auth
$client = $client->swapAuthorizationCodeForReusableTokens(
    $appId,
    $appSecret,
    $authorizationCode
);

// uses access/refresh tokens for auth
$client->users()->list();

Examples

This README contains a lot of examples, but there are more examples for you to experiment with in ./examples.

Customers

Get a customer. Whenever getting a customer, all it's entities (email addresses, phone numbers, social profiles, etc.) come preloaded in the same request.

$customer = $client->customers()->get($customerId);

Get customers.

$customers = $client->customers()->list();

Get customers with a filter.

As described in the API docs the customer list can be filtered by a variety of fields. The CustomerFields class provides a simple interface to set filter values. For example:

use HelpScout\Api\Customers\CustomerFilters;

$filter = (new CustomerFilters())
    ->byFirstName('Tom')
    ->byLastName('Graham');

$customers = $client->customers()->list($filter);

Create a customer.

use HelpScout\Api\Customers\Customer;

$customer = new Customer();
$customer->setFirstName('Bob');
// ...

try {
    $customerId = $client->customers()->create($customer);
} catch (\HelpScout\Api\Exception\ValidationErrorException $e) {
    var_dump($e->getError()->getErrors());
}

Update a customer.

// ...
$customer->setFirstName('Bob');

$client->customers()->update($customer);

Email

Create a customer email.

use HelpScout\Api\Customers\Entry\Email;

$email = new Email();
$email->setValue('[email protected]');
$email->setType('work');
// ...

$client->customerEntry()->createEmail($customerId, $email);

Update a customer email.

// ...
$email->setType('home');

$client->customerEntry()->updateEmail($customerId, $email);

Delete a customer email.

$client->customerEntry()->deleteEmail($customerId, $emailId);

Address

Create a customer address.

use HelpScout\Api\Customers\Entry\Address;

$address = new Address();
$address->setCity('Boston');
// ...

$client->customerEntry()->createAddress($customerId, $address);

Update a customer address.

// ...
$address->setCity('Boston');

$client->customerEntry()->updateAddress($customerId, $address);

Delete a customer address.

$client->customerEntry()->deleteAddress($customerId);

Phone number

Create a customer phone.

use HelpScout\Api\Customers\Entry\Phone;

$phone = new Phone();
$phone->setValue('123456789');
$phone->setType('work');
// ...

$client->customerEntry()->createPhone($customerId, $phone);

Update a customer phone.

// ...
$phone->setType('home');

$client->customerEntry()->updatePhone($customerId, $phone);

Delete a customer phone.

$client->customerEntry()->deletePhone($customerId, $phoneId);

Chat Handles

Create a customer chat.

use HelpScout\Api\Customers\Entry\ChatHandle;

$chat = new ChatHandle();
$chat->setValue('1239134812348');
$chat->setType('icq');
// ...

$client->customerEntry()->createChat($customerId, $chat);

Update a customer chat.

// ...
$chat->setValue('1230148584583');

$client->customerEntry()->updateChat($customerId, $chat);

Delete a customer chat.

$client->customerEntry()->deleteChat($customerId, $chatId);

Social profile

Create a customer social profile.

use HelpScout\Api\Customers\Entry\SocialProfile;

$socialProfile = new SocialProfile();
$socialProfile->setValue('helpscout');
$socialProfile->setType('twitter');
// ...

$client->customerEntry()->createSocialProfile($customerId, $socialProfile);

Update a customer social profile.

// ...
$socialProfile->setType('facebook');

$client->customerEntry()->updateSocialProfile($customerId, $socialProfile);

Delete a customer social profile.

$client->customerEntry()->deleteSocialProfile($customerId, $socialProfileId);

Website

Create a customer website.

use HelpScout\Api\Customers\Entry\Website;

$website = new Website();
$website->setValue('https://www.helpscout.com');
// ...

$client->customerEntry()->createWebsite($customerId, $website);

Update a customer website.

// ...
$website->setValue('https://www.helpscout.net');

$client->customerEntry()->updateWebsite($customerId, $website);

Delete a customer website.

$client->customerEntry()->deleteWebsite($customerId, $websiteId);

Properties

Get a customer's properties and their values.

$customer = $client->customers()->get(418048101);
// ...

foreach ($customer->getProperties() as $property) {
    echo $property->getName().': '.$property->getValue().PHP_EOL;
}

Update a customer's properties.

use HelpScout\Api\Entity\Collection;
use HelpScout\Api\Entity\Patch;

$operations = new Collection(
    [
        new Patch('remove', 'property-1'),
        new Patch('replace', 'property-2', 'value'),
    ];
);
$client->customerEntry()->updateProperties($customerId, $operations);

Mailboxes

Get a mailbox.

$mailbox = $client->mailboxes()->get($mailboxId);

Get a mailbox with pre-loaded sub-entities.

A mailbox entity has two related sub-entities:

  • Fields
  • Folders

Each of these sub-entities can be pre-loaded when fetching a mailbox to remove the need for multiple method calls. The MailboxRequest class is used to describe which sub-entities should be pre-loaded. For example:

use HelpScout\Api\Mailboxes\MailboxRequest;

$request = (new MailboxRequest)
    ->withFields()
    ->withFolders();

$mailbox = $client->mailboxes()->get($mailboxId, $request);

$fields = $mailbox->getFields();
$folders = $mailbox->getFolders();

Get mailboxes.

$mailboxes = $client->mailboxes()->list();

Get mailboxes with pre-loaded sub-entities.

use HelpScout\Api\Mailboxes\MailboxRequest;

$request = (new MailboxRequest)
    ->withFields()
    ->withFolders();

$mailboxes = $client->mailboxes()->list($request);

Conversations

Get a conversation.

$conversation = $client->conversations()->get($conversationId);

You can easily eager load additional information/relationships for a conversation. For example:

use HelpScout\Api\Conversations\ConversationRequest;

$request = (new ConversationRequest)
    ->withMailbox()
    ->withPrimaryCustomer()
    ->withCreatedByCustomer()
    ->withCreatedByUser()
    ->withClosedBy()
    ->withThreads()
    ->withAssignee();

$conversation = $client->conversations()->get($conversationId, $request);

$mailbox = $conversation->getMailbox();
$primaryCustomer = $conversation->getCustomer();

Get conversations.

$conversations = $client->conversations()->list();

Get conversations with pre-loaded sub-entities.

use HelpScout\Api\Conversations\ConversationRequest;

$request = (new ConversationRequest)
    ->withMailbox()
    ->withPrimaryCustomer()
    ->withCreatedByCustomer()
    ->withCreatedByUser()
    ->withClosedBy()
    ->withThreads()
    ->withAssignee();

$conversations = $client->conversations()->list(null, $request);

Narrow down the list of Conversations based on a set of filters.

use HelpScout\Api\Conversations\ConversationFilters;

$filters = (new ConversationFilters())
    ->inMailbox(1)
    ->inFolder(13)
    ->inStatus('all')
    ->hasTag('testing')
    ->assignedTo(1771)
    ->modifiedSince(new DateTime('2017-05-06T09:04:23+05:00'))
    ->byNumber(42)
    ->sortField('createdAt')
    ->sortOrder('asc')
    ->withQuery('query')
    ->byCustomField(123, 'blue');

$conversations = $client->conversations()->list($filters);

You can even combine the filters with the pre-loaded sub-entities in one request

use HelpScout\Api\Conversations\ConversationRequest;
use HelpScout\Api\Conversations\ConversationFilters;

$request = (new ConversationRequest)
    ->withMailbox()
    ->withThreads();

$filters = (new ConversationFilters())
    ->inMailbox(1)
    ->inFolder(13)
    ->byCustomField(123, 'blue');

$conversations = $client->conversations()->list($filters, $request);

Update the custom fields on a conversation:

$customField = new CustomField();
$customField->setId(10524);
$customField->setValue(new DateTime('today'));
$client->conversations()->updateCustomFields($conversationId, [$customField]);

Create a new conversation, as if the customer sent an email to your mailbox.

// We can specify either the id or email for the Customer
$customer = new Customer();
$customer->addEmail('[email protected]');

$thread = new CustomerThread();
$thread->setCustomer($customer);
$thread->setText('Test');

$conversation = new Conversation();
$conversation->setSubject('Testing the PHP SDK v2: Phone Thread');
$conversation->setStatus('active');
$conversation->setType('email');
$conversation->setMailboxId(80261);
$conversation->setCustomer($customer);
$conversation->setThreads(new Collection([
    $thread,
]));

// You can optionally add tags
$tag = new Tag();
$tag->setName('testing');
$conversation->addTag($tag);

try {
    $conversationId = $client->conversations()->create($conversation);
} catch (ValidationErrorException $e) {
    var_dump($e->getError()->getErrors());
}

Here's some other example scenarios where you might create conversations:

Phone conversation, initiated by a Help Scout user
$user = $client->users()->get(31231);

$customer = new Customer();
$customer->setId(193338443);

$thread = new PhoneThread();
$thread->setCustomer($customer);
$thread->setCreatedByUser($user);
$thread->setText('Test');

$conversation = new Conversation();
$conversation->setSubject('Testing the PHP SDK v2: Phone Thread');
$conversation->setStatus('active');
$conversation->setType('phone');
$conversation->setMailboxId(80261);
$conversation->setCustomer($noteCustomer);
$conversation->setCreatedByUser($user);
$conversation->setThreads(new Collection([
    $thread,
]));
Chat conversation, initiated by the Customer
$noteCustomer = new Customer();
$noteCustomer->setId(163315601);
$thread = new ChatThread();
$thread->setCustomer($noteCustomer);
$thread->setText('Test');
$conversation = new Conversation();
$conversation->setSubject('Testing the PHP SDK v2: Chat Thread');
$conversation->setStatus('active');
$conversation->setType('chat');
$conversation->setAssignTo(271315);
$conversation->setMailboxId(138367);
$conversation->setCustomer($noteCustomer);
$conversation->setThreads(new Collection([
    $thread,
]));

// Also adding a tag to this conversation
$tag = new Tag();
$tag->setName('testing');
$conversation->addTag($tag);

$conversationId = $client->conversations()->create($conversation);

Delete a conversation:

$client->conversations()->delete($conversationId);

Update an existing conversation:

$client->conversations()->move($conversationId, 18);
$client->conversations()->updateSubject($conversationId, 'Need more help please');
$client->conversations()->updateCustomer($conversationId, 6854);
$client->conversations()->publishDraft($conversationId);
$client->conversations()->updateStatus($conversationId, 'closed');
$client->conversations()->assign($conversationId, 127);
$client->conversations()->unassign($conversationId);

Threads

Chat Threads

Create new Chat threads for a conversation.

use HelpScout\Api\Customers\Customer;
use HelpScout\Api\Conversations\Threads\ChatThread;

$thread = new ChatThread();
$customer = new Customer();
$customer->setId(163487350);

$thread->setCustomer($customer);
$thread->setText('Thanks for reaching out to us!');

$client->threads()->create($conversationId, $thread);

Customer Threads

Create new Customer threads for a conversation.

use HelpScout\Api\Customers\Customer;
use HelpScout\Api\Conversations\Threads\CustomerThread;

$thread = new CustomerThread();
$customer = new Customer();
$customer->setId(163487350);

$thread->setCustomer($customer);
$thread->setText('Please help me figure this out');

$client->threads()->create($conversationId, $thread);

Note Threads

Create new Note threads for a conversation.

use HelpScout\Api\Conversations\Threads\NoteThread;

$thread->setText('We are still looking into this');

$client->threads()->create($conversationId, $thread);

Phone Threads

Create new Phone threads for a conversation.

use HelpScout\Api\Customers\Customer;
use HelpScout\Api\Conversations\Threads\PhoneThread;

$thread = new PhoneThread();
$customer = new Customer();
$customer->setId(163487350);

$thread->setCustomer($customer);
$thread->setText('This customer called and spoke with us directly about the delay on their order');

$client->threads()->create($conversationId, $thread);

Reply Threads

Create new Reply threads for a conversation.

use HelpScout\Api\Customers\Customer;
use HelpScout\Api\Conversations\Threads\ReplyThread;

$thread = new ReplyThread();
$customer = new Customer();
$customer->setId(163487350);

$thread->setCustomer($customer);
$thread->setText("Thanks, we'll be with you shortly!");

$client->threads()->create($conversationId, $thread);

Get threads for a conversation.

$threads = $client->threads()->list($conversationId);

Attachments

Get an attachment.

$attachment = $client->attachments()->get($conversationId, $attachmentId);
$attachment->getData(); // attached file's contents

Create an attachment:

use HelpScout\Api\Conversations\Threads\Attachments\AttachmentFactory;
use HelpScout\Api\Support\Filesystem;

$attachmentFactory = new AttachmentFactory(new Filesystem());
$attachment = $attachmentFactory->make('path/to/profile.jpg');

$attachment->getMimeType(); // image/jpeg
$attachment->getFilename(); // profile.jpg
$attachment->getData(); // base64 encoded contents of the file

$client->attachments()->create($conversationId, $threadId, $attachment);

Delete an attachment:

$client->attachments()->delete($conversationId, $attachmentId);

Chats

Get a chat

$chat = $client->chats()->get($chatId);

List the chat events

$events = $client->chats()->events($chatId);

Tags

List the tags

$tags = $client->tags()->list();

Teams

List the teams

$teams = $client->teams()->list();

List the members of a team

$users = $client->teams()->members($teamId);

Users

Get a user.

$user = $client->users()->get($userId);

Get users.

$users = $client->users()->list();

Narrow down the list of Users based on a set of filters.

use HelpScout\Api\Users\UserFilters;

$filters = (new UserFilters())
    ->inMailbox(1)
    ->byEmail('[email protected]');

$users = $client->users()->list($filters);

Reports

When running reports using the SDK, refer to the developer docs for the exact endpoint, parameters, and response formats. While most of the endpoints in this SDK are little more than pass-through methods to call the API, there are a few conveniences.

First, for the start, end, previousStart, and previousEnd parameters, you may pass a formatted date-time string or any object implementing the \DateTimeInterface as the parameter. The client will automatically convert these objects to the proper format.

For those parameters that accept multiple values (mailboxes, tags, types, and folders), you may pass an array of values and let the client convert them to the proper format. You may also pass a single value (or a comma-separated list of values) if you like.

To run the report, use the runReport method available on the ApiClient instance. Pass the class path of the report class you'd like to use as the first argument and the array of report parameters as the second argument. Be sure the keys in the parameter array match the URL params specified in the docs. The client will convert the JSON response returned by the API into an array.

// Example of running the Company Overall Report
// https://developer.helpscout.com/mailbox-api/endpoints/reports/company/reports-company-overall/

use HelpScout\Api\Reports\Company;

$params = [
    // Date interval fields can be passed as an object implementing the \DateTimeInterface
    // or as a string in the 'Y-m-d\Th:m:s\Z' format. All times should be in UTC.
    'start' => new \DateTime('-7 days'),
    'end' => new \DateTimeImmutable(),
    'previousStart' => '2015-01-01T00:00:00Z',
    'previousEnd' => '2015-01-31T23:59:59Z',

    // Fields accepting multiple values can be passed as an array or a comma-separated string
    'mailboxes' => [123, 321],
    'tags' => '987,789',
    'types' => ['chat', 'email'],
    'folders' => [111, 222]
];

$report = $client->runReport(Company\Overall::class, $params);

Webhooks

Get a webhook.

$webhook = $client->webhooks()->get($webhookId);

List webhooks.

$webhooks = $client->webhooks()->list();

Create a webhook.

The default state for a newly-created webhook is enabled.

use HelpScout\Api\Webhooks\Webhook;

$data = [
    'url' => 'http://bad-url.com',
    'events' => ['convo.assigned', 'convo.moved'],
    'secret' => 'notARealSecret'
];
$webhook = new Webhook();
$webhook->hydrate($data);
// ...

$client->webhooks()->create($webhook);

Update a webhook

This operation replaces the entire webhook entity, so you must provide the secret again. Once updated, the webhook will be in the enabled state again.

$webhook->setUrl('http://bad-url.com/really_really_bad');
$webhook->setSecret('mZ9XbGHodY');
$client->webhooks()->update($webhook);

Delete a webhook.

$client->webhooks()->delete($webhookId);

Processing an incoming webhook

You can also use the SDK to easily process an incoming webhook. Signature validation will happen when creating the new object, so no need to check if it is valid or not. If the signatures do not match, the constructor of the IncomingWebhook object will throw an InvalidSignatureException to let you know something is wrong.

// Build it from globals
$incoming = IncomingWebhook::makeFromGlobals($secret);
// or build using a request object that satisfies the PSR-7 RequestInterface
/** @var RequestInterface $request */
$request = new Request(...);
$secret = 'superSekretKey';
$incoming = new IncomingWebhook($request, $secret);

Once you have the incoming webhook object, you can check the type of payload (customer, conversation, or test) as well as retrieve the data (see example). If a customer or conversation, you can retrieve the model associated. Otherwise, you can get the payload as either an associative array or standard class object.

Workflows

Fetch a paginated list of all workflows.

$workflows = $client->workflows()->list();

Run a manual workflow on a list of conversations.

$convos = [
    123,
    321
];
$client->workflows()->runWorkflow($id, $convos);

Change a workflow status to either "active" or "inactive"

$client->workflows()->updateStatus($id, 'active');

Error handling

Any exception thrown by the client directly will implement HelpScout\Api\Exception and HTTP errors will result in Http\Client\Exception\RequestException being thrown.

If an OAuth2 token is not provided or invalid then a HelpScout\Api\Exception\AuthenticationException is thrown.

Validation

You'll encounter a ValidationErrorException if there are any validation errors with the request you submitted to the API. Here's a quick example on how to use that exception:

try {
    // do something
} catch (\HelpScout\Api\Exception\ValidationErrorException $e) {
    $error = $e->getError();

    var_dump(
        // A reference id for that request.  Including this anytime you contact Help Scout will
        // empower us to dig right to the heart of the issue
        $error->getCorrelationId(),

        // Details about the invalid fields in the request
        $error->getErrors()
    );
    exit;
}

Pagination

When fetching a collection of entities the client will return an instance of HelpScout\Api\Entity\Collection. If the end point supports pagination then it will return an instance of HelpScout\Api\Entity\PagedCollection.

/** @var PagedCollection $users */
$users = $client->users()->list();

// Iterate over the first page of results
foreach ($users as $user) {
    echo $users->getFirstName();
}

// The current page number
$users->getPageNumber();

// The total number of pages
$users->getTotalPageCount();

// Load the next page
$nextUsers = $users->getNextPage();

// Load the previous page
$previousUsers = $users->getPreviousPage();

// Load the first page
$firstUsers = $users->getFirstPage();

// Load the last page
$lastUsers = $users->getLastPage();

// Load a specific page
$otherUsers = $users->getPage(12);

// Paged results are accessible as normal arrays, so you can simply iterate over them
foreach ($otherUsers as $user) {
    echo $user->getFirstName();
}

Testing

The SDK comes with a handy mock method on the ApiClient class. To use this, pass in the name of the endpoint you want to mock. You'll get a \Mockery\MockInterface object back. Once you set the mock, any subsequent calls to that endpoint will return the mocked object.

// From within the tests/ApiClientTest.php file...
public function testMockReturnsProperMock()
{
    $client = ApiClientFactory::createClient();
    $mockedWorkflows = $client->mock('workflows');

    $this->assertInstanceOf(WorkflowsEndpoint::class, $mockedWorkflows);
    $this->assertInstanceOf(MockInterface::class, $mockedWorkflows);

    $this->assertSame(
        $mockedWorkflows,
        $client->workflows()
    );
}

Once you've mocked an endpoint, you may want to clear it later on. To do this, you can use the clearMock($endpoint) method on the ApiClient.

Getting Support

Please open an issue for any SDK related problems or feature requests. For any issues that may involve sensitive customer or account data, please reach out to us via email at [email protected].

Comments
  • PHP Fatal error:  Class 'Curl' not found in /HelpScout/ApiClient.php on line 37

    PHP Fatal error: Class 'Curl' not found in /HelpScout/ApiClient.php on line 37

    I'm probably doing something wrong but I'm struggling to get a simple example up and running. I downloaded the library, added an index.php in the src folder and pasted the following code from the readme into my PHP file. I keep getting this error:

    PHP Fatal error: Class 'Curl' not found in /HelpScout/ApiClient.php on line 37

    The code from the readme I used:

    include 'HelpScout/ApiClient.php';
    
    use HelpScout\ApiClient;
    
    $scout = ApiClient::getInstance();
    $scout->setKey('your-api-key-here'); // I have added my key here
    
    $report = $scout->getConversationsReport([
        'start' => '2014-01-01T00:00:00Z',
        'end' => '2014-12-31T23:59:59Z'
    ]);
    

    screen shot 2015-07-10 at 2 06 10 pm

    Even if I just use $scout = ApiClient::getInstance(); the same fatal error occurs.

    I can also make curl requests via command line fine. I'm using PHP 5.5.10 with Mamp Pro (curl is included).

    Can you please provide some guidance on what I'm doing wrong? I had no trouble using an earlier version of this library, but it was ~v1.3.

    opened by amdrew 15
  • Exception on getPage() - The link

    Exception on getPage() - The link "page" is not templated

    Hi,

    We're recently working with the API client and trying to get specific mailbox/convo pages. However, it seems the getPage() method always returns the RuntimeException The link "page" is not templated. Even on other endpoints like customers. The actual page number does exist btw.

    $this->client = ApiClientFactory::createClient();
    $this->client->useClientCredentials($this->platform->api_key, $this->platform->api_secret);
    
    $listConversations = $this->client->conversations()->list($filters, $convoRequest);
    
    $pagedConversations= $listConversations->getPage( 2 ); //throws the exception
    
    dd($pagedConversations);
    

    But also

    $customers = $this->client->customers()->list();
    
    $otherCustomers = $customers->getPage(5); //throws the exception
    
    dd($otherCustomers);
    

    For some reason the HalLink.php method isTemplated always(?) returns false.

    Could you guys look into this?

    opened by Sebbe909 14
  • More detailed authentication example

    More detailed authentication example

    The auth example seems like it's missing some newbie details.

    1. How do you get the tokens in the first place? ($client->getTokens()) is returning an array with all keys empty, so I'm stuck at the moment.
    2. Following up on this issue how do you know when a token is expired and needs to be refreshed?
    3. When do you need to set a redirect URL in your HS account?

    Thanks!

    opened by helgatheviking 13
  • Could patchConversation() be public? Options for deleting a thread?

    Could patchConversation() be public? Options for deleting a thread?

    Could patchConversation() be public or is there another way to patch/delete threads in a conversation?

    The flow for my project is that I receive a form submission from the marketplace that sells my product. It is auto-forwarded by gsuite to my HelpScout email. I'm receiving a webhook when this conversation is created and I'd like the replace the initial thread (which is a wall of text as it includes a website's "system status") with two threads... one Note with the "system status" and one with their question... plus some custom fields and a tag.

    So far I've been able to parse the hook correctly and create 2 additional threads, but ideally I'd like to delete the original and just leave my 2 sparkly clean threads in the conversation.

    https://developer.helpscout.com/mailbox-api/endpoints/conversations/threads/update/

    Suggests that you can patch a thread with a remove or replace operation. But in the PHP API I'm stuck since patchConversation() is a private method. I can't call RestClient->patchResource() directly either since I've yet to figure out how to access/return the restClient property.

    • PHP version: 7.4
    • SDK version: 2.0
    opened by helgatheviking 11
  • Invalid Json Response from deleteConversation

    Invalid Json Response from deleteConversation

    I'm getting an error when trying to use the deleteConversation function. Here's the dumped response:

    object(CurlResponse)#110 (2) { ["body"]=> array(2) { ["code"]=> int(400) ["error"]=> string(12) "Invalid Json" } ["headers"]=> array(8) { ["Http-Version"]=> string(3) "1.1" ["Status-Code"]=> string(3) "400" ["Status"]=> string(15) "400 Bad Request" ["Content-Type"]=> string(31) "application/json; charset=utf-8" ["Date"]=> string(29) "Fri, 08 Jan 2016 00:59:56 GMT" ["Server"]=> string(5) "nginx" ["Content-Length"]=> string(2) "35" ["Connection"]=> string(10) "keep-alive" } }

    Any suggestions as to what I might be doing wrong?

    opened by mstaples 11
  • 400 bad request while migrating conversation threads

    400 bad request while migrating conversation threads

    • PHP version: 7.2
    • SDK version: v2

    Current behavior I am trying to migrate the conversation threads however getting bad request error while creating a new conversation. I am trying to use getThreads() to get conversation threads and setThreads() to add threads data to new conversation. Here's my code:

    `// GET conversation with the threads $conversationRequest = (new ConversationRequest()) ->withThreads(); $conversationRequest = $conversationRequest->withThreads();

    $filters = (new ConversationFilters())
        ->withMailbox($from_mailboxid)
        ->withSortField('createdAt')
        ->withSortOrder('asc')
        ->withStatus('all');
    
    // $second_page = $from_conversations->getPage(2);
    $from_conversations = $from_client->conversations()->list($filters, $conversationRequest);
    
    foreach( $from_conversations as $key => $conv ) {
        /**
         * Create Conversation: Customer thread - as if the customer emailed in
         */
        $customer = $conv->getCustomer();
        $thread   = $conv->getThreads();
        $subject  = $conv->getSubject();
        $status   = $conv->getStatus();
        $type     = $conv->getType();
    
        $conversation = new Conversation();
        $conversation->setSubject( $subject );
        $conversation->setStatus( $status );
        $conversation->setType( $type );
        $conversation->setMailboxId( $to_mailboxid );
        $conversation->setCustomer( $customer );
        $conversation->setThreads( $thread );
    
        try {
            $conversationId = $to_client->conversations()->create($conversation);
        } catch (\HelpScout\Api\Exception\ValidationErrorException $e) {
            var_dump($e->getError()->getErrors());
        }`
    

    Expected behavior How can I migrate the threads creating a new conversation?

    Thank You.

    opened by abhishekrijal 9
  • How to fetch original source of thread that arrives via webhook

    How to fetch original source of thread that arrives via webhook

    • PHP version: 7.4
    • SDK version: 2

    I have a webhook set up for new conversations. I have to parse the incoming email and then update the conversation accordingly.

    Not sure how I overlooked this before but Helpscout help advised me today that they add a prefix to all html IDs

    The conversation object as passed in the webhook reflects parsing that we do in-app—it's not the raw source of the email as received in your external mailbox. One of the things that that includes is appending the ex- prefix to objects brought in from outside of Help Scout.

    So for example <div id="something"> becomes <div id="ex-something"> and that is messing up my domDocument parsing.

    I was also told you can get the original source html via an API endpoint: https://developer.helpscout.com/mailbox-api/endpoints/conversations/threads/thread-source-rfc822/

    But I would like to know if it's possible to get that somehow using the API wrapper.

    Once the webhook comes in, I can get the conversation from it

    $conversation    = $webhook->getConversation();
    $conversation_id = $conversation->getId();
    

    But pretty sure this doesn't include the threads... which is why I was getting the threads from the embedded property of the webhook's json payload.

    Not quite sure where to go from here, so would appreciate any help!

    opened by helgatheviking 8
  • Gets uncaught TypeError when no folder in the mailbox

    Gets uncaught TypeError when no folder in the mailbox

    Will get uncaught TypeError when no folder in the mailbox

    $mailbox_id = 'xxxx'; $mailboxRequest = (new MailboxRequest())->withFolders()->withFields(); $mailbox = $client->mailboxes()->get($mailbox_id, $mailboxRequest);

    //===========

    PHP Warning: array_map(): Argument #2 should be an array in \vendor\helpscout\api\src\Http\Hal\HalDeserializer.php on line 62 PHP Fatal error: Uncaught TypeError: Argument 1 passed to HelpScout\Api\Http\Hal\HalPagedResources::__construct() must be of the type array, null given, called in \vendor\helpscout\api\src\Http\Hal\HalDeserializer.php on line 73 and defined in \vendor\helpscout\api\src\Http\Hal\HalPagedResources.php:19 Stack trace: #0 \vendor\helpscout\api\src\Http\Hal\HalDeserializer.php(73): HelpScout\Api\Http\Hal\HalPagedResources->__construct(NULL, Object(HelpScout\Api\Http\Hal\HalLinks), Object(HelpScout\Api\Http\Hal\HalPageMetadata)) #1 \vendor\helpscout\api\src\Http\RestClient.php(159): HelpScout\Api\Http\Hal\HalDeserializer::deserializeResources('HelpScout\Api\M...', 'fields', Object(HelpScout\Api\Http\Hal\HalDocument)) #2 \vendor\helpscout\api\src\Entity\LinkedEntityLoader.php(69): Help in \vendor\helpscout\api\src\Http\Hal\HalPagedResources.php on line 19

    opened by wrmond 8
  • Validation errors when creating customers

    Validation errors when creating customers

    For some reason creating customers with an Email or Phone object will throw a validation error. We even tried doing this with the test code from the examples in this PHP API.

    Here's a sample:

    `require dirname(__FILE__) . '/config/helpscout.php';
    $client = ApiClientFactory::createClient();
    $client->useClientCredentials($appId, $appSecret);			
    
    $filters = (new CustomerFilters())->withQuery('email:"' . $data['email'] . '"');
    $customers = $client->customers()->list($filters);
    
    if(count($customers) == 0) {
    	// Create Customer
    	$customer = new Customer();
    	$customer->setFirstName($data['firstName']);
    	$customer->setLastName($data['lastName']);
    
    	if(!empty($data['email'])) {
    		$customer->addEmail(new Email([
    		    'email' => "[email protected]",
    		    'type' => 'work',
    		]));
    	}
    
    	$test = $client->customers()->create($customer);
    	var_dump($test);
    }`
    

    As long as we don't add the Email object via addEmail, the call will succeed. If we use this same method to add a phone, it will also throw a validation error. We have tested this with other email addresses, phone numbers, etc with multiple formats and get the same validation error every time. Seems to be returning a 400 error from the API under the hood.

    The $data object is populated with a sample name and email address.

    This is the error that is produced when no customer is returned and the code tries to create a new customer. As long as we do not try to add and email or phone, the above code works. As soon as we try to add an email or phone, it fails.

    Note that the path has been sanitized in the error message for security reasons. The actual path is not /path/to/code obviously.

    <b>Fatal error</b>: Uncaught HelpScout\Api\Exception\ValidationErrorException: Validation error in /path/to/code/includes/helpscout/vendor/helpscout/api/src/Http/Handlers/ValidationHandler.php:23 Stack trace: #0 /path/to/code/includes/helpscout/vendor/guzzlehttp/promises/src/Promise.php(203): HelpScout\Api\Http\Handlers\ValidationHandler-&gt;HelpScout\Api\Http\Handlers\{closure}(Object(GuzzleHttp\Psr7\Response)) #1 /path/to/code/includes/helpscout/vendor/guzzlehttp/promises/src/Promise.php(156): GuzzleHttp\Promise\Promise::callHandler(1, Object(GuzzleHttp\Psr7\Response), Array) #2 /path/to/code/includes/helpscout/vendor/guzzlehttp/promises/src/TaskQueue.php(47): GuzzleHttp\Promise\Promise::GuzzleHttp\Promise\{closure}() #3 /path/to/code/includes/helpscout/vendor/guzzlehttp/promises/src/Promise.php(246): GuzzleHttp\Promise\TaskQueue-&gt;run(true) #4 /home/wpboost/www/wp-content/themes/wpb in <b>/path/to/code/includes/helpscout/vendor/helpscout/api/src/Http/Handlers/ValidationHandler.php</b> on line <b>23</b><br />

    opened by lancedockins 7
  • Automatically attempt to refresh token on 400 response

    Automatically attempt to refresh token on 400 response

    Targeting v2...

    An API call where the token has expired should attempt a refresh, then try the request again.

    Without making the Authenticator class too clumsy... might make sense to move that class to be a middleware/request handler and then the refresh check/fetch could be a response handler. Would be a fair bit of work

    enhancement 
    opened by davidstanley01 7
  • Two bugs in param names

    Two bugs in param names

    • In the API there is no type 'unassigned' anymore, only 'open'.
    • In the API in conversations the param modifiedAt is renamed to userModifiedAt

    But are also missing from the API documentation, but found these changes in the live API while debugging.

    opened by casperbakker 7
  • Question: How to get a Customer based on a Property

    Question: How to get a Customer based on a Property

    I added a location (Accra) property to customer and want to list the customers base on this property

    $filter = (new CustomerFilters())
            ->withQuery('property:"Accra');
    
    $customers = $client->customers()->list($filter);
    

    This returns an empty result. is it possible to query customers based on a Property?

    opened by PeterOcansey 0
  • Invalid Refresh Token issue

    Invalid Refresh Token issue

    Thank you for taking the time to submit an issue with all the details shown below. Our engineering team monitors issues submitted and strives to respond with 1-2 business days.

    • PHP version: 7.3
    • SDK version: ^3.0

    Current behavior

    We've begun getting invalid token for the refresh token on our API integration with HelpScout.

    The error message specifically is: Client error: POST https://api.helpscout.net/v2/oauth2/token resulted in a 400 Bad Request response: {"error_description":"Invalid refresh_token","error":"invalid_request"}

    Everything was working fine, and the app credentials are still valid.

    When I tried to replay the error in Rollbar, I got this: Helpscout helpdesk error: Signature mismatch: Expected signature is xmrOl2Rwf66y6NQrdld/JFGMdzw=. tdysaqdApzeCJPo/kaGMFegn2Go= was provided.

    We're using the HelpScout PHP package ("helpscout/api": "^3.0") to interact with your API.

    This is what the response & error message says:

    Client error: `POST https://api.helpscout.net/v2/oauth2/token` resulted in a `400 Bad Request` response:
    {"error_description":"Invalid refresh_token","error":"invalid_request"}
    

    We don't log the full error occurrence, just the error message. This seems to be happening for incoming webhook, which is weird, as we should only be using the API for outgoing calls.

    We created an App, which helps auto-translate HelpScout conversations in foreign languages. The app can be found here. This then creates a Webhook on the customer's profile that can be found here.

    We use the Refresh Token authentication in the HelpScout guide, and this can be found here:

        public function getClient($helpdesk)
        {
            $this->config = config('helpdesk.helpscout');
            $appId = $this->config['appId'];
            $appSecret = $this->config['appSecret'];
    
            $this->client = ApiClientFactory::createClient([], function (Authenticator $authenticator) use ($helpdesk) {
                $helpdesk->access_token = json_encode($authenticator->getTokens());
                $helpdesk->save();
            });
    
            $this->client->useRefreshToken($appId, $appSecret, json_decode($helpdesk->access_token)->refresh_token);
    
            return $helpdesk;
        }
    

    (it's always worked, not sure what has changed recently).

    Expected behavior

    Refresh token works automatically without any issues.

    Steps to reproduce

    1. Send incoming web hook to Website
    2. Incoming web hook authenticates request, and app.
    opened by tymolls 2
  • Support hydrating embedded threads.

    Support hydrating embedded threads.

    This pulls the code from https://github.com/helpscout/helpscout-api-php/pull/208

    It keeps just the hydrating embedded threads part of that pull request while removing the part that added withEmbed to conversation filters.

    I'm pulling this back up because when getting webhooks with the IncomingWebhook class the threads are all embedded in the request, but it is impossible to access them since they are never hydrated.

    The only thing that might be missing here is getting the correct value for ->getThreadCount().

    Currently the webhooks I'm getting are setting threads to 1 which in this code:

    if (is_numeric($data['threads'])) {
         $this->setThreadCount($data['threads']);
    }
    

    Sets it to that. I'm not sure if embedded threads should do anything to change that without knowing how the underlying system works so I'll leave that up to you if that needs to be modified more. This change doesn't change that at all from how it currently works anyway. And this change allows me to use ->getThreads() which is the goal of what I'm trying to do with this.

    opened by matt-h 0
  • How to get Conversation ID?

    How to get Conversation ID?

    This is not an issue, but I need help.

    After calling:

    $conversations = $client->conversations()->list($filters); echo json_encode($conversations);

    All I get was:

    {"0":{}}

    How do I get the conversation ID?

    opened by yaphawhann 3
  • RestClient::encodeEntity() should allow for errors

    RestClient::encodeEntity() should allow for errors

    The return type for this method is declared as string but in case of an error json_encode() will return false. The return type should be updated at the very least, or an exception thrown with contents of json_last_error_msg() included.

    I came across this error recently, though I have no record of the arguments involved or why the Conversation object I passed couldn't be encoded.

    [2021-06-18 20:11:56] production.ERROR: HelpScout\Api\Http\RestClient::encodeEntity(): Return value must be of type string, bool returned {"exception":"[object] (TypeError(code: 0): HelpScout\\Api\\Http\\RestClient::encodeEntity(): Return value must be of type string, bool returned at /xxxx/vendor/helpscout/api/src/Http/RestClient.php:161)
    [stacktrace]
    #0 /xxxx/vendor/helpscout/api/src/Http/RestClient.php(68): HelpScout\\Api\\Http\\RestClient->encodeEntity()
    #1 /xxxx/vendor/helpscout/api/src/Conversations/ConversationsEndpoint.php(35): HelpScout\\Api\\Http\\RestClient->createResource()
    #2 /xxxx/app/Helpscout.php(209): HelpScout\\Api\\Conversations\\ConversationsEndpoint->create()
    
    opened by miken32 4
Releases(3.6.3)
  • 3.6.3(Feb 18, 2022)

    What's Changed

    • Add missing 'text' property to Conversation Custom Fields by @pchemali in https://github.com/helpscout/helpscout-api-php/pull/286

    New Contributors

    • @pchemali made their first contribution in https://github.com/helpscout/helpscout-api-php/pull/286

    Full Changelog: https://github.com/helpscout/helpscout-api-php/compare/3.6.2...3.6.3

    Source code(tar.gz)
    Source code(zip)
  • 3.6.2(Aug 12, 2021)

  • 3.6.1(Jul 15, 2021)

  • 3.6.0(Jul 15, 2021)

  • 3.5.2(Jun 29, 2021)

    • PR #278 now throws a JsonException if an outbound request to Help Scout's API is unable to generate valid json. This is not a breaking change because previously this invalid state would error out anyways.
    Source code(tar.gz)
    Source code(zip)
  • 3.5.1(Jun 2, 2021)

  • 3.5.0(Apr 23, 2021)

  • 3.4.0(Apr 8, 2021)

    • PR https://github.com/helpscout/helpscout-api-php/pull/266 brings the ability to access customer property values for a Customer - thanks @andryska
    Source code(tar.gz)
    Source code(zip)
  • 3.3.2(Jan 27, 2021)

  • 3.3.1(Jan 26, 2021)

    • PR https://github.com/helpscout/helpscout-api-php/pull/261 Now gracefully handling scenarios where failures were encountered when hydrating a Conversation where the User who created the convo had been deleted
    Source code(tar.gz)
    Source code(zip)
  • 3.3.0(Dec 8, 2020)

    • PR https://github.com/helpscout/helpscout-api-php/pull/259 adds a few more sorting options for conversations that are supported by the API - thanks @amybowers!
    Source code(tar.gz)
    Source code(zip)
  • 3.2.1(Sep 23, 2020)

  • 3.2.0(Jul 2, 2020)

  • 2.7.0(Jul 2, 2020)

  • 3.1.0(Jun 30, 2020)

  • 2.6.0(Jun 30, 2020)

  • 3.0.1(May 7, 2020)

    This release includes:

    • Support for a new customer.updated web hook via PR https://github.com/helpscout/helpscout-api-php/pull/231
    • Readme updates
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0(Jan 7, 2020)

    With this release we're targeting improving the developer experience to make the SDK easier to work with. There's no new API interaction being added.

    • [Breaking Change] PHP 7.1 has reached end of life and 7.2 is receiving security updates only, so v3 now requires PHP 7.3 (see #216). PHPDoc tags have been removed in favor of strongly typed parameters. Any classes that extend the SDK may require updating.
    • [Breaking Change] All filter method names have changed and will need to be updated (see PRs #224, #225 and #226). Filter object's method names were causing some confusion. The code snippet (new ConversationFilter())->withMailbox(40323) can give the impression that we're eager loading mailbox 40323 alongside the conversations, but it'll actually list the conversations in that mailbox. To make this more clear, we've eliminated the with*() verbiage for filters in favor of more specific verbiage like inMailbox(2), modifiedSince(Carbon::now()->subDay()) and hasTag('trial-customer'). Refactored filter objects to use more clear terminology.
    • [Breaking Change] An HelpScout\Api\Exception\AuthenticationException is now thrown when the API responds with a 401. Previously this threw a GuzzleHttp\Exception\RequestException, so if you are catching that exception to refresh your token, you'll need to update your code base. (see #212)
    • [Breaking Change] HelpScout\Api\Customers\Entry\Chat has been removed completely in place of HelpScout\Api\Customers\Entry\ChatHandle. This was previously deprecated in v2. (see #229, #100, #102)
    • New isAssigned method has been added to HelpScout\Api\Conversations\Threads\Thread.php to help reduct additional logic when determining if a given Thread is assigned.(See #230)
    Source code(tar.gz)
    Source code(zip)
  • 2.5.0(Nov 22, 2019)

    • PR https://github.com/helpscout/helpscout-api-php/pull/207 removes unnecessary docblocks where type hints or return values are sufficient. This PR makes no functional changes to the SDK.
    • PR https://github.com/helpscout/helpscout-api-php/pull/206 fulfills https://github.com/helpscout/helpscout-api-php/issues/205 by adding the ability to list users with access to a given mailbox.
    Source code(tar.gz)
    Source code(zip)
  • 1.9.6(Nov 13, 2019)

    Additional headers can be specified statically using:

    ApiClient::setAdditionalHeaders([
        'foo' => 'bar',
    ]);
    

    All subsequent requests will have those headers included.

    Source code(tar.gz)
    Source code(zip)
  • 2.4.1(Oct 29, 2019)

    • PR https://github.com/helpscout/helpscout-api-php/pull/200 fulfills https://github.com/helpscout/helpscout-api-php/issues/197 by adding some additional returns for chained method calls when building objects.
    Source code(tar.gz)
    Source code(zip)
  • 2.4.0(Aug 23, 2019)

    • PR https://github.com/helpscout/helpscout-api-php/pull/191 fulfills https://github.com/helpscout/helpscout-api-php/issues/174 by adding the ability to list teams and their members
    • PR https://github.com/helpscout/helpscout-api-php/pull/192 fulfills https://github.com/helpscout/helpscout-api-php/issues/183 by adding the ability to specify a status when creating Note or Reply threads
    • PR https://github.com/helpscout/helpscout-api-php/pull/193 fulfills https://github.com/helpscout/helpscout-api-php/issues/179 by hydrating the first phone number for a Customer. We were already sending any other Customer fields alongside the Conversation creation.
    Source code(tar.gz)
    Source code(zip)
  • 2.3.10(Aug 16, 2019)

    • PR https://github.com/helpscout/helpscout-api-php/pull/189 resolves a regression that caused php exceptions when embedded entities were present but empty
    Source code(tar.gz)
    Source code(zip)
  • 2.3.9(Aug 8, 2019)

    • PR https://github.com/helpscout/helpscout-api-php/pull/185 resolves an issue with the date format used when filtering Customers or Conversations
    Source code(tar.gz)
    Source code(zip)
  • 2.3.8(Jul 30, 2019)

    • PR https://github.com/helpscout/helpscout-api-php/pull/180 resolves https://github.com/helpscout/helpscout-api-php/issues/178 by preventing the hydration of empty objects when empty embedded collections were provided by the API
    • PR https://github.com/helpscout/helpscout-api-php/pull/181 adds getCorrelationId() as an alias for getLogRef() so it's more clear how to provide the ID when contacting Help Scout
    • PR https://github.com/helpscout/helpscout-api-php/pull/182 offers more clear direction on which scenarios can throw validation exceptions as well as more clear communication on how to obtain specifics on validation failures
    Source code(tar.gz)
    Source code(zip)
  • 2.3.7(Jul 17, 2019)

    Thanks to @matt-allan for submitting issue #175 and creating PR #176 to resolve it. There was an issue where the API response format changed and we were improperly assuming that HalDocument _embedded properties were only arrays and this was causing issues.

    Source code(tar.gz)
    Source code(zip)
  • 2.3.6(Jul 8, 2019)

    • Thanks to @dodedodo33 who submitted PR https://github.com/helpscout/helpscout-api-php/pull/173 resolving https://github.com/helpscout/helpscout-api-php/issues/172 where there was an issue with the request body that was being sent to the Help Scout API preventing users from using the SDK from running webhooks.
    Source code(tar.gz)
    Source code(zip)
  • 2.3.5(Jun 17, 2019)

    • PR https://github.com/helpscout/helpscout-api-php/pull/171 expands the available shorthand types for Customers. You can now use these methods with strings and the SDK will build the underlying objects:
      • $customer->addChatHandle('helpscout', 'aim')
      • $customer->addSocialProfile('helpscout', 'twitter')
      • $customer->addPhone('14235554837')
      • $customer->addWebsite('https://helpscout.com')
    Source code(tar.gz)
    Source code(zip)
  • 2.3.4(Jun 10, 2019)

    • PR https://github.com/helpscout/helpscout-api-php/pull/167 resolves https://github.com/helpscout/helpscout-api-php/issues/163 - user is now correctly populated with the expected id of the user when creating a new Conversation
    • PR https://github.com/helpscout/helpscout-api-php/pull/165 adds new shorthand syntax for adding an Email to a Customer - $customer->addEmail($emailAddress, 'work')
    Source code(tar.gz)
    Source code(zip)
  • 2.3.3(Jun 7, 2019)

    • Thanks again to @wking-io for PR https://github.com/helpscout/helpscout-api-php/pull/162 which resolves https://github.com/helpscout/helpscout-api-php/issues/161 where in a certain scenario $customer->getEmails() was incorrectly returning emails as a collection of strings rather than Emails
    Source code(tar.gz)
    Source code(zip)
Owner
Help Scout
Help Scout
A composer package designed to help you create a JSON:API in Phalcon

phalcon-json-api-package A composer package designed to help you create a JSON:API in Phalcon What happens when a PHP developer wants to create an API

Jim 36 Oct 7, 2022
It's basically a dynamic web browser extension that can display notifications with the help of an admin-controlled dynamic API.

D-NOTIFIER A self controlled dynamic API based web browser extension built by Sahil Kumar How It Works? It's basically a dynamic web browser extension

Sahil Kumar 1 Jan 6, 2022
Dedicated plugin for PocketMine-API 4, this will help staff members to make players follow the rules to the letter

StaffMode Dedicated plugin for PocketMine-API 4, this will help staff members to make players follow the rules to the letter Annotation This plugin is

Kurth 3 Aug 12, 2022
Opensource php wrapper to WhatsApp Cloud API.

WhatsApp Latest Cloud API Wrapper for PHP Opensource php wrapper to WhatsApp Cloud API. Features supported Sending messages Sending Media (images, aud

Novath Thomas 58 Nov 30, 2022
A simple Object Oriented wrapper for Linear API, written with PHP.

PHP Linear API A simple Object Oriented wrapper for Linear API, written with PHP. NOTE You should take a look Linear GraphQL API Schema for all nodes

Mustafa KÜÇÜK 6 Sep 2, 2022
A simple API with Guzzle wrapper, providing easy access to wppconnect's endpoints.

WPPConnect Team Wppconnect Laravel Client A simple API with Guzzle wrapper, providing easy access to wppconnect's endpoints. Requirements PHP 7.4 or n

null 28 Dec 18, 2022
Standardized wrapper for popular currency rate APIs. Currently supports FixerIO, CurrencyLayer, Open Exchange Rates and Exchange Rates API.

?? Wrapper for popular Currency Exchange Rate APIs A PHP API Wrapper to offer a unified programming interface for popular Currency Rate APIs. Dont wor

Alexander Graf 24 Nov 21, 2022
An un-offical API wrapper for logsnag.com to get notifications and track your project events

An un-offical API wrapper for logsnag.com to get notifications and track your project events

David Oti 3 Oct 15, 2022
PHP functions that help you validate structure of complex nested PHP arrays.

PHP functions that help you validate structure of complex nested PHP arrays.

cd rubin 7 May 22, 2022
Bug bounty tools built in PHP to help penetration tester doing the job

BugBountyTools-PHP Bug bounty tools built in PHP to help penetration tester doing the job Website who using this script: KitaBantu Soon! 403 Bypasser

Muhammad Daffa 7 Aug 17, 2022
A small library to help run PHP servers easily and quickly.

PHP Server A small library to help run PHP servers easily and quickly. Installation composer require ahmard/php-server Usage PHP Built-In Server An i

Ahmad Mustapha 9 Dec 31, 2022
It allows frontend developer to send ajax requests and return a custom information from the server without a php developer help

[Magento 1 Extension] It allows frontend developer to send ajax requests and return a custom information from the server without a php developer help (without any php code).

Vladimir Fishchenko 62 Apr 1, 2022
Lightweight PHP wrapper for OVH APIs. That's the easiest way to use OVH.com APIs in your PHP applications.

This PHP package is a lightweight wrapper for OVH APIs. That's the easiest way to use OVH.com APIs in your PHP applications.

OVHcloud 263 Dec 14, 2022
RMT is a handy tool to help releasing new version of your software

RMT - Release Management Tool RMT is a handy tool to help releasing new versions of your software. You can define the type of version generator you wa

Liip 442 Dec 8, 2022
This tool can help you to see the real IP behind CloudFlare protected websites.

CrimeFlare Bypass Hostname Alat untuk melihat IP asli dibalik website yang telah dilindungi CloudFlare. Introduction Alat ini berfungsi untuk melakuka

zidan rahmandani 126 Oct 20, 2021
A Pocketmine-MP (PMMP) plugin to help staff members enforce the rules of the server.

StaffMode is an all-in-one Pocketmine-MP (PMMP) moderation plugin made to simplify the life of staff members.

ItsMax123 9 Sep 17, 2022
A class to help convert bytes into other units (kb, mb, etc).

A class to help convert bytes into other units (kb, mb, etc). This package can be used to convert int|float values from bytes to KB, MB and GB as well

Ryan Chandler 12 Mar 15, 2022
Allow players to see how well they are doing while pvping with the help of a combo counter

Combo-Counter Allow players to see how well they are doing while pvping with the help of a combo counter Settngs / Config #set to false if you dont wa

null 3 Jun 1, 2022
A library of powerful code snippets to help you get the job done with Gravity Forms and Gravity Perks.

Gravity Wiz Snippet Library Gravity Wiz is creating the most comprehensive library of snippets for Gravity Forms ever. We'll be consistently moving ou

Gravity Wiz 151 Dec 27, 2022