Établissements

Endpoints des établissements, par ordre d’usage : exemples cURL, Python, Node ; liste, recherche, ID/code magasin, création, mise à jour, archivage, photos

Les fiches, les avis, les publications et les données d’analyse sont tous associés à un identifiant d’établissement. La ressource des établissements comporte treize endpoints : quatre en lecture pour trouver les établissements, quatre en écriture pour les créer, les modifier et les archiver, et cinq pour gérer les photos. Une clé Read (lecture) permet les opérations de lecture. Chaque POST nécessite une clé Write (écriture) ; l’utilisation d’une clé Read pour une opération d’écriture renvoie le statut HTTP 400 avec SY90016.

Chaque identifiant de ce guide est un identifiant Relay en base64, par exemple TG9jYXRpb246MTgwMDI4OQ== (qui donne Location:1800289 une fois décodé). Les deux SDK acceptent également le databaseId numérique et l’encodent pour vous.

Les endpoints en un coup d’œil

MéthodeCheminFonctionRéférence
GET/locationsParcourir tous les établissements par pages, du plus récent au plus ancien.Lister tous les établissements
GET/locations/searchRechercher des établissements par mot-clé dans le nom, l’adresse et l’identifiant du magasin.Rechercher des établissements
GET/locations-by-idsRécupérer les enregistrements complets correspondant à un lot d’identifiants en base64.Récupérer des établissements par identifiants
GET/locations-by-store-codesRécupérer les enregistrements complets correspondant à vos propres codes de magasin.Récupérer des établissements par codes de magasin
POST/locationsCréer un établissement et le mettre en file d’attente pour soumission.Créer un établissement
POST/locations/updateModifier n’importe quel champ accessible en écriture d’un établissement.Mettre à jour un établissement
POST/locations/archivePlanifier l’archivage d’établissements à la fin du cycle de facturation.Archiver des établissements
POST/locations/cancel_archiveRétablir le statut actif des établissements dont l’archivage est planifié.Annuler un archivage planifié
POST/locations/photosJoindre un logo, une photo de couverture ou des photos de galerie par URL.Importer des photos
GET/locations/photos/requests/{requestId}Vérifier périodiquement l’état d’un import groupé en file d’attente.Vérifier l’état d’un import groupé
GET/locations/{locationId}/photosLister tous les fichiers multimédias d’un établissement.Récupérer les photos
POST/locations/photos/starMarquer des photos supplémentaires d’une étoile ou retirer cette étoile.Marquer des photos d’une étoile
POST/locations/photos/removeDétacher des photos supplémentaires.Retirer des photos

Lister tous les établissements

GET /locations permet de parcourir tous les établissements du compte par pages, du plus récent au plus ancien. Il s’agit d’une connexion paginée par curseur : edges[].node contient chaque enregistrement, et pageInfo.endCursor fournit la valeur du paramètre after pour la page suivante. Utilisez cet endpoint pour synchroniser l’ensemble du compte avec votre propre système.

Paramètres

NomEmplacementObligatoireRemarques
firstqueryNonTaille de page pour parcourir les résultats vers l’avant. Valeur par défaut : 50. À combiner avec after.
afterqueryNonLe pageInfo.endCursor de la page précédente.
lastqueryNonTaille de page pour parcourir les résultats vers l’arrière. À combiner avec before.
beforequeryNonLe cursor de la première arête de la page depuis laquelle vous revenez en arrière. pageInfo ne possède pas de startCursor.
filterqueryNonObjet encodé en JSON pour restreindre l’ensemble des résultats côté serveur, par exemple selon le statut d’approbation. Aucune des méthodes de liste des deux SDK ne l’accepte ; appelez REST pour obtenir un sous-ensemble filtré.
List
curl "https://listingsapi.com/api/v4/locations?first=50" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "allLocations": {      "edges": [        {          "cursor": "TG9jYXRpb246MTgwMDI4OQ==",          "node": {            "id": "TG9jYXRpb246MTgwMDI4OQ==",            "databaseId": 1800289,            "name": "Jenny Home",            "storeId": null,            "approved": "APPROVED",            "archived": false,            "archivalScheduledAt": null,            "city": "Austin",            "stateIso": "TX",            "countryIso": "US",            "subCategoryId": 1432,            "planName": "Launch",            "placeActionLinks": [],            "discoveredPlaceActionLinks": []          }        }      ],      "pageInfo": {        "hasNextPage": false,        "hasPreviousPage": false,        "endCursor": "TG9jYXRpb246MTgwMDI4OQ==",        "total": 1      }    }  }}

