L'API AICA Address Autocomplete renvoie des suggestions d'adresses canadiennes structurées pendant la saisie. Utilisez-la depuis votre backend ou via notre widget hébergé. Tout le trafic de production doit utiliser HTTPS.
Les routes incorrectes ou mal configurées renvoient un JSON qui inclut l’état du service (status, service, note) ainsi que error_code — pas de page santé séparée.
Les requêtes utilisent votre clé aica_live_. Pour le widget, la clé est dans le navigateur et doit être restreinte par domaine. Pour le serveur, gardez la clé uniquement côté backend.
Vous pouvez aussi passer la clé avec ?api_key= sur une URL GET standard. Préférez l’en-tête X-AICA-API-Key lorsque c’est possible. Utilisez uniquement une clé serveur — jamais dans une page navigateur. Les clés dans les URL peuvent apparaître dans les journaux de proxy et l’historique du navigateur.
Ajoutez un script CDN et un div vide. Mettez votre clé aica_live_ sur le div (data-api-key). Autorisez votre domaine dans le tableau de bord — aucun proxy PHP requis.
1. Installation en un script (recommandé)
Ajoutez ce script unique dans le head de la page (ou avant la balise body de fermeture). aica-loader.js injecte le CSS une fois et charge le widget. Pas de aica-proxy.php. Restreignez la clé à vos domaines dans la gestion des clés.
Placez ce div vide là où la recherche d’adresse doit apparaître (paiement, facturation, contact). Mettez votre clé aica_live_ dans data-api-key. Le chargeur de l’étape 1 trouve automatiquement chaque div data-aica-autocomplete.
Mise en page checkout complète avec l’installation recommandée (loader + montage + champs autofill). Affichez le code coloré ou téléchargez le fichier.
Après la sélection finale, AICA peut remplir vos champs existants comme si le visiteur avait saisi l’adresse. Aucun mapping JSON requis. Utilisez data-aica-field ou des alias name/id.
Jetons de champs supportés
result / address / full_address
street_line / street / address1
building_line / building
unit / apt / suite
city
postal_code / postal / zip
province / provice_abbr / state
addr_id
lat / lon
city_prov
Le remplissage écrit dans les champs — il ne sert pas à la recherche. Les sites avancés peuvent toujours utiliser le JSON onSelect.
Langue du widget
Le texte indicatif et le compte d’unités suivent d’abord la page hôte : définissez ou (fr-CA, en-US, etc. sont reconnus). Si la page ne déclare pas de langue, le widget utilise la langue du navigateur du visiteur (navigator.languages). Sinon, l’anglais. Bonne pratique : toujours indiquer lang sur la racine du document pour que tous les visiteurs de ce site voient le même texte.
Confidentialité du paramètre URL
Par défaut le widget écrit aussi ?aica_selection= (JSON en base64) dans l’URL. Cette valeur contient des données d’adresse — évitez de partager ces liens, ou effacez le paramètre après enregistrement.
Dépannage
Si le widget s’affiche sans CSS, que la boîte est vide, ou que la saisie n’appelle jamais l’API, corrigez d’abord votre site :
Utilisez une balise <script src="…"> (ou aica-loader.js). N’iframez pas, n’utilisez pas <embed> ni <object> sur nos pages. Le HTML AICA envoie X-Frame-Options: DENY : un iframe restera blanc.
Votre Content-Security-Policy, WAF ou liste de domaines autorisés doit permettre à la page de communiquer avec https://aica-apis.quebecstore.ca — scripts, CSS, images et appels API (script-src, style-src, img-src et connect-src). Bloquer cet hôte = pas de styles et pas d’autocomplétion.
Dans le tableau de bord AICA, ajoutez le domaine exact de la page qui héberge le widget sur cette clé widget (example.com et www.example.com si vous utilisez les deux).
N'exposez jamais votre clé API dans du HTML ou du JavaScript navigateur. Choisissez votre stack ci-dessous — chaque chemin garde la clé côté serveur et pointe le widget vers votre proxy.
Les appels API directement côté serveur (sans proxy accessible au navigateur) sont les plus sécurisés : aucune URL publique à abuser. Utilisez un proxy widget seulement pour l'autocomplétion front-end.
Configuration PHP
1. aica-config.php
Placez-le à côté de aica-proxy.php (ex. public_html/api/). Définissez api_key et site_origin (https://votredomaine.com). Dans le tableau de bord, ajoutez seulement votredomaine.com — www est ajouté automatiquement.
<?php
declare(strict_types=1);
/**
* AICA API credentials — server-side only. Never expose in HTML or JavaScript.
*
* WHERE TO PUT THIS FILE
* ----------------------
* Recommended: same folder as aica-proxy.php
* your-site/api/aica-config.php
* your-site/api/aica-proxy.php
*
* Also works (proxy searches automatically):
* your-site/aica-config.php
* your-site/config/aica-config.php
* or set env AICA_CONFIG_PATH=/full/path/to/aica-config.php
*
* WIDGET HTML (any page on your site)
* -----------------------------------
* <div id="aica-address" data-api-base="/api/aica-proxy.php"></div>
*
* The browser JS (hosted by AICA) reads data-api-base and calls:
* /api/aica-proxy.php?search=...
* /api/aica-proxy.php/units?parent_id=...
* /api/aica-proxy.php/session-complete
*
* site_origin — your public site URL. The proxy sends this to AICA (never the
* browser Origin header) so callers cannot spoof your domain.
*/
return [
'api_key' => 'class="tok-key">aica_live_REPLACE_WITH_YOUR_KEY',
'api_base' => 'class="tok-url">https://aica-apis.quebecstore.ca/v1/address/autocomplete/canada',
'site_origin' => 'class="tok-url">https://example.com',
];
2. aica-proxy.php
Le JS du widget appelle ce fichier via data-api-base. Il charge la config dans le même dossier et transmet les requêtes à AICA avec votre clé et un site_origin fixe.
<?php
declare(strict_types=1);
/**
* AICA Address Autocomplete — server-side proxy for the embed widget.
*
* UPLOAD
* ------
* Put this file on your PHP host, e.g. public_html/api/aica-proxy.php
* Put aica-config.php in the same folder (or see loadAicaConfig() below).
*
* WIDGET → THIS FILE (browser, from your HTML)
* --------------------------------------------
* Your page includes:
* <link href="class="tok-url">https://aica-apis.quebecstore.ca/.../aica-autocomplete.css" />
* <div id="aica-address" data-api-base="/api/aica-proxy.php"></div>
* <script src="class="tok-url">https://aica-apis.quebecstore.ca/.../aica-autocomplete.js"></script>
* <script>AicaAutocomplete.mount('#aica-address');</script>
*
* The widget JS calls data-api-base + query string, for example:
* GET /api/aica-proxy.php?search=657
* GET /api/aica-proxy.php/units?parent_id=...
* GET /api/aica-proxy.php/session-complete
*
* Change data-api-base to match where YOU uploaded this file.
* Examples:
* /api/aica-proxy.php
* /shop/api/aica-proxy.php
* class="tok-url">https://yourdomain.com/api/aica-proxy.php
*
* THIS FILE → aica-config.php (server only)
* -----------------------------------------
* Loads your API key from aica-config.php (never sent to the browser).
*
* DOMAIN TIP
* ----------
* In the AICA dashboard add only example.com — www.example.com is added automatically.
*
* SECURITY
* --------
* Set site_origin in aica-config.php. This proxy sends that fixed Origin to AICA
* and ignores the browser Origin header (prevents domain spoofing).
*/
/**
* @returnarray{api_key: string, api_base: string, site_origin?: string}
*/
function loadAicaConfig(): array
{
$candidates = array_filter([
getenv('AICA_CONFIG_PATH') ?: null,
__DIR__ . '/aica-config.php',
dirname(__DIR__) . '/aica-config.php',
dirname(__DIR__) . '/config/aica-config.php',
isset($_SERVER['DOCUMENT_ROOT']) ? rtrim((string) $_SERVER['DOCUMENT_ROOT'], '/') . '/config/aica-config.php' : null,
]);
foreach ($candidatesas$path) {
if (is_file($path)) {
$loaded = require $path;
if (is_array($loaded)) {
return$loaded;
}
}
}
return [
'api_key' => getenv('AICA_API_KEY') ?: '',
'api_base' => getenv('AICA_API_BASE') ?: 'class="tok-url">https://aica-apis.quebecstore.ca/v1/address/autocomplete/canada',
];
}
$config = loadAicaConfig();
$apiKey = (string) ($config['api_key'] ?? '');
$apiBase = rtrim((string) ($config['api_base'] ?? ''), '/');
$siteOrigin = rtrim((string) ($config['site_origin'] ?? ''), '/');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Origin: ' . ($_SERVER['HTTP_ORIGIN'] ?? '*'));
header('Access-Control-Allow-Headers: X-AICA-Session-Id, X-AICA-Interaction-Complete, Content-Type');
header('Access-Control-Allow-Methods: GET, OPTIONS');
http_response_code(204);
exit;
}
if ($apiKey === '' || str_contains($apiKey, 'REPLACE_WITH_YOUR_KEY')) {
http_response_code(503);
header('Content-Type: application/json');
echo json_encode(['error' => 'Proxy not configured — edit api_key in aica-config.php next to this file']);
exit;
}
if ($siteOrigin === '' || str_contains($siteOrigin, 'example.com')) {
http_response_code(503);
header('Content-Type: application/json');
echo json_encode(['error' => 'Proxy not configured — set site_origin in aica-config.php to your site URL']);
exit;
}
$uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
$script = $_SERVER['SCRIPT_NAME'] ?? '';
$suffix = '';
if ($script !== '' && str_contains($uri, $script)) {
$suffix = substr($uri, strpos($uri, $script) + strlen($script));
}
$suffix = ($suffix === '' || $suffix === '/') ? '' : $suffix;
$allowed = ['', '/session-complete', '/units'];
if (!in_array($suffix, $allowed, true) && !str_starts_with($suffix, '/units')) {
http_response_code(404);
header('Content-Type: application/json');
echo json_encode(['error' => 'Proxy path not found']);
exit;
}
$query = $_SERVER['QUERY_STRING'] ?? '';
$target = $apiBase . $suffix . ($query !== '' ? '?' . $query : '');
$headers = [
'Accept: application/json',
'X-AICA-API-Key: ' . $apiKey,
'Origin: ' . $siteOrigin,
];
foreach (['HTTP_X_AICA_SESSION_ID', 'HTTP_X_AICA_INTERACTION_COMPLETE'] as$h) {
if (!empty($_SERVER[$h])) {
$name = str_replace(' ', '-', ucwords(strtolower(str_replace('HTTP_', '', $h)), '_'));
$headers[] = $name . ': ' . $_SERVER[$h];
}
}
$ch = curl_init($target);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30,
CURLOPT_FOLLOWLOCATION => false,
]);
$body = curl_exec($ch);
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
http_response_code($status > 0 ? $status : 502);
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: ' . ($_SERVER['HTTP_ORIGIN'] ?? '*'));
header('Access-Control-Allow-Headers: X-AICA-Session-Id, X-AICA-Interaction-Complete, Content-Type');
echo$body === false ? '{}' : $body;
3. Protéger la config (Apache)
.htaccess optionnel pour empêcher le téléchargement HTTP de aica-config.php.
# .htaccess in api/ — block direct HTTP access to config
<Files "aica-config.php">
Require all denied
</Files>
Pour l'écosystème Microsoft (.NET 6+). Même HTML widget sur une page .cshtml ; data-api-base vers /api/aica-proxy. Les sites .aspx legacy peuvent utiliser ASP.NET Core ou un reverse-proxy vers PHP.
// ASP.NET Core minimal API proxy — same role as api/aica-proxy.php (IIS / Azure / Windows hosts).
// Add to Program.cs. Store secrets in appsettings or environment variables.
//
// Widget HTML (any .cshtml / .aspx page):
// <divid="aica-address"data-api-base="/api/aica-proxy"></div>
//
// Env / appsettings:
// AICA:ApiKey, AICA:ApiBase, AICA:SiteOrigin
using System.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var apiKey = builder.Configuration["AICA:ApiKey"] ?? "";
var apiBase = (builder.Configuration["AICA:ApiBase"] ?? "https://aica-apis.quebecstore.ca/v1/address/autocomplete/canada").TrimEnd('/');
var siteOrigin = (builder.Configuration["AICA:SiteOrigin"] ?? "").TrimEnd('/');
app.MapGet("/api/aica-proxy/{**path}", async (HttpContext ctx, string? path) =>
{
if (string.IsNullOrEmpty(apiKey))
return Results.Json(new { error = "Proxy not configured" }, statusCode: 503);
if (string.IsNullOrEmpty(siteOrigin))
return Results.Json(new { error = "Set AICA:SiteOrigin" }, statusCode: 503);
var suffix = string.IsNullOrEmpty(path) ? "" : "/" + path.TrimStart('/');
var allowed = new[] { "", "/session-complete", "/units" };
if (!allowed.Contains(suffix) && !suffix.StartsWith("/units", StringComparison.Ordinal))
return Results.Json(new { error = "Not found" }, statusCode: 404);
var query = ctx.Request.QueryString.HasValue ? ctx.Request.QueryString.Value : "";
var target = $"{apiBase}{suffix}{query}";
using var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Get, target);
req.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
req.Headers.Add("X-AICA-API-Key", apiKey);
req.Headers.Add("Origin", siteOrigin);
if (ctx.Request.Headers.TryGetValue("X-AICA-Session-Id", out var sid))
req.Headers.Add("X-AICA-Session-Id", sid.ToString());
if (ctx.Request.Headers.TryGetValue("X-AICA-Interaction-Complete", out var done))
req.Headers.Add("X-AICA-Interaction-Complete", done.ToString());
var res = await client.SendAsync(req);
var body = await res.Content.ReadAsStringAsync();
return Results.Content(body, "application/json", statusCode: (int)res.StatusCode);
});
app.Run();
Pointer le widget vers votre proxy
<%-- .aspx / Razor: same widget markup --%>
<divid="address-field"data-api-base="/api/aica-proxy"></div><scriptsrc="https://aica-apis.quebecstore.ca/v1/address/autocomplete/canada/interactive/aica-autocomplete.js"></script><script>AicaAutocomplete.mount("#address-field");</script>
cURL / HTTP brut
Appels HTTPS bruts depuis n'importe quel langage. N'intégrez jamais la clé dans le code front-end.
URL standard qui renvoie du JSON si elle est correctement appelée (clé serveur via ?api_key= ou en-tête) :
GET /v1/address/autocomplete?search={query}&api_key={key}
Alias avec en-tête (widget / proxy / cURL avec X-AICA-API-Key) :
GET /v1/address/autocomplete/canada?search={query}
GET
Unités d'immeuble
Utilisez l'addr_id du bâtiment retourné par l'autocomplétion. Les deux noms de paramètre fonctionnent :
Principal (route autocomplete)
GET /v1/address/autocomplete/canada?complex-search={building_addr_id}
Alias (route units dédiée)
GET /v1/address/autocomplete/canada/units?parent_id={building_addr_id}
GET
Validation d'adresse
Validez une adresse canadienne (style Postes Canada ou partielle) et obtenez la meilleure correspondance officielle avec un score de confiance. Préférez l'auth par en-tête; ?api_key= est réservé à la doc / aux tests.
GET /v1/address/validate?address={address}
URL standard qui renvoie du JSON si elle est correctement appelée (clé serveur via ?api_key= ou en-tête) :
GET /v1/address/validate?address={address}&api_key={key}
Paramètres : address (requis). Accepte les codes postaux avec ou sans espace (G0W 2H0 / G0W2H0), sans virgules, et les types de rue abrégés.
GET
Géocodage inverse
Trouvez l'adresse de bâtiment canadienne la plus proche à partir de la latitude et de la longitude. Les appartements sont exclus — le bâtiment est retourné.
GET /v1/address/reverse-geocode?lat={lat}&lon={lon}
URL standard qui renvoie du JSON si elle est correctement appelée (clé serveur via ?api_key= ou en-tête) :
GET /v1/address/reverse-geocode?lat={lat}&lon={lon}&api_key={key}
Paramètres : lat et lon (requis, pleine précision — ne pas arrondir). Recherche 50 m → 100 m → 250 m. Correspondance exacte = 1 résultat; sinon jusqu'à 3 plus proches.
POST
Fin de session
POST /v1/address/autocomplete/canada/session-complete
Réponses
Pendant la saisie, GET ?search=… renvoie des suggestions :
Quand le visiteur choisit une adresse, votre callback onSelect reçoit l'enregistrement complet (le JSON n'est pas affiché dans l'interface du widget) :
{
"result": "1001-1080 BAY ST, TORONTO, ON, M5S 0A5",
"other_meta": {
"addr_id": "f6aefa64-f190-4b67-a1f0-3a2c39303dcb",
"is_address_building": false,
"unit_count": 0,
"unit": "1001",
"building_line": "1080 BAY ST",
"relation_complex": "c45f2597-88a2-4b3d-bfa8-73541f98a845",
"full_addr": "1001-1080 BAY ST",
"street_line": "1001-1080 BAY ST",
"mail_line": "TORONTO ON M5S 0A5",
"city": "TORONTO",
"pruid": 35,
"postal_code": "M5S 0A5",
"provice": "Ontario",
"provice_abbr": "ON",
"location": {
"lat": 43.666825,
"lon": -79.388364
},
"display_line": "1001-1080 BAY ST, TORONTO, ON, M5S 0A5"
}
}
Réponse de validation
Quand la confiance est élevée et qu'une seule adresse domine clairement :
{
"status": "ok",
"service": "AICA Address Autocomplete",
"message": "Hi there, if you are seeing this it means your API request was misconfigured...",
"documentation": "https://aica-apis.quebecstore.ca/aica-adresse-auto-complete/documentation",
"instruction": "Use GET /v1/address/autocomplete/canada?search=YOUR_QUERY...",
"autocomplete": "/v1/address/autocomplete/canada?search=",
"note": "Service is reachable. See documentation for authentication...",
"error_reason": "misconfigured api-call",
"error_code": "AICA-E001"
}
Les routes incorrectes ou mal configurées renvoient un JSON qui inclut l’état du service (status, service, note) ainsi que error_code — pas de page santé séparée.
{
"error": "The API key is invalid or unknown.",
"error_reason": "invalid or unknown api-key",
"error_code": "AICA-E003"
}
L'utilisation est facturée par session d'interaction, pas par requête HTTP.
Réinitialisation du quota : le 1er de chaque mois à minuit, heure du Québec.
Journaux d'utilisation
Chaque requête API est enregistrée dans votre tableau de bord, sous Utilisation API. Deux champs aident à comprendre ce qui s'est passé :
Recherche — Recherche — ce que l'utilisateur a tapé (ex. h0h 0h0).
Résultats / Réponse — Résultats / Réponse — un résumé court de ce qui a été retourné.
Quand l'utilisateur choisit une adresse finale, le journal affiche une ligne lisible, par ex. PÈRE NOËL, PÔLE NORD, H0H 0H0.
LONG RESPONSE signifie que l'API a retourné plus d'une adresse dans une même réponse. Nous n'enregistrons pas la liste JSON complète — seulement le fait que plusieurs résultats ont été retournés. Cela inclut les immeubles à logements multiples; l'appartement choisi n'apparaît qu'après la sélection de l'unité.
Dans le tableau de bord, les aperçus longs et les lignes LONG RESPONSE affichent un bouton Voir. Cliquez pour ouvrir un panneau centré avec l’aperçu stocké (et une explication pour LONG RESPONSE).
Quand une seule adresse est retournée et que l'utilisateur n'a pas encore terminé, un aperçu sur une ligne peut s'afficher.
Nous utilisons des témoins (cookies) pour améliorer votre expérience. En utilisant notre site, vous acceptez notre utilisation des témoins. En savoir plus dans notre Politique des témoins