How can we help you?

Popular searches
ExportWorkflowCatalog
HomeTechnical

Receive in real time the creations, modifications and deletions of media and References, instead of polling the API.

A webhook is a URL of your system that Grand Shooting calls when something changes in the account: a media uploaded, approved, retagged, a Reference imported… Your integration reacts within seconds, without spending its API quota checking for news.

The events

A subscription links a URL to a topic:

TopicTriggered when
pictures/createa media is created (upload, export…)
pictures/updatea media is modified (status, tags, new version, Reference, View code…)
pictures/deletea media is deleted
references/createa Reference is created
references/updatea Reference is modified
references/deletea Reference is deleted

Events are generic: there is no "media approved" topic. To process only approved media, subscribe to pictures/update and filter on picturestatus in the message.

Subscribing

From the account administration, Team > API menu, WebHooks section (account administrators), or with the API:

curl -X POST -H "Authorization: Bearer $GS_TOKEN" -H "Content-Type: application/json" \
  -d '{"address":"https://myapp.example/gs/webhook-7f3a9c","topic":"pictures/update"}' \
  https://api.grand-shooting.com/v3/webhook

One subscription = one URL + one topic: to follow several topics, create one subscription per topic (the same URL can be used for all of them). GET /webhook lists the subscriptions of the account, PUT /webhook/{webhook_id} modifies one, DELETE /webhook/{webhook_id} deletes it.

The address is stored in lower case. If your URL contains a token or a case-sensitive path, choose it in lower case.

A new subscription receives its first events within a minute.

The message

Grand Shooting sends a JSON POST. Events are grouped: a message holds up to 100 objects of the same account and the same topic, and directly contains their full state (the same as GET /picture/{id} or GET /reference/{id}):

{
  "account_id": 10229487,
  "topic": "pictures/update",
  "type": "picture",
  "ids": [175, 176],
  "data": {
    "175": { "picture_id": 175, "bench_root_id": 19, "picturestatus": 50, "ref": "SS26-DRESS-110", "…": "…" },
    "176": { "picture_id": 176, "bench_root_id": 19, "picturestatus": 40, "ref": "SS26-DRESS-110", "…": "…" }
  }
}
  • type is picture or reference; ids lists the objects concerned.
  • data gives the state of each object when the message is sent (so it can be more recent than the event itself). For a deleted or unreadable object, the value is null.
  • The picture_id (or reference_id) field, a copy of ids, is deprecated and will be removed: use ids.

Delivery and retries

  • Events are sent every five seconds or so. Several modifications of the same object within this interval produce a single delivery.
  • Answer with a 2xx code within 5 seconds. Beyond that, the delivery is considered failed.
  • On failure, Grand Shooting retries after 10 seconds, then after one minute. After three failures, the message is dropped and never sent again.
  • While a retry is pending, the deliveries of the account are on hold: a slow or failing receiver delays all the webhooks of the account.

Best practices

  1. Answer right away, process later. Put the message in a queue and answer 200; the processing (downloading the files, updating your DAM) happens in the background.
  2. Be idempotent. The same object can arrive several times (retries, successive modifications), and an update can arrive without any visible change. Handle each message as the current state of the object, not as a difference.
  3. Don't assume any order between topics or between messages: when in doubt, read the object again through the API.
  4. Secure the URL. Messages are not signed: use a long, unguessable HTTPS URL, check that account_id is an expected account, and for sensitive actions read the object again through the authenticated API from ids rather than trusting data.
  5. Plan a catch-up. A dropped message is not resent: a periodic job that reads the modified objects again (GET /picture?date_mod=gte:…) fills any gaps.

Example of a receiver (Node.js / Express):

app.post('/gs/webhook-7f3a9c', express.json({ limit: '20mb' }), (req, res) => {
  const { account_id, topic, ids } = req.body;
  if (!EXPECTED_ACCOUNTS.has(account_id)) return res.sendStatus(403);
  res.sendStatus(200); // answer before processing
  queue.push({ account_id, topic, ids });
});

There is no test event. To try your receiver, modify a media or a Reference of the account (a tag is enough): the message arrives within seconds.