Consultez pageInfo.hasNextPage et arrêtez-vous lorsque sa valeur est false. Le nombre d’éléments de la page est indiqué par pageInfo.total. Le id du nœud est l’identifiant attendu par tous les autres endpoints.

Rechercher des établissements

GET /locations/search recherche un mot-clé dans le nom de l’établissement, l’adresse postale, la ville et l’identifiant du magasin. Il s’agit d’une connexion paginée par curseur dont les structures edges[].node et pageInfo.endCursor sont identiques à celles de la liste. Utilisez cet endpoint pour un sélecteur avec suggestions à la saisie, ou pour vérifier qu’un établissement existe avant de le créer.

Paramètres

NomEmplacementObligatoireRemarques
queryqueryNonLe mot-clé de recherche. Omettez-le pour parcourir tous les établissements par pages sans filtrage.
firstqueryNonTaille de page. Valeur par défaut : 50.
afterqueryNonLe pageInfo.endCursor de la page précédente.
filterqueryNonObjet de filtrage encodé en JSON, comme pour l’endpoint de liste.

Les deux SDK acceptent également une liste fields pour limiter la recherche à des champs précis, par exemple uniquement name.

Search
curl "https://listingsapi.com/api/v4/locations/search?query=Jenny&first=10" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "searchLocations": {      "edges": [        {          "cursor": "TG9jYXRpb246MTgwMDI4OQ==",          "node": {            "id": "TG9jYXRpb246MTgwMDI4OQ==",            "databaseId": 1800289,            "name": "Jenny Home",            "storeId": null,            "street": "600 Congress Ave, Suite 200",            "city": "Austin",            "approved": "APPROVED",            "archived": false          }        }      ],      "pageInfo": {        "endCursor": "TG9jYXRpb246MTgwMDI4OQ==",        "hasNextPage": false,        "hasPreviousPage": false,        "total": 1      }    }  }}

Un tableau edges vide signifie qu’aucune correspondance n’a été trouvée. Le nœud contient l’adresse complète, la catégorie et le statut approved ; une ligne de résultat ne nécessite donc aucune seconde récupération.

Récupérer des établissements par leurs identifiants

GET /locations-by-ids renvoie l'enregistrement complet de chaque établissement d'un lot d'identifiants base64 en un seul appel, ce qui utilise moins de requêtes qu'un appel par établissement. Les établissements actifs comme les établissements archivés sont retrouvés. Le résultat est un simple tableau, pas une connexion. Utilisez cet appel pour récupérer les enregistrements correspondant aux identifiants provenant de la charge utile d'un webhook ou de votre propre stockage.

Paramètres

NomEmplacementObligatoireRemarques
idsqueryOuiTableau d'identifiants base64 encodé en JSON. Encodez toute la valeur pour une URL ; curl -G --data-urlencode le fait pour vous.
By ids
curl -G https://listingsapi.com/api/v4/locations-by-ids \--data-urlencode 'ids=["TG9jYXRpb246MTgwMDI4OQ==","TG9jYXRpb246MTgwMDI5MA=="]' \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "getLocationsByIds": [      {        "id": "TG9jYXRpb246MTgwMDI4OQ==",        "databaseId": 1800289,        "storeId": null,        "name": "Jenny Home",        "archived": false,        "archivedAt": null,        "city": "Austin",        "stateIso": "TX",        "countryIso": "US",        "subCategoryId": 1432,        "businessHours": [],        "locationPhotos": [],        "placeActionLinks": [],        "discoveredPlaceActionLinks": []      }    ]  }}

