The PHP client covers the endpoints meant for servers. It requires PHP 8.1 and no dependency: curl is used when available, PHP streams otherwise. The WordPress plugin is built on it.
Installation
Download the ZIP and unzip it in your project, for
instance under packages/rikochey-php.
Without Composer, load its autoloader:
require __DIR__.'/packages/rikochey-php/autoload.php';
With Composer, declare the folder as a path repository:
{
"repositories": [{ "type": "path", "url": "packages/rikochey-php" }],
"require": { "rikochey/rikochey-php": "^1.0" }
}
Create a client
use Rikochey\Client;
$client = new Client('42|pQ7d…', 'https://api.example.com');
The first argument is the organization's API key — see
Obtain and manage a key. The second is the platform
address, without /api. Keep the key in your server's configuration, never in a page.
The client creates that key in their panel, where it is shown once, and sends it to you. The library never asks for a password, because the API has no password authentication at all: a key opens that organization's API and nothing else, and it is revoked in one click without disturbing the account.
A dumped client keeps the key to itself, so it does not leak into a log:
print_r($client); // token => ***
Need a second key — one per integration, so revoking one never takes down the others? Mint
it with the one you already hold, through tokens() below.
Websites and forms
foreach ($client->websites()->all() as $website) {
echo $website->name.' — '.$website->host;
}
$website = $client->websites()->get('019f0000-0000-7000-8000-00000000000a');
$inVienne = $client->websites()->inDepartment('86'); // empty array when none
$forms = $client->websites()->forms($website->id);
echo $forms->scriptTag(); // <script src="…" defer></script>
echo $forms->find('contact')?->embedTag(); // <div data-rikochey-form="contact"></div>
foreach ($forms as $form) {
echo $form->name.': '.count($form->fields).' fields';
}
Responses are read-only objects: Website, Department, WebsiteForms, Form,
FormField, Token. Dates are DateTimeImmutable.
Languages
Form::$locale is the form's source language, Form::$locales the languages it offers,
source first. Form::$name and the FormField texts — label, placeholder,
helpText — are in the source language: the script translates them on the page.
Without a language, the tag follows the lang attribute of the page's <html>. Pass one
to force it:
use Rikochey\Data\Form;
echo Form::tag('contact', [], 'en');
// <div data-rikochey-form="contact" data-rikochey-lang="en"></div>
echo $forms->find('contact')?->embedTag(['my-form'], 'en');
en-GB is accepted, and so is en_GB, written en-GB in the tag; a value that is not
a language code is left out. A language the form does not offer renders it in its source
language — see Languages.
Submit a request from a server
use Rikochey\Data\Captcha;
$submission = $client->submissions()->submit($website->id, 'contact', [
'name' => 'Smith',
'email' => '[email protected]',
'message' => 'Hello, I would like a quote.',
'page_url' => 'https://www.example.com/contact',
]);
echo $submission->message; // the client's confirmation message
echo $submission->leadId;
Add 'form_locale' => 'en' to the fields to get the messages and the confirmation in one
of the form's languages; without it, the website language applies, resolved among the
form's languages.
The token replaces the website signature and the origin check, not the captcha: when
the website's chain has links, pass the token solved on the page —
new Captcha('recaptcha_v3:12', $token) as the fourth argument. See
Submit a form.
Following a captcha fallback
When the API finds a chain link unreachable, it refuses the submission with a
captcha_fallback error that names the next link and signs a fallback token
authorizing it. Solve the captcha again at that link, then replay the submission with
both:
use Rikochey\Data\Captcha;
use Rikochey\Exception\AuthorizationException;
try {
$submission = $client->submissions()->submit($website->id, 'contact', $fields, $captcha);
} catch (AuthorizationException $exception) {
if ($exception->errorCode !== 'captcha_fallback') {
throw $exception;
}
$exception->nextCaptchaProvider(); // "altcha:7" — the link to solve next
$exception->captchaFallbackToken(); // "1789459320.4f3b…" — valid two minutes
$submission = $client->submissions()->submit($website->id, 'contact', $fields, Captcha::afterFallback(
$exception,
$tokenSolvedAtThatLink,
));
}
Captcha::afterFallback() is shorthand for
new Captcha($exception->nextCaptchaProvider(), $token, $exception->captchaFallbackToken()).
The client sends the link key and its token as headers, and the fallback token in the
captcha_fallback field of the body, which the API strips before recording the request.
Without the fallback token, a third-party link the API did not designate is refused with
captcha_provider_not_allowed. Follow one fallback per link, no more — see
The captcha.
Manage keys with a key
$created = $client->tokens()->create('CRM integration'); // $created->token, shown once
foreach ($client->tokens()->all() as $token) {
echo $token->name.' — last used '.($token->lastUsedAt?->format('Y-m-d') ?? 'never');
}
$client->tokens()->revoke($token->id);
$client->tokens()->revokeAll(); // including the token of this client
Errors
Every exception extends Rikochey\Exception\RikocheyException.
| Exception | When | Useful properties |
|---|---|---|
AuthenticationException |
401: key unknown, expired or revoked |
status |
AuthorizationException |
403: other organization, website disabled, captcha |
errorCode, nextCaptchaProvider(), captchaFallbackToken() |
NotFoundException |
404: unknown website, form or token |
status |
ValidationException |
422: invalid fields |
errors(), by field name |
RateLimitException |
429 — see Rate limiting |
retryAfter, in seconds |
ApiException |
Any other status — 413 payload_too_large among them — or a response that is not JSON |
status, errorCode, payload |
TransportException |
No response: DNS, TLS, timeout | — |
use Rikochey\Exception\ValidationException;
try {
$client->submissions()->submit($website->id, 'contact', $fields);
} catch (ValidationException $exception) {
foreach ($exception->errors() as $field => $messages) {
// show $messages next to $field
}
}
Your own HTTP client
Implement Rikochey\Http\Transport to send requests through your framework — proxy,
logging, retries — and pass it as the third argument:
use Rikochey\Http\Request;
use Rikochey\Http\Response;
use Rikochey\Http\Transport;
final class MyTransport implements Transport
{
public function send(Request $request): Response
{
// $request->method, $request->url, $request->headers, $request->body
return new Response($status, $headers, $body);
}
}
$client = new Client('42|pQ7d…', 'https://api.example.com', new MyTransport);
A transport returns every response, errors included: the client turns them into
exceptions. It throws TransportException only when no response was received.
API