Secure Exchanges SDK for PHP - Secure messaging, file exchange, and electronic signatures
Requires
- php: >=8.2
- ext-curl: *
- ext-json: *
- ext-mbstring: *
- ext-openssl: *
- chrome-php/chrome: ^1.0
- giggsey/libphonenumber-for-php: ^8.13
- paragonie/pqcrypto_compat: 0.3.2
- phpseclib/phpseclib: ^3.0
- setasign/fpdf: ^1.8
- setasign/fpdi: ^2.6
- smalot/pdfparser: ^2.0
Requires (Dev)
- phpstan/phpstan: ^1.0
- phpunit/phpunit: ^10.0
- squizlabs/php_codesniffer: ^3.0
Suggests
- ext-opcache: Significantly speeds up the pure-PHP post-quantum fallback when ext-pqcrypto is not available (enable JIT).
- ext-pqcrypto: Native C implementation of ML-KEM/ML-DSA (github.com/paragonie/ext-pqcrypto). Strongly recommended in production: faster and side-channel resistant. Compat uses it automatically when loaded.
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-08-27 19:43:55 UTC
README
English | Français
PHP SDK for the Secure Exchanges API: end-to-end encrypted messaging, secure file transfer, electronic signature, and PKI certification of PDF documents.
With this SDK, from your PHP application you can:
- send end-to-end encrypted messages and files, with or without a password, with SMS-based 2FA verification;
- have PDF documents signed electronically (signature zones, initials, dates, text fields, and more);
- certify PDFs with a digital certificate (PKI) before sending them for signature;
- read and delete messages, and create secure reply envelopes;
- view activity logs (sends, opens, signatures).
Table of contents
- Security
- Installation
- Requirements
- Credentials
- Configuration
- SDK architecture
- Usage guide
- API reference
- Support
- License
Security
- End-to-end encryption: the content (message body, files) is encrypted inside your application before it leaves your server, using keys generated locally and never transmitted to the Secure Exchanges server: the server only ever sees encrypted data.
- Post-quantum key exchange: ML-KEM-1024 (FIPS 203), resistant to quantum-computer attacks, signed with ML-DSA-87 (FIPS 204).
- AES-256 encryption of the content and of all exchanges with the server.
- Interception protection: any man-in-the-middle (MITM) attack attempt causes the connection to fail.
- Nothing to manage: the SDK automatically applies the entire protocol on every call: you never have to handle any cryptographic primitives yourself.
Installation
composer require secure-exchanges/sdk
Dependencies
Dependencies are managed by Composer (see composer.json):
paragonie/pqcrypto_compat(0.3.2) : post-quantum cryptography (ML-KEM / ML-DSA)phpseclib/phpseclib: cryptography (RSA for some auxiliary functions, utilities)setasign/fpdi,setasign/fpdf: PDF manipulationsmalot/pdfparser: PDF readingchrome-php/chrome: PKI certification (headless browser)giggsey/libphonenumber-for-php: phone number validation
Requirements
To use the SDK, you need:
- PHP 8.2 or newer (
composer installfails on PHP 8.1) - PHP extensions:
openssl,json,mbstring,curl ext-sockets: required for PKI signing (used bychrome-php)- Google Chrome or Chromium installed on the machine, required only for PDF certification (PKI); the binary path can be provided via the
SE_CHROME_PATHenvironment variable - A serial number (
serial) : the UUID of your license - An API user ID (
api_user) : the UUID of the API user - An API password (
api_password) : the UUID of the API password
Recommended in production: the native ext-pqcrypto extension (github.com/paragonie/ext-pqcrypto), significantly faster for post-quantum cryptography than the pure-PHP implementation installed by Composer. No code changes are needed: the SDK automatically switches to the extension as soon as it is loaded. Otherwise, enabling ext-opcache (with JIT) noticeably speeds up the pure-PHP implementation.
Credentials
Where to find your credentials
The 3 required credentials (serial, api_user, api_password) are UUIDs (GUIDs) that you obtain from your Secure Exchanges administration portal when creating your API license.
| Credential | Description |
|---|---|
serial |
Serial number of your license |
api_user |
API user ID |
api_password |
API user password |
Configuration
Selecting the environment
SettingsHelper selects the environment and returns its endpoints, nothing
else. The SDK stores no credentials and never reads them from the process
environment: the serial, API user and API password are yours, and are passed
explicitly to every call.
use SecureExchanges\SDK\Client\SecureExchangesClient; use SecureExchanges\SDK\Client\SEMSClient; use SecureExchanges\SDK\Helpers\SettingsHelper; // ['apiEndpoint' => ..., 'semsEndpoint' => ..., 'environment' => 'production'] $endpoints = SettingsHelper::configureProduction(); $client = new SecureExchangesClient($endpoints['apiEndpoint']); $sems = new SEMSClient($endpoints['semsEndpoint']); // loadMyCredentials() is your own function: it returns the three UUIDs of your // licence. See "Protecting your credentials" below for where to keep them. [$serial, $apiUser, $apiPassword] = loadMyCredentials();
| Method | Environment | Endpoints |
|---|---|---|
configureProduction() |
production | www.secure-exchanges.com |
configurePreview() |
preview (test) | preview.secure-exchanges.com |
Preview and production credentials are different: a production serial will not work in preview, and vice versa.
Individual endpoints can still be overridden afterwards with setCustomFileHandler(), setCustomFileUploadHandler() and setCustomSemsEndpoint().
Protecting your credentials
These three UUIDs are the equivalent of a password on your Secure Exchanges account: whoever holds them can send, read and delete messages in your name, and the usage is billed to you. How they are stored is entirely your decision, and your responsibility.
- Never commit them. Keep them out of the repository, and check the history too:
git log -S <serial>finds a secret committed once and removed later. A secret that reached a remote is compromised: rotate it, don't just delete the line. - Never hard-code them in source. They end up in stack traces, in bug reports, in the packages you ship, and in every fork of the code.
- Prefer a secret manager. Encrypted at rest, access audited, rotation possible without a redeploy.
- If you keep them in a file, encrypt it or lock it down. Outside the document root, owned by the PHP-FPM user, permissions
0400, and excluded from your backups or encrypted inside them. - Never put them in an FPM pool's
env[]. They then become visible inphpinfo(), in$_ENV, and in most error dumps, for the whole lifetime of every worker. The same goes forputenv(): a value set that way still shows up inphpinfo(). - Keep them out of your logs. Exclude them explicitly from your error handler, from your crash reporter and from request dumps, which capture the environment and superglobals by default. Never
var_dump()a configuration array in production. - Rotate them from the administration portal the moment you suspect an exposure: a laptop lost, a log shipped to a third party, a developer leaving.
SDK architecture
The SDK is organized into three main modules (namespace SecureExchanges\SDK, PSR-4 from src/):
Client/contains the HTTP layer:SecureExchangesClient(main API),SEMSClient(email delivery service), both built onBaseClient.Helpers/contains the business logic:MessageHelper(send/read/delete),HandShakeHelper(post-quantum handshake),CryptoHelper(AES/RSA/SHA primitives),SignHelperandZoneBuilder(signature zones),PkiFileHelper(PDF certification),FileHelper(encrypted upload/download),SettingsHelper(configuration), plus validation helpers (EmailHelper,PhoneHelper), logging (LogsHelper), and licensing (LicenceHelper).Models/contains the typed objects: responses (Answer/), entities (Entity/), enumerations (Enum/), transport objects (Transport/), JSON models (JsonModels/), arguments (Args/). TheCallback/folder contains the notification (callback) objects.
Usage guide
All the examples below assume the following setup:
use SecureExchanges\SDK\Client\SecureExchangesClient; use SecureExchanges\SDK\Client\SEMSClient; use SecureExchanges\SDK\Helpers\SettingsHelper; use SecureExchanges\SDK\Helpers\MessageHelper; use SecureExchanges\SDK\Models\Entity\RecipientInfo; $endpoints = SettingsHelper::configureProduction(); $client = new SecureExchangesClient($endpoints['apiEndpoint']); $sems = new SEMSClient($endpoints['semsEndpoint']); // Your own function: it returns the three UUIDs of your licence. [$serial, $apiUser, $apiPassword] = loadMyCredentials();
Sending a simple message
$answer = MessageHelper::multiRecipientMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, recipients: [new RecipientInfo(email: 'recipient@example.com')], message: 'Hello, here is your secure message.', subject: 'Confidential message', cultureId: 'en-US', semsClient: $sems ); if ($answer->status === 200 && $answer->recipientsAnswer) { echo 'URL: ' . $answer->recipientsAnswer[0]->answer->url; }
Sending with a password and SMS 2FA
The password and the delivery channel are two independent, combinable options. The channel is controlled by the SendMethodEnum enumeration:
| Value | Behavior |
|---|---|
SendMethodEnum::ONLY_EMAIL (default) |
Link sent by email |
SendMethodEnum::SMS_ONLY |
Link sent by SMS only |
SendMethodEnum::SMS_WITH_EMAIL_CODE |
Link by SMS, opening code by email |
SendMethodEnum::EMAIL_WITH_SMS_CODE |
Link by email, opening code by SMS (2FA) |
use SecureExchanges\SDK\Models\Enum\SendMethodEnum; $answer = MessageHelper::multiRecipientMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, recipients: [new RecipientInfo(email: 'user@example.com', phone: '+15145551234')], message: 'Confidential document attached.', subject: 'Protected document', password: 'SecretPassword123', sendMethod: SendMethodEnum::EMAIL_WITH_SMS_CODE, cultureId: 'en-US', maximumOpenTime: 3, // maximum number of opens minutesExpiration: 1440, // expiration in minutes (1440 = 24 h) semsClient: $sems );
Sending with attachments
// From disk $answer = MessageHelper::multiRecipientMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, recipients: [new RecipientInfo(email: 'recipient@example.com')], message: 'See attached files.', subject: 'Secure files', filesPath: ['/path/to/document.pdf', '/path/to/image.png'], semsClient: $sems ); // From memory $pdfBytes = file_get_contents('/path/to/report.pdf'); $csvBytes = file_get_contents('/path/to/data.csv'); $answer = MessageHelper::multiRecipientMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, recipients: [new RecipientInfo(email: 'recipient@example.com')], message: 'Report and data attached.', subject: 'Monthly report', filesList: [ ['binary' => $pdfBytes, 'file_name' => 'report.pdf'], ['binary' => $csvBytes, 'file_name' => 'data.csv'], ], semsClient: $sems );
Both parameters (filesPath and filesList) can be combined in the same send.
Sending with PKI certification
PKI certification applies a digital certificate to the PDF before it is sent for signature. It requires Chrome/Chromium (headless browser via chrome-php) and the ext-sockets extension. Signature zones are defined with ZoneBuilder, which reads the actual dimensions of the PDF pages. The flow:
use SecureExchanges\SDK\Helpers\CryptoHelper; use SecureExchanges\SDK\Helpers\ZoneBuilder; use SecureExchanges\SDK\Helpers\SignHelper; use SecureExchanges\SDK\Helpers\PkiFileHelper; use SecureExchanges\SDK\Models\Args\CertifyPdfArgs; use SecureExchanges\SDK\Models\Args\FileArgs; use SecureExchanges\SDK\Models\Entity\SignZoneDefinition; use SecureExchanges\SDK\Models\Transport\FileZoneDefinition; use SecureExchanges\SDK\Models\Transport\RecipientZoneDefinition; // 1. Read the PDF and compute its SHA512 $pdfPath = '/path/to/contract.pdf'; $pdfBytes = file_get_contents($pdfPath); $sha512 = CryptoHelper::getSha512HashOfBytes($pdfBytes); // 2. Define the signature zones with ZoneBuilder $zoneBuilder = new ZoneBuilder($pdfPath, isPki: true); $zones = [ // Signature zone on page 1 $zoneBuilder->createFieldSignZone( page: 1, fieldName: 'signature1', x: 60, y: 400, width: 200, height: 50 ), // Date zone filled in automatically at signing time $zoneBuilder->createFieldDateSignZone( page: 1, fieldName: 'date1', x: 60, y: 500, width: 100, height: 25, fontSize: 12 ), ]; $zoneBuilder->close(); // 3. Bind the zones to the recipient (file referenced by its SHA512) $recipientZones = [ new RecipientZoneDefinition( email: 'signer@example.com', phone: null, zonesDefByFile: [ new FileZoneDefinition( uniqueName: $sha512, zonesDef: new SignZoneDefinition(mode: 'define', zones: $zones), doNotEncryptPDF: false, doNotAppendCertificateToFile: false, isPKIFile: true ) ] ) ]; // 4. Get the certification token $tokenResponse = SignHelper::getCertificationToken( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, originalHash: $sha512, cultureId: 'en-US', tokenType: 'CertifyDocument' ); $certData = $tokenResponse['certification_data']; // 5. Certify the PDF (headless browser) $certifyArgs = new CertifyPdfArgs( file: new FileArgs($pdfBytes, 'contract.pdf'), recipients: ['signer@example.com'], certifyData: $certData, detectExistingFields: true, recipientZoneDefinitions: $recipientZones ); $pkiHelper = new PkiFileHelper(isPreview: false, culture: 'en-US'); $certifiedPdf = $pkiHelper->certifyPkiPdf($certifyArgs); $pkiHelper->close(); // 6. Send the certified PDF $answer = MessageHelper::multiRecipientMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, recipients: [new RecipientInfo(email: 'signer@example.com')], message: 'Please sign this certified document.', subject: 'PKI document to sign', cultureId: 'en-US', certifiedPdfs: [$certifiedPdf], semsClient: $sems );
A complete, runnable example, including signature options (SignOptions: restricting the signing mode, requesting a file in return) and callback parameters (contextual keys), can be found in example_pki_with_signature_options.php at the root of phpSDK/.
Retrieving a message
// The link the recipient received $link = 'https://www.secure-exchanges.com/message.aspx?msgid=xxx&sems=yyy&cpart=zzz'; $msgParams = MessageHelper::getSecureExchangesMessageFromLink($link); // Decrypt the message $response = MessageHelper::getMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, msgParams: $msgParams, password: null, // password if the message is protected digitCode: null, // SMS code if 2FA is enabled semsClient: $sems ); if ($response->status === 200) { echo "Subject: {$response->subject}\n"; echo "Message: {$response->message}\n"; // $response->filesMetaData, $response->remainingOpenTime, $response->expireOn, ... }
Note: each read consumes one of the message's allowed opens (
maximumOpenTime).
Deleting a message
$success = MessageHelper::deleteMessageByTrackingId( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, trackingId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', cultureId: 'en-US' );
Reply envelopes
Envelopes let a recipient send documents to you securely:
// Simple envelope (single recipient) $envelope = MessageHelper::getEnveloppe( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, subject: 'Send me your documents', destination: 'recipient@example.com', cultureId: 'en-US', replyExpirationHours: 48, maximumReplyOpenTime: 3, replyToApi: true, authorizedExtensions: ['.pdf', '.docx'] ); if ($envelope->url) { echo "Envelope URL: {$envelope->url}"; } // Multiple envelopes (one per recipient; a phone number on the // recipient automatically enables 2FA) $envelopes = MessageHelper::getEnvelopes( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, subject: 'Required documents', recipients: [ ['Email' => 'user1@example.com'], ['Email' => 'user2@example.com', 'Phone' => '+15145551234'], ] );
API reference
MessageHelper
Static class for sending and receiving messages (src/Helpers/MessageHelper.php).
| Method | Description |
|---|---|
multiRecipientMessage(...) |
Sends a message to one or more recipients |
getMessage($apiClient, $serial, $apiUser, $apiPassword, $msgParams, $password = null, $digitCode = null, $semsClient = null) |
Retrieves and decrypts a message |
getSecureExchangesMessageFromLink($link) |
Parses a Secure Exchanges link into parameters for getMessage |
deleteMessageByTrackingId($apiClient, $serial, $apiUser, $apiPassword, $trackingId, $cultureId = 'en-US') |
Deletes a message |
getEnveloppe(...) |
Creates a reply envelope (single recipient) |
getEnvelopes(...) |
Creates multiple envelopes |
sendTrace($apiClient, $traceContent) |
Sends a support trace |
multiRecipientMessage parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
apiClient |
SecureExchangesClient |
required | API client |
serial |
string |
required | Serial number (UUID) |
apiUser |
string |
required | API user ID (UUID) |
apiPassword |
string |
required | API password (UUID) |
recipients |
RecipientInfo[] |
required | Recipients |
message |
string |
required | Message content (HTML accepted) |
subject |
string |
required | Subject |
password |
?string |
null |
Protection password |
filesList |
?array |
null |
In-memory files (['binary' => ..., 'file_name' => ...]) |
filesPath |
?array |
null |
Paths of files on disk |
sendMethod |
SendMethodEnum |
ONLY_EMAIL |
Delivery channel (see table above) |
getBackHtml |
bool |
true |
true = you send the email yourself, false = Secure Exchanges sends it |
showSubject |
bool |
true |
Show the subject in the notification |
getNotify |
bool |
true |
Notify when the message is opened |
cultureId |
string |
'en-US' |
Language ('fr-CA', 'en-US', …) |
maximumOpenTime |
int |
1 |
Maximum number of opens (1-99) |
minutesExpiration |
int |
10080 |
Expiration in minutes (default: 7 days) |
createMessageCopy |
bool |
false |
Copy for the sender |
name |
string |
'Secure Exchanges' |
Sender name |
callbackParameters |
?string |
null |
Callback parameters (JSON) |
replyOptions |
?array |
null |
Recipient reply options (see below) |
ownerDontNeedToSign |
bool |
true |
The sender does not sign |
semsClient |
?SEMSClient |
null |
SEMS client (created automatically if omitted) |
certifiedPdfs |
?array |
null |
PKI-certified PDFs (CertifiedPdfContainer) |
signOptions |
?SignOptions |
null |
Signature options (restriction, return upload) |
replyOptions is an array (PascalCase keys, matching the API) that controls
what the recipient can do in reply:
use SecureExchanges\SDK\Models\Enum\ReplyFileUploadMode; $replyOptions = [ 'UploadMode' => ReplyFileUploadMode::UPLOAD_MANDATORY->value, // NO_UPLOAD, UPLOAD_OPTIONAL, UPLOAD_MANDATORY 'AuthorizedExtensions' => ['.jpg', '.png', '.pdf'], 'Messages' => [ ['CultureID' => 'fr-CA', 'Message' => 'Veuillez joindre votre document.'], ['CultureID' => 'en-CA', 'Message' => 'Please attach your document.'], ], 'NoEditor' => false, // disable the reply text editor 'NoReply' => false, // disallow any reply ];
Returns: MultiRecipientAnswer (status, data, recipientsAnswer[], each element containing a SendMessageAnswer with url, trackingId, htmlMsg, …).
SignHelper
Signature zone and certification management (src/Helpers/SignHelper.php).
| Method | Description |
|---|---|
getCertificationToken($apiClient, $serial, $apiUser, $apiPassword, $originalHash, $cultureId, $tokenType = null, $metadata = null) |
Obtains a PKI certification token |
addFileZoneDefToRecipientIndex(...) |
Converts templates into zone definitions |
convertRecipientIndexToList($recipientIndex) |
Converts the index into a list for multiRecipientMessage |
concatRecipientZoneDefinitionLists($list1, $list2) |
Merges two zone definition lists |
validateAllRecipientsHaveZones(...) |
Checks that all recipients have zones |
validateDocumentSignatureIntegrity(...) |
Validates the integrity of a signed document |
applyPKI($manifest, $certificationData) |
Applies the certification data to a manifest |
PkiFileHelper
PKI certification of PDFs via headless browser (src/Helpers/PkiFileHelper.php, based on chrome-php).
$helper = new PkiFileHelper(isPreview: false, culture: 'en-US'); $certified = $helper->certifyPkiPdf($args); // CertifyPdfArgs -> CertifiedPdfContainer $helper->close();
The Chrome/Chromium binary is detected automatically, or forced via the SE_CHROME_PATH environment variable.
CertifyPdfArgs: file (FileArgs: binary + name), recipients (emails), certifyData (certification token), detectExistingFields (default true), recipientZoneDefinitions, defineRecipients (optional callback).
CertifiedPdfContainer (return value): fileBytes (certified PDF), fileName, signFilesRequired (including the new sha512), recipientZoneDefinitions (zones updated with the new hash).
ZoneBuilder
Builds signature zones from a PDF (src/Helpers/ZoneBuilder.php).
$builder = new ZoneBuilder($filePath, isPki: true); $zone = $builder->createFieldSignZone(page: 1, fieldName: 'sig1', x: 50, y: 600, width: 200, height: 80); $builder->close();
Zone types: signature, initials, text, number, date, signing date, checkbox, radio buttons, text area, option list, dropdown list. Utilities: getPageCount(), getPageDimensions($page).
SettingsHelper
SDK configuration (src/Helpers/SettingsHelper.php).
| Method | Description |
|---|---|
configureProduction() / configurePreview() |
Selects the environment and returns its endpoints |
getEnvironment() |
Name of the configured environment |
getVerifySsl() |
Whether TLS certificates are verified |
setCustomFileHandler($url) / setCustomFileUploadHandler($url) / setCustomSemsEndpoint($url) |
Override an individual endpoint |
getSemsEndpoint() / getFileUploadHandler() / getFileDownloadHandler() |
Effective endpoints |
reset() |
Resets to default values |
Other helpers
| Class | Purpose |
|---|---|
LogsHelper |
getLog(...) (by messageId or trackingId), getLogs($apiClient, $serial, $apiUser, $apiPassword, $fromDate, $toDate, $userLogSerial = null) |
LicenceHelper |
getSerialInfo(...), isLicenceValid(...) : license information and validity |
CryptoHelper |
Primitives: getSha512HashOfBytes, getSha512OfFile, AES (encryptBinary/decryptBinaryFromBytes, …), RSA, .NET GUID (guidToBytesLe) |
EmailHelper / PhoneHelper |
Validation of email addresses and phone numbers (E.164, SMS capability) |
Data models
// Recipient: a phone number enables the SMS/2FA features new RecipientInfo(email: 'user@example.com', phone: '+15145551234');
GetMessageResponse (reading): status, subject, message, filesMetaData, remainingOpenTime, expireOn, createdDate, callbackParameters, containsSignsFiles, …
GetEnveloppeResponse (envelope): url, statusCode, repId, recipient.
Support
For any question or issue, contact supportdev@secure-exchanges.info or visit secure-exchanges.com. Product documentation: help.secure-exchanges.com.
License
Licensed under the Apache License, Version 2.0. Full text at
https://www.apache.org/licenses/LICENSE-2.0. The copyright notice is in the
NOTICE file.
Using the SDK also requires a Secure Exchanges licence (serial, API user and API password) obtained from your administration portal. The Apache licence covers this source code, not access to the service.
Secure Exchanges PHP SDK (version française)
English | Français
SDK PHP pour l'API Secure Exchanges : messagerie chiffrée de bout en bout, transfert sécurisé de fichiers, signature électronique et certification PKI de documents PDF.
Avec ce SDK vous pouvez, depuis votre application PHP :
- envoyer des messages et des fichiers chiffrés de bout en bout, avec ou sans mot de passe, avec vérification 2FA par SMS ;
- faire signer électroniquement des documents PDF (zones de signature, initiales, dates, champs texte…) ;
- certifier des PDF avec un certificat numérique (PKI) avant l'envoi pour signature ;
- lire et supprimer des messages, créer des enveloppes de réponse sécurisées ;
- consulter les journaux d'activité (envois, ouvertures, signatures).
Table des matières
- Sécurité
- Installation
- Prérequis
- Identifiants
- Configuration
- Architecture du SDK
- Guide d'utilisation
- Référence API
- Support
- Licence
Sécurité
- Chiffrement de bout en bout : le contenu (corps du message, fichiers) est chiffré dans votre application avant de quitter votre serveur, avec des clés générées localement et jamais transmises au serveur Secure Exchanges : celui-ci ne voit passer que des données chiffrées.
- Échange de clés post-quantique : ML-KEM-1024 (FIPS 203), résistant aux attaques par ordinateur quantique, signé en ML-DSA-87 (FIPS 204).
- Chiffrement AES-256 du contenu et des échanges avec le serveur.
- Protection anti-interception : toute tentative d'attaque de type homme du milieu (MITM) fait échouer la connexion.
- Rien à gérer : le SDK applique automatiquement l'intégralité du protocole à chaque appel : vous n'avez aucune primitive cryptographique à manipuler.
Installation
composer require secure-exchanges/sdk
Dépendances
Les dépendances sont gérées par Composer (voir composer.json) :
paragonie/pqcrypto_compat(0.3.2) : cryptographie post-quantique (ML-KEM / ML-DSA)phpseclib/phpseclib: cryptographie (RSA pour certaines fonctions annexes, utilitaires)setasign/fpdi,setasign/fpdf: manipulation de PDFsmalot/pdfparser: lecture de PDFchrome-php/chrome: certification PKI (navigateur headless)giggsey/libphonenumber-for-php: validation des numéros de téléphone
Prérequis
Pour utiliser le SDK, vous devez disposer de :
- PHP 8.2 ou plus récent (
composer installéchoue sous PHP 8.1) - Extensions PHP :
openssl,json,mbstring,curl ext-sockets: requise pour la signature PKI (utilisée parchrome-php)- Google Chrome ou Chromium installé sur la machine, requis uniquement pour la certification PDF (PKI) ; le chemin du binaire peut être fourni via la variable d'environnement
SE_CHROME_PATH - Un numéro de série (
serial) : UUID de votre licence - Un identifiant API (
api_user) : UUID de l'utilisateur API - Un mot de passe API (
api_password) : UUID du mot de passe API
Recommandé en production : l'extension native ext-pqcrypto (github.com/paragonie/ext-pqcrypto), nettement plus rapide pour la cryptographie post-quantique que l'implémentation PHP pur installée par Composer. Aucun changement de code n'est nécessaire : le SDK bascule automatiquement sur l'extension dès qu'elle est chargée. À défaut, activer ext-opcache (avec JIT) accélère sensiblement l'implémentation PHP pur.
Identifiants
Où trouver vos identifiants
Les 3 identifiants nécessaires (serial, api_user, api_password) sont des UUID (GUIDs) que vous obtenez dans votre portail d'administration Secure Exchanges lors de la création de votre licence API.
| Identifiant | Description |
|---|---|
serial |
Numéro de série de votre licence |
api_user |
Identifiant de l'utilisateur API |
api_password |
Mot de passe de l'utilisateur API |
Configuration
Sélection de l'environnement
SettingsHelper sélectionne l'environnement et retourne ses endpoints, rien
d'autre. Le SDK ne stocke aucun identifiant et ne va jamais les chercher dans
l'environnement du processus : le serial, l'utilisateur et le mot de passe API
vous appartiennent, et sont passés explicitement à chaque appel.
use SecureExchanges\SDK\Client\SecureExchangesClient; use SecureExchanges\SDK\Client\SEMSClient; use SecureExchanges\SDK\Helpers\SettingsHelper; // ['apiEndpoint' => ..., 'semsEndpoint' => ..., 'environment' => 'production'] $endpoints = SettingsHelper::configureProduction(); $client = new SecureExchangesClient($endpoints['apiEndpoint']); $sems = new SEMSClient($endpoints['semsEndpoint']); // chargerMesIdentifiants() est votre propre fonction : elle retourne les trois UUID // de votre licence. Voir « Protéger vos identifiants » ci-dessous pour savoir // où les conserver. [$serial, $apiUser, $apiPassword] = chargerMesIdentifiants();
| Méthode | Environnement | Endpoints |
|---|---|---|
configureProduction() |
production | www.secure-exchanges.com |
configurePreview() |
preview (test) | preview.secure-exchanges.com |
Vos identifiants preview et production sont différents : un serial de production ne fonctionnera pas en preview, et inversement.
Les endpoints individuels restent surchargeables ensuite avec setCustomFileHandler(), setCustomFileUploadHandler() et setCustomSemsEndpoint().
Protéger vos identifiants
Ces trois UUID valent un mot de passe sur votre compte Secure Exchanges : quiconque les détient peut envoyer, lire et supprimer des messages en votre nom, et la consommation vous est facturée. Leur stockage est entièrement votre décision, et votre responsabilité.
- Ne les committez jamais. Gardez-les hors du dépôt, et vérifiez aussi l'historique :
git log -S <serial>retrouve un secret committé une fois puis retiré. Un secret parti sur un remote est compromis : il faut le régénérer, pas seulement supprimer la ligne. - Ne les écrivez jamais en dur dans le code. Ils se retrouvent dans les traces d'exception, dans les rapports de bogue, dans les paquets que vous distribuez et dans chaque fork du code.
- Privilégiez un gestionnaire de secrets. Chiffrés au repos, accès journalisé, rotation possible sans redéploiement.
- Si vous les gardez dans un fichier, chiffrez-le ou verrouillez-le. Hors de la racine web, appartenant à l'utilisateur PHP-FPM, permissions
0400, et exclu de vos sauvegardes ou chiffré à l'intérieur. - Ne les mettez jamais dans le
env[]d'un pool FPM. Ils deviennent alors visibles dansphpinfo(), dans$_ENVet dans la plupart des vidages d'erreur, pour toute la durée de vie de chaque worker. Idem pourputenv(): une valeur posée ainsi apparaît quand même dansphpinfo(). - Tenez-les hors de vos journaux. Excluez-les explicitement de votre gestionnaire d'erreurs, de votre rapporteur d'incidents et des vidages de requête, qui capturent l'environnement et les superglobales par défaut. Ne faites jamais un
var_dump()d'un tableau de configuration en production. - Régénérez-les depuis le portail d'administration dès que vous soupçonnez une exposition : un portable perdu, un journal transmis à un tiers, le départ d'un développeur.
Architecture du SDK
Le SDK est organisé en trois grands modules (namespace SecureExchanges\SDK, PSR-4 depuis src/) :
Client/contient la couche HTTP :SecureExchangesClient(API principale),SEMSClient(service d'envoi d'emails), tous deux bâtis surBaseClient.Helpers/contient la logique métier :MessageHelper(envoi/lecture/suppression),HandShakeHelper(handshake post-quantique),CryptoHelper(primitives AES/RSA/SHA),SignHelperetZoneBuilder(zones de signature),PkiFileHelper(certification PDF),FileHelper(upload/download chiffrés),SettingsHelper(configuration), plus des helpers de validation (EmailHelper,PhoneHelper), de logs (LogsHelper) et de licence (LicenceHelper).Models/contient les objets typés : réponses (Answer/), entités (Entity/), énumérations (Enum/), objets de transport (Transport/), modèles JSON (JsonModels/), arguments (Args/). Le dossierCallback/contient les objets de notification (callbacks).
Guide d'utilisation
Tous les exemples ci-dessous supposent la configuration suivante :
use SecureExchanges\SDK\Client\SecureExchangesClient; use SecureExchanges\SDK\Client\SEMSClient; use SecureExchanges\SDK\Helpers\SettingsHelper; use SecureExchanges\SDK\Helpers\MessageHelper; use SecureExchanges\SDK\Models\Entity\RecipientInfo; $endpoints = SettingsHelper::configureProduction(); $client = new SecureExchangesClient($endpoints['apiEndpoint']); $sems = new SEMSClient($endpoints['semsEndpoint']); // Votre propre fonction : elle retourne les trois UUID de votre licence. [$serial, $apiUser, $apiPassword] = chargerMesIdentifiants();
Envoi d'un message simple
$answer = MessageHelper::multiRecipientMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, recipients: [new RecipientInfo(email: 'destinataire@example.com')], message: 'Bonjour, voici votre message sécurisé.', subject: 'Message confidentiel', cultureId: 'fr-CA', semsClient: $sems ); if ($answer->status === 200 && $answer->recipientsAnswer) { echo 'URL : ' . $answer->recipientsAnswer[0]->answer->url; }
Envoi avec mot de passe et 2FA par SMS
Le mot de passe et le canal d'envoi sont deux options indépendantes et combinables. Le canal est contrôlé par l'énumération SendMethodEnum :
| Valeur | Comportement |
|---|---|
SendMethodEnum::ONLY_EMAIL (défaut) |
Lien envoyé par email |
SendMethodEnum::SMS_ONLY |
Lien envoyé par SMS uniquement |
SendMethodEnum::SMS_WITH_EMAIL_CODE |
Lien par SMS, code d'ouverture par email |
SendMethodEnum::EMAIL_WITH_SMS_CODE |
Lien par email, code d'ouverture par SMS (2FA) |
use SecureExchanges\SDK\Models\Enum\SendMethodEnum; $answer = MessageHelper::multiRecipientMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, recipients: [new RecipientInfo(email: 'user@example.com', phone: '+15145551234')], message: 'Document confidentiel ci-joint.', subject: 'Document protégé', password: 'MotDePasseSecret123', sendMethod: SendMethodEnum::EMAIL_WITH_SMS_CODE, cultureId: 'fr-CA', maximumOpenTime: 3, // nombre maximum d'ouvertures minutesExpiration: 1440, // expiration en minutes (1440 = 24 h) semsClient: $sems );
Envoi avec fichiers joints
// Depuis le disque $answer = MessageHelper::multiRecipientMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, recipients: [new RecipientInfo(email: 'destinataire@example.com')], message: 'Voir fichiers joints.', subject: 'Fichiers sécurisés', filesPath: ['/chemin/vers/document.pdf', '/chemin/vers/image.png'], semsClient: $sems ); // Depuis la mémoire $pdfBytes = file_get_contents('/chemin/vers/rapport.pdf'); $csvBytes = file_get_contents('/chemin/vers/donnees.csv'); $answer = MessageHelper::multiRecipientMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, recipients: [new RecipientInfo(email: 'destinataire@example.com')], message: 'Rapport et données en pièces jointes.', subject: 'Rapport mensuel', filesList: [ ['binary' => $pdfBytes, 'file_name' => 'rapport.pdf'], ['binary' => $csvBytes, 'file_name' => 'donnees.csv'], ], semsClient: $sems );
Les deux paramètres (filesPath et filesList) peuvent être combinés dans un même envoi.
Envoi avec certification PKI
La certification PKI appose un certificat numérique sur le PDF avant l'envoi pour signature. Elle nécessite Chrome/Chromium (navigateur headless via chrome-php) et l'extension ext-sockets. Les zones de signature se définissent avec ZoneBuilder, qui lit les dimensions réelles des pages du PDF. Le flux :
use SecureExchanges\SDK\Helpers\CryptoHelper; use SecureExchanges\SDK\Helpers\ZoneBuilder; use SecureExchanges\SDK\Helpers\SignHelper; use SecureExchanges\SDK\Helpers\PkiFileHelper; use SecureExchanges\SDK\Models\Args\CertifyPdfArgs; use SecureExchanges\SDK\Models\Args\FileArgs; use SecureExchanges\SDK\Models\Entity\SignZoneDefinition; use SecureExchanges\SDK\Models\Transport\FileZoneDefinition; use SecureExchanges\SDK\Models\Transport\RecipientZoneDefinition; // 1. Lire le PDF et calculer son SHA512 $pdfPath = '/chemin/vers/contrat.pdf'; $pdfBytes = file_get_contents($pdfPath); $sha512 = CryptoHelper::getSha512HashOfBytes($pdfBytes); // 2. Définir les zones de signature avec ZoneBuilder $zoneBuilder = new ZoneBuilder($pdfPath, isPki: true); $zones = [ // Zone de signature en page 1 $zoneBuilder->createFieldSignZone( page: 1, fieldName: 'signature1', x: 60, y: 400, width: 200, height: 50 ), // Zone de date remplie automatiquement à la signature $zoneBuilder->createFieldDateSignZone( page: 1, fieldName: 'date1', x: 60, y: 500, width: 100, height: 25, fontSize: 12 ), ]; $zoneBuilder->close(); // 3. Associer les zones au destinataire (fichier référencé par son SHA512) $recipientZones = [ new RecipientZoneDefinition( email: 'signataire@example.com', phone: null, zonesDefByFile: [ new FileZoneDefinition( uniqueName: $sha512, zonesDef: new SignZoneDefinition(mode: 'define', zones: $zones), doNotEncryptPDF: false, doNotAppendCertificateToFile: false, isPKIFile: true ) ] ) ]; // 4. Obtenir le token de certification $tokenResponse = SignHelper::getCertificationToken( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, originalHash: $sha512, cultureId: 'fr-CA', tokenType: 'CertifyDocument' ); $certData = $tokenResponse['certification_data']; // 5. Certifier le PDF (navigateur headless) $certifyArgs = new CertifyPdfArgs( file: new FileArgs($pdfBytes, 'contrat.pdf'), recipients: ['signataire@example.com'], certifyData: $certData, detectExistingFields: true, recipientZoneDefinitions: $recipientZones ); $pkiHelper = new PkiFileHelper(isPreview: false, culture: 'fr-CA'); $certifiedPdf = $pkiHelper->certifyPkiPdf($certifyArgs); $pkiHelper->close(); // 6. Envoyer le PDF certifié $answer = MessageHelper::multiRecipientMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, recipients: [new RecipientInfo(email: 'signataire@example.com')], message: 'Veuillez signer ce document certifié.', subject: 'Document PKI à signer', cultureId: 'fr-CA', certifiedPdfs: [$certifiedPdf], semsClient: $sems );
Un exemple complet et exécutable, incluant les options de signature (SignOptions : restriction du mode de signature, demande de fichier en retour) et les paramètres de callback (clés contextuelles), se trouve dans example_pki_with_signature_options.php à la racine de phpSDK/.
Récupération d'un message
// Le lien reçu par le destinataire $link = 'https://www.secure-exchanges.com/message.aspx?msgid=xxx&sems=yyy&cpart=zzz'; $msgParams = MessageHelper::getSecureExchangesMessageFromLink($link); // Déchiffrer le message $response = MessageHelper::getMessage( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, msgParams: $msgParams, password: null, // mot de passe si le message est protégé digitCode: null, // code SMS si 2FA semsClient: $sems ); if ($response->status === 200) { echo "Sujet : {$response->subject}\n"; echo "Message: {$response->message}\n"; // $response->filesMetaData, $response->remainingOpenTime, $response->expireOn, ... }
Note : chaque lecture consomme une ouverture du message (
maximumOpenTime).
Suppression d'un message
$success = MessageHelper::deleteMessageByTrackingId( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, trackingId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', cultureId: 'fr-CA' );
Enveloppes de réponse
Les enveloppes permettent à un destinataire de vous envoyer des documents de manière sécurisée :
// Enveloppe simple (un destinataire) $envelope = MessageHelper::getEnveloppe( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, subject: 'Envoyez-moi vos documents', destination: 'destinataire@example.com', cultureId: 'fr-CA', replyExpirationHours: 48, maximumReplyOpenTime: 3, replyToApi: true, authorizedExtensions: ['.pdf', '.docx'] ); if ($envelope->url) { echo "URL de l'enveloppe : {$envelope->url}"; } // Enveloppes multiples (une par destinataire ; un numéro de téléphone // dans le destinataire active automatiquement le 2FA) $envelopes = MessageHelper::getEnvelopes( apiClient: $client, serial: $serial, apiUser: $apiUser, apiPassword: $apiPassword, subject: 'Documents requis', recipients: [ ['Email' => 'user1@example.com'], ['Email' => 'user2@example.com', 'Phone' => '+15145551234'], ] );
Référence API
MessageHelper
Classe statique pour l'envoi et la réception de messages (src/Helpers/MessageHelper.php).
| Méthode | Description |
|---|---|
multiRecipientMessage(...) |
Envoie un message à un ou plusieurs destinataires |
getMessage($apiClient, $serial, $apiUser, $apiPassword, $msgParams, $password = null, $digitCode = null, $semsClient = null) |
Récupère et déchiffre un message |
getSecureExchangesMessageFromLink($link) |
Parse un lien Secure Exchanges en paramètres pour getMessage |
deleteMessageByTrackingId($apiClient, $serial, $apiUser, $apiPassword, $trackingId, $cultureId = 'en-US') |
Supprime un message |
getEnveloppe(...) |
Crée une enveloppe de réponse (un destinataire) |
getEnvelopes(...) |
Crée des enveloppes multiples |
sendTrace($apiClient, $traceContent) |
Envoie une trace de support |
Paramètres de multiRecipientMessage
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
apiClient |
SecureExchangesClient |
requis | Client API |
serial |
string |
requis | Numéro de série (UUID) |
apiUser |
string |
requis | Identifiant API (UUID) |
apiPassword |
string |
requis | Mot de passe API (UUID) |
recipients |
RecipientInfo[] |
requis | Destinataires |
message |
string |
requis | Contenu du message (HTML accepté) |
subject |
string |
requis | Sujet |
password |
?string |
null |
Mot de passe de protection |
filesList |
?array |
null |
Fichiers en mémoire (['binary' => ..., 'file_name' => ...]) |
filesPath |
?array |
null |
Chemins de fichiers sur le disque |
sendMethod |
SendMethodEnum |
ONLY_EMAIL |
Canal d'envoi (voir tableau plus haut) |
getBackHtml |
bool |
true |
true = vous envoyez l'email vous-même, false = Secure Exchanges l'envoie |
showSubject |
bool |
true |
Afficher le sujet dans la notification |
getNotify |
bool |
true |
Notifier à l'ouverture |
cultureId |
string |
'en-US' |
Langue ('fr-CA', 'en-US', …) |
maximumOpenTime |
int |
1 |
Nombre maximum d'ouvertures (1-99) |
minutesExpiration |
int |
10080 |
Expiration en minutes (défaut : 7 jours) |
createMessageCopy |
bool |
false |
Copie pour l'expéditeur |
name |
string |
'Secure Exchanges' |
Nom de l'expéditeur |
callbackParameters |
?string |
null |
Paramètres de callback (JSON) |
replyOptions |
?array |
null |
Options de réponse du destinataire (voir plus bas) |
ownerDontNeedToSign |
bool |
true |
L'expéditeur ne signe pas |
semsClient |
?SEMSClient |
null |
Client SEMS (créé automatiquement si omis) |
certifiedPdfs |
?array |
null |
PDF certifiés PKI (CertifiedPdfContainer) |
signOptions |
?SignOptions |
null |
Options de signature (restriction, upload en retour) |
replyOptions est un tableau (clés PascalCase, comme l'API) qui contrôle ce que
le destinataire peut faire en réponse :
use SecureExchanges\SDK\Models\Enum\ReplyFileUploadMode; $replyOptions = [ 'UploadMode' => ReplyFileUploadMode::UPLOAD_MANDATORY->value, // NO_UPLOAD, UPLOAD_OPTIONAL, UPLOAD_MANDATORY 'AuthorizedExtensions' => ['.jpg', '.png', '.pdf'], 'Messages' => [ ['CultureID' => 'fr-CA', 'Message' => 'Veuillez joindre votre document.'], ['CultureID' => 'en-CA', 'Message' => 'Please attach your document.'], ], 'NoEditor' => false, // désactiver l'éditeur de texte de réponse 'NoReply' => false, // interdire toute réponse ];
Retour : MultiRecipientAnswer (status, data, recipientsAnswer[], chaque élément contenant un SendMessageAnswer avec url, trackingId, htmlMsg, …).
SignHelper
Gestion des zones de signature et de la certification (src/Helpers/SignHelper.php).
| Méthode | Description |
|---|---|
getCertificationToken($apiClient, $serial, $apiUser, $apiPassword, $originalHash, $cultureId, $tokenType = null, $metadata = null) |
Obtient un token de certification PKI |
addFileZoneDefToRecipientIndex(...) |
Convertit des templates en définitions de zones |
convertRecipientIndexToList($recipientIndex) |
Convertit l'index en liste pour multiRecipientMessage |
concatRecipientZoneDefinitionLists($list1, $list2) |
Fusionne deux listes de définitions de zones |
validateAllRecipientsHaveZones(...) |
Vérifie que tous les destinataires ont des zones |
validateDocumentSignatureIntegrity(...) |
Valide l'intégrité d'un document signé |
applyPKI($manifest, $certificationData) |
Applique les données de certification à un manifeste |
PkiFileHelper
Certification PKI de PDF via navigateur headless (src/Helpers/PkiFileHelper.php, basé sur chrome-php).
$helper = new PkiFileHelper(isPreview: false, culture: 'fr-CA'); $certified = $helper->certifyPkiPdf($args); // CertifyPdfArgs -> CertifiedPdfContainer $helper->close();
Le binaire Chrome/Chromium est détecté automatiquement, ou imposé via la variable d'environnement SE_CHROME_PATH.
CertifyPdfArgs : file (FileArgs : binaire + nom), recipients (emails), certifyData (token de certification), detectExistingFields (défaut true), recipientZoneDefinitions, defineRecipients (callback optionnel).
CertifiedPdfContainer (retour) : fileBytes (PDF certifié), fileName, signFilesRequired (dont le nouveau sha512), recipientZoneDefinitions (zones mises à jour avec le nouveau hash).
ZoneBuilder
Construction de zones de signature à partir d'un PDF (src/Helpers/ZoneBuilder.php).
$builder = new ZoneBuilder($filePath, isPki: true); $zone = $builder->createFieldSignZone(page: 1, fieldName: 'sig1', x: 50, y: 600, width: 200, height: 80); $builder->close();
Types de zones : signature, initiales, texte, nombre, date, date de signature, case à cocher, boutons radio, zone de texte, liste d'options, liste déroulante. Utilitaires : getPageCount(), getPageDimensions($page).
SettingsHelper
Configuration du SDK (src/Helpers/SettingsHelper.php).
| Méthode | Description |
|---|---|
configureProduction() / configurePreview() |
Sélectionne l'environnement et retourne ses endpoints |
getEnvironment() |
Nom de l'environnement configuré |
getVerifySsl() |
Validation des certificats TLS |
setCustomFileHandler($url) / setCustomFileUploadHandler($url) / setCustomSemsEndpoint($url) |
Surcharge un endpoint particulier |
getSemsEndpoint() / getFileUploadHandler() / getFileDownloadHandler() |
Endpoints effectifs |
reset() |
Retour aux valeurs par défaut |
Autres helpers
| Classe | Rôle |
|---|---|
LogsHelper |
getLog(...) (par messageId ou trackingId), getLogs($apiClient, $serial, $apiUser, $apiPassword, $fromDate, $toDate, $userLogSerial = null) |
LicenceHelper |
getSerialInfo(...), isLicenceValid(...) : informations et validité de licence |
CryptoHelper |
Primitives : getSha512HashOfBytes, getSha512OfFile, AES (encryptBinary/decryptBinaryFromBytes, …), RSA, GUID .NET (guidToBytesLe) |
EmailHelper / PhoneHelper |
Validation d'adresses email et de numéros de téléphone (E.164, aptitude SMS) |
Modèles de données
// Destinataire : un téléphone active les fonctionnalités SMS/2FA new RecipientInfo(email: 'user@example.com', phone: '+15145551234');
GetMessageResponse (lecture) : status, subject, message, filesMetaData, remainingOpenTime, expireOn, createdDate, callbackParameters, containsSignsFiles, …
GetEnveloppeResponse (enveloppe) : url, statusCode, repId, recipient.
Support
Pour toute question ou problème, contactez supportdev@secure-exchanges.info ou visitez secure-exchanges.com. Documentation produit : help.secure-exchanges.com.
Licence
Distribué sous licence Apache, version 2.0. Texte complet sur
https://www.apache.org/licenses/LICENSE-2.0. Le copyright figure dans le
fichier NOTICE.
L'utilisation du SDK requiert par ailleurs une licence Secure Exchanges (serial, usager et mot de passe API) obtenue depuis votre portail d'administration. La licence Apache couvre ce code source, pas l'accès au service.