Les identifiants qui n'existent pas dans votre compte sont ignorés silencieusement au lieu de provoquer une erreur. Comparez le nombre d'enregistrements renvoyés au nombre d'identifiants envoyés pour détecter les identifiants obsolètes.

Récupérer des établissements par leurs codes magasin

GET /locations-by-store-codes effectue la recherche inverse : il retrouve les enregistrements complets à partir de vos propres codes magasin. Il renvoie un simple tableau. Utilisez-le lorsque votre système de point de vente ou votre registre de franchises identifie les établissements par leur code magasin et que vous n'avez jamais enregistré nos identifiants.

Paramètres

NomEmplacementObligatoireRemarques
storeCodesqueryOuiTableau de codes magasin sous forme de chaînes de caractères, encodé en JSON. Encodez toute la valeur pour une URL.
By store codes
curl -G https://listingsapi.com/api/v4/locations-by-store-codes \--data-urlencode 'storeCodes=["ACME01","ACME02"]' \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "getLocationsByStoreCodes": [      {        "id": "TG9jYXRpb246MTgwMDI4OQ==",        "databaseId": 1800289,        "storeId": "ACME01",        "name": "Jenny Home",        "archived": false,        "city": "Austin",        "countryIso": "US",        "planName": "Launch"      }    ]  }}

Faites correspondre le champ storeId de chaque objet à une entrée de votre registre. Les codes sans correspondance sont absents. Un établissement ne possède un code magasin que si storeId a été défini lors de sa création ou de sa mise à jour.

Créer un établissement

POST /locations crée un établissement et le place dans la file d'attente pour soumission à chaque plateforme de publication de votre forfait, ou uniquement à enabledSiteIds lorsque vous transmettez ce champ. Tous les champs doivent être placés sous input. 10 champs sont obligatoires ; les autres sont facultatifs, et les plateformes de publication affichent comme vides ceux que vous omettez.

Paramètres

NomEmplacementObligatoireRemarques
input.namebodyOuiNom de l'entreprise, de 2 à 150 caractères.
input.descriptionbodyOuiAu moins 200 caractères (SY10005). Les plateformes de publication l'utilisent comme texte de la fiche.
input.countryIsobodyOuiUn code pays pris en charge (SY10036). À récupérer dans pays.
input.citybodyOuiObligatoire lorsque le champ hasCity du pays vaut true (SY10126). Lorsqu'il vaut false, toute ville transmise est ignorée.
input.subCategoryIdbodyOuidatabaseId numérique provenant de sous-catégories.
input.streetbodyOuiDe 5 à 100 caractères (SY10011).
input.stateIsobodyOuiObligatoire lorsque le champ stateRequired du pays vaut true, et doit être l'une des valeurs de states de ce pays (SY10010). Omettez-le lorsque stateRequired vaut false.
input.phonebodyOuiValidé en fonction du pays (SY10015).
input.postalCodebodyNonFacultatif, mais transmettez-le chaque fois que le champ hasPostalCode du pays vaut true. Toute valeur transmise doit respecter le format du pays (SY10014).
input.bizUrlbodyOuiPublié en tant que site web. Certaines plateformes de publication refusent une fiche qui n'en comporte pas.
input.storeIdbodyNonVotre propre code magasin, unique à l'échelle du compte.
input.businessHoursbodyNon7 entrées de { day, type, slots }. type vaut OPEN ou CLOSED ; pour un jour de fermeture, slots est vide.
input.placeActionLinksbodyNonLiens d'action Google : placeActionType, uri, et éventuellement isPreferred. Aucun action n'est nécessaire lors de la création. Consultez Liens d'action d'établissement.
input.enabledSiteIdsbodyNonPubliez sur un sous-ensemble des sites de votre forfait. Identifiants provenant de sites du forfait.
input.clientMutationIdbodyNonRenvoyé sans modification pour permettre la corrélation. N'empêche pas les écritures en double.

S'y ajoutent les champs facultatifs répertoriés sur la page de référence : catégories GCID Google, URL des réseaux sociaux, coordonnées géographiques, coordonnées du propriétaire, moyens de paiement, zone desservie, etc.

Create
curl -X POST https://listingsapi.com/api/v4/locations \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "name": "Acme Kitchen",    "description": "Acme Kitchen is a family-owned neighborhood restaurant in downtown New York serving wood-fired pizza, hand-rolled pasta, and a short seasonal menu built around produce from nearby growers. Our dining room seats forty, takeout and delivery run until close, and the bar pours regional wine and beer on tap.",    "storeId": "ACME01",    "street": "123 Jump Street",    "city": "New York",    "stateIso": "NY",    "postalCode": "10013",    "countryIso": "US",    "phone": "6443859313",    "bizUrl": "https://acmekitchen.example.com",    "subCategoryId": 1432,    "businessHours": [      { "day": "MONDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "TUESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "WEDNESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "THURSDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "FRIDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "SATURDAY", "type": "OPEN", "slots": [{ "start": "10:00am", "end": "02:00pm" }] },      { "day": "SUNDAY", "type": "CLOSED", "slots": [] }    ],    "placeActionLinks": [      { "placeActionType": "FOOD_ORDERING", "uri": "https://order.acmekitchen.example.com", "isPreferred": true }    ]  }}'
JSON
{  "data": {    "createLocation": {      "clientMutationId": null,      "success": true,      "errors": null,      "location": {        "id": "TG9jYXRpb246MTgwMDI5MA==",        "databaseId": 1800290,        "storeId": "ACME01",        "name": "Acme Kitchen",        "city": "New York",        "countryIso": "US",        "status": "PENDING",        "placeActionLinks": [          { "placeActionType": "FOOD_ORDERING", "uri": "https://order.acmekitchen.example.com", "isPreferred": true, "name": null, "submissionStatus": null }        ],        "discoveredPlaceActionLinks": []      }    }  }}

Conservez les 2 identifiants : id en base64 est nécessaire pour toutes les écritures ultérieures, et databaseId est le numéro affiché dans les tableaux de bord et les webhooks. status vaut initialement PENDING et évolue sans appel API supplémentaire. Les champs name et submissionStatus d'un lien d'action d'établissement restent à null jusqu'à sa synchronisation avec Google. En Python, locations.add() valide les champs obligatoires localement et lève ValidationError avant tout appel réseau ; locations.create(input) accepte à la place un dictionnaire camelCase brut.

Mettre à jour un établissement

POST /locations/update effectue une mise à jour partielle : input.id est le seul champ obligatoire et seuls les champs que vous envoyez sont modifiés. Le même appel permet de corriger un numéro de téléphone ou de réécrire les horaires hebdomadaires. locations.update() en Python et updateLocation() en Node acceptent tous deux un seul objet camelCase.

Paramètres

NomDansObligatoireRemarques
input.idbodyOuiL'identifiant base64 de l'établissement. Le champ est id, et non locationId.
input.phone, input.bizUrl, input.name, champs d'adressebodyNonTout champ modifiable de l'appel de création.
input.businessHoursbodyNonEnvoyez les sept jours ; une semaine partielle est rejetée. Pour les dérogations liées aux jours fériés, ajoutez des entrées day: "SPECIAL" avec un specialDate, en complément de la semaine, jamais à sa place.
input.moreHoursbodyNonHoraires secondaires, par exemple pour la livraison, chacun au format { hoursTypeId, moreHour }. Aucun specialDate ici.
input.temporarilyClosedbodyNontrue interrompt la publication des horaires pendant la fermeture de l'établissement.
input.primaryGbpSiteCategoryId, input.additionalGbpSiteCategoryIdsbodyNonCatégories Google sous forme de GCID, par exemple gcid:restaurant. Envoyez countryIso avec celles-ci.
input.placeActionLinksbodyNonAjout ou insertion avec mise à jour : chaque élément accepte action avec la valeur add (par défaut) ou delete. Les liens que vous ne mentionnez pas restent inchangés.
input.submissionDisabledSiteIdsbodyNonIdentifiants des sites à exclure de la soumission.

Ainsi que les champs facultatifs répertoriés sur la page de référence.

Update
curl -X POST https://listingsapi.com/api/v4/locations/update \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "id": "TG9jYXRpb246MTgwMDI4OQ==",    "phone": "5125550199",    "bizUrl": "https://acmekitchen.example.com",    "primaryGbpSiteCategoryId": "gcid:restaurant",    "businessHours": [      { "day": "MONDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "TUESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "WEDNESDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "THURSDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "FRIDAY", "type": "OPEN", "slots": [{ "start": "09:00am", "end": "05:00pm" }] },      { "day": "SATURDAY", "type": "OPEN", "slots": [{ "start": "10:00am", "end": "02:00pm" }] },      { "day": "SUNDAY", "type": "CLOSED", "slots": [] }    ]  }}'
JSON
{  "data": {    "updateLocation": {      "clientMutationId": null,      "success": true,      "errors": null,      "location": {        "id": "TG9jYXRpb246MTgwMDI4OQ==",        "databaseId": 1800289,        "name": "Jenny Home",        "phone": "5125550199",        "status": "APPROVED",        "placeActionLinks": [          { "placeActionType": "FOOD_ORDERING", "uri": "https://order.example.com", "isPreferred": true, "name": null, "submissionStatus": null }        ],        "discoveredPlaceActionLinks": []      }    }  }}

La réponse renvoie l'établissement mis à jour afin que vous puissiez confirmer l'application des nouvelles valeurs. Chaque mise à jour renvoie l'ensemble complet des placeActionLinks actuels de l'établissement. Pour modifier l'URL d'un lien, envoyez un delete pour l'ancien lien et un add pour le nouveau dans la même requête.

Archiver des établissements

POST /locations/archive programme l'archivage d'un ou de plusieurs établissements. L'archivage n'est pas immédiat : chaque établissement passe à l'état ARCHIVE_SCHEDULED, reste actif et facturé jusqu'à la fin du cycle de facturation en cours, puis est archivé lors de la génération de la facture suivante. Utilisez cet appel pour retirer par lot les établissements fermés.

Paramètres

NomDansObligatoireRemarques
input.locationIdsbodyOuiTableau des identifiants base64 des établissements à archiver.
input.clientMutationIdbodyNonRenvoyé sans modification. N'empêche pas les écritures en double.
Archive
curl -X POST https://listingsapi.com/api/v4/locations/archive \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ==", "TG9jYXRpb246MTgwMDI5MA=="]  }}'
JSON
{  "data": {    "archiveLocations": {      "clientMutationId": null,      "bulkEditTaskId": null,      "errors": null,      "success": null,      "result": [        { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null },        { "locationId": "TG9jYXRpb246MTgwMDI5MA==", "status": "ARCHIVE_SCHEDULED", "success": true, "errors": null }      ]    }  }}

Le champ success de premier niveau vaut null ; lisez plutôt le champ success propre à chaque entrée result[], afin qu'un identifiant incorrect ne bloque pas le reste du lot.

result[].statusSignification
ARCHIVE_SCHEDULEDMis en file d'attente. L'établissement reste actif et facturé jusqu'à la fin du cycle de facturation.
ARCHIVEDDéjà archivé lors d'un appel ou d'un cycle antérieur.

Tant que l'archivage est programmé, le champ archivalScheduledAt de l'établissement est défini dans les résultats de liste et de recherche. Un établissement archivé ne peut pas être mis à jour tant qu'il n'a pas été réactivé.

Annuler un archivage programmé

POST /locations/cancel_archive fait repasser les établissements à l'état ARCHIVE_SCHEDULED à l'état ACTIVE, et la facturation se poursuit. Cet appel ne fonctionne que tant que le cycle de facturation est encore ouvert ; une fois un établissement entièrement archivé, il renvoie 404 pour cet établissement.

Paramètres

NomDansObligatoireRemarques
input.locationIdsbodyOuiTableau des identifiants base64 des établissements actuellement à l'état ARCHIVE_SCHEDULED.
input.clientMutationIdbodyNonRenvoyé sans modification.
Cancel archive
curl -X POST https://listingsapi.com/api/v4/locations/cancel_archive \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="] } }'
JSON
{  "data": {    "cancelLocationsArchive": {      "clientMutationId": null,      "bulkEditTaskId": null,      "errors": null,      "success": null,      "result": [        { "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ACTIVE", "success": true, "errors": null }      ]    }  }}

Chaque entrée result[] confirme que l'établissement est revenu à l'état ACTIVE. Les deux SDK exigent deux arguments positionnels supplémentaires, un type de sélection et un identifiant changedBy, qu'ils envoient avec locationIds ; le corps de la requête REST ne nécessite que les identifiants.

Importer des photos dans un établissement

POST /locations/photos joint des images à partir d'une URL publique : vous hébergez l'image, l'API la récupère et la stocke. Un établissement conserve un LOGO, un COVER et un nombre quelconque de photos ADDITIONAL. Une nouvelle importation d'un logo ou d'une photo de couverture remplace l'élément existant.

Paramètres

NomDansObligatoireRemarques
input.locationIdbodyOuiL'identifiant base64 de l'établissement.
input.photosbodyOuiTableau d'objets image ; ne doit pas être vide.
input.photos[].photobodyOuiUne URL publique d'image JPEG ou PNG. Le champ est photo, et non url.
input.photos[].typebodyNonLOGO, COVER ou ADDITIONAL. Par défaut, il s'agit d'une photo supplémentaire.
input.photos[].starredbodyNonMarquez la photo d'une étoile dès son importation. Uniquement pour les photos supplémentaires, avec un maximum de 4 par établissement.
input.photos[].sourcebodyNonLibellé en texte libre indiquant la provenance de l'image.
Upload
curl -X POST https://listingsapi.com/api/v4/locations/photos \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "photos": [      { "photo": "https://cdn.example.com/jenny-home/logo.png", "type": "LOGO" },      { "photo": "https://cdn.example.com/jenny-home/storefront.jpg", "type": "ADDITIONAL" }    ]  }}'
JSON
{  "data": {    "addLocationPhotos": {      "clientMutationId": null,      "success": true,      "request": {},      "photos": [        { "id": "TWVkaWFGaWxlOjU4Nzg5MzE=", "databaseId": 5878931, "type": "LOGO", "starred": false,          "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/f47d62a6.png" },        { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "databaseId": 5878932, "type": "ADDITIONAL", "starred": false,          "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/ef4ffb12.jpg" }      ],      "errors": null    }  }}

Conservez le id de chaque photo ; les points de terminaison permettant de marquer une photo d'une étoile et de la supprimer l'utilisent. Il s'agit du format de réponse directe pour un petit lot. Un lot volumineux est en revanche mis en file d'attente : photos est renvoyé vide et request.requestId est défini, et vous interrogez périodiquement le point de terminaison suivant avec cet identifiant. Le seuil est configuré côté serveur ; adaptez donc le traitement selon la présence de request.requestId.

Vérifier l’état d’un envoi groupé de photos

GET /locations/photos/requests/{requestId} interroge régulièrement l’état d’un envoi en file d’attente jusqu’à ce qu’il atteigne un état définitif, puis indique le résultat pour chaque image. Utilisez-le pour alimenter une barre de progression et afficher précisément les fichiers rejetés et les raisons de leur rejet.

Paramètres

NomDansObligatoireRemarques
requestIdpathOuiL’UUID fourni dans request.requestId dans la réponse d’un envoi mis en file d’attente. Interrogez uniquement les identifiants renvoyés par un envoi récent.
Upload status
curl https://listingsapi.com/api/v4/locations/photos/requests/e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "getLocationPhotosUploadStatus": {      "requestId": "e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a",      "status": "SUCCESS",      "photos": [        { "mediaId": "TWVkaWFGaWxlOjkxODI3", "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "SUCCESS", "errorMessage": null },        { "mediaId": null, "locationId": "TG9jYXRpb246MTgwMDI4OQ==", "status": "ERROR", "errorMessage": "Image dimensions below the minimum 250x250." }      ]    }  }}
status de la tâcheSignification
PROCESSINGTraitement en cours. Interrogez à nouveau l’état après quelques secondes.
SUCCESSTraitement terminé. Certaines photos peuvent néanmoins avoir échoué ; consultez donc photos[].status et photos[].errorMessage.
ERRORLa tâche elle-même a échoué. Examinez photos[].errorMessage.

L’état de la tâche indique uniquement que le traitement est terminé, et non que toutes les images ont bien été ajoutées. Espacez les interrogations de quelques secondes ; une boucle sans pause risque de vous faire atteindre rapidement 429.

Récupérer les photos d’un établissement

GET /locations/{locationId}/photos renvoie tous les fichiers multimédias d’un établissement sous forme de tableau simple : logo, couverture et images supplémentaires, chacun avec son url public, son thumbnailUrl, sa catégorie, son indicateur starred et son viewsCount cumulé sur toute sa durée de vie. Appelez-le avant un envoi pour voir quel emplacement est vide et recueillir les identifiants nécessaires aux appels d’attribution d’étoiles et de suppression.

Paramètres

NomDansObligatoireRemarques
locationIdpathOuiL’identifiant en base64. La valeur numérique brute de databaseId est également acceptée. Si la valeur ne correspond à aucun de ces deux formats, l’endpoint renvoie HTTP 200 avec SY90002: Invalid Id dans errors.
List photos
curl https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/photos \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "mediaFilesOfLocation": [      { "id": "TWVkaWFGaWxlOjU4Nzg5MzE=", "categoryName": "Logo", "categoryId": "4", "fileType": "image", "starred": false, "viewsCount": 0,        "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/f47d62a6.jpg",        "thumbnailUrl": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/thumbnail_f47d62a6.jpg" },      { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "categoryName": "Additional", "categoryId": "12", "fileType": "image", "starred": true, "viewsCount": 143,        "url": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/ef4ffb12.jpg",        "thumbnailUrl": "https://sy-media-store.s3-us-west-2.amazonaws.com/f868bb3f/59c670a3/thumbnail_ef4ffb12.jpg" }    ]  }}

categoryName vaut Logo, Cover ou Additional. Utilisez thumbnailUrl pour une grille et url pour l’affichage en taille réelle. Pour un établissement sans fichiers multimédias, l’endpoint renvoie un tableau vide, et non une erreur.

Attribuer une étoile aux photos d’un établissement

POST /locations/photos/star marque les photos supplémentaires pour leur donner un placement prioritaire sur les plateformes de publication qui prennent en charge la mise en avant d’images. Seules les photos ADDITIONAL peuvent recevoir une étoile, dans la limite de 4 par établissement. Le retrait d’une étoile fonctionne de la même manière : l’indicateur starred dans le corps de la requête détermine l’action effectuée.

Paramètres

NomDansObligatoireRemarques
input.locationIdbodyOuiL’identifiant en base64 de l’établissement.
input.photoIdsbodyOuiLes identifiants des fichiers multimédias issus de la réponse de récupération des photos ou d’envoi.
input.starredbodyOuitrue pour attribuer une étoile, false pour la retirer. C’est cet indicateur, et non le chemin, qui détermine l’action ; /star et /unstar se comportent de la même manière.
Star
curl -X POST https://listingsapi.com/api/v4/locations/photos/star \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "photoIds": ["TWVkaWFGaWxlOjU4Nzg5MzI="],    "starred": true  }}'
JSON
{  "data": {    "starUnstarLocationPhotos": {      "success": true,      "photos": [        { "id": "TWVkaWFGaWxlOjU4Nzg5MzI=", "databaseId": 5878932, "type": "ADDITIONAL", "starred": true }      ],      "error": null    }  }}

La clé de réponse est starUnstarLocationPhotos pour les deux chemins et contient un seul error, et non un tableau errors. Une cinquième photo avec une étoile fait échouer l’ensemble de l’appel avec SY15012 ; la vérification compte les photos ayant déjà une étoile ainsi que celles de cette requête.

Supprimer des photos d’un établissement

POST /locations/photos/remove dissocie les photos supplémentaires à partir de leur identifiant de fichier multimédia. Utilisez-le pour retirer des images de devanture obsolètes ou annuler un import groupé incorrect. Seules les photos ADDITIONAL peuvent être supprimées ; pour remplacer un logo ou une couverture, envoyez une nouvelle image avec ce type.

Paramètres

NomDansObligatoireRemarques
input.locationIdbodyOuiL’identifiant en base64 de l’établissement.
input.photoIdsbodyOuiLes identifiants des fichiers multimédias à dissocier, issus de la réponse de récupération des photos ou d’envoi.
Remove
curl -X POST https://listingsapi.com/api/v4/locations/photos/remove \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "photoIds": ["TWVkaWFGaWxlOjU4Nzg5MzI="]  }}'
JSON
{  "data": {    "removeLocationPhotos": {      "clientMutationId": null,      "removedCount": 1,      "errors": null    }  }}

removedCount indique le nombre de fichiers effectivement dissociés ; 0 avec errors: null signifie donc qu’aucun de ces identifiants n’était associé à cet établissement. La suppression dissocie le fichier ; elle ne le supprime pas nécessairement de votre bibliothèque multimédia.

Erreurs que vous rencontrerez

CodeSignificationCorrection
SY10005description est absent ou contient moins de 200 caractères lors de la création.Rédigez une description plus complète.
SY10011street est absent ou contient moins de 5 caractères.Envoyez street.
SY10010stateIso est absent ou n’est pas valide pour le pays.Envoyez un code d’État provenant de pays.
SY10036countryIso n’est pas un pays pris en charge.Utilisez un code provenant de pays.
SY10126city est absent pour un pays qui l’exige.Envoyez city.
SY15012Plus de 4 photos avec une étoile pour un établissement.Retirez d’abord l’étoile d’une photo ou retirez starred de l’envoi.
SY90002Identifiant non valide dans le chemin des photos.Transmettez l’identifiant en base64 ou le nombre brut, sans utiliser d’autre format.
SY90016Une clé Read a été utilisée pour une opération d’écriture.Utilisez une clé Write.
HTTP 404Annulation de l’archivage d’un établissement qui n’est plus ARCHIVE_SCHEDULED.L’établissement est entièrement archivé ; utilisez plutôt la procédure de réactivation.
HTTP 429Limite de débit dépassée.Attendez pendant la durée indiquée par Retry-After. Consultez les limites de débit.

Les opérations d’écriture peuvent également renvoyer HTTP 200 avec success: false et les détails dans data.<operation>.errors. La liste complète figure dans les codes d’erreur.

Étapes suivantes