Webhook
How to Use Webhooks
You can register webhooks so that your app can receive events occurring within Gency in real time.
Registering a Webhook
In the API Center, click Create New Webhook and enter the following items.
- Name (required, up to 100 characters): A name that identifies the webhook
- URL (required, HTTPS only, up to 500 characters): The endpoint to which a POST request is sent when an event occurs
- Subscribed Events: Select the event types to receive via checkboxes
Supported Event Types
| Event | Description |
|---|---|
product.created | A product was created |
product.deleted | A product was deleted |
product.pending_review | Changed to pending review status |
product.in_review | Changed to in review status |
product.review_completed | Review was completed |
product.creation_failed | Product creation failed |
Event Payload
When an event occurs, a POST request in the following format is sent to the registered URL. The event field is common, while the internal structure of data varies by event type.
product.pending_review / product.in_review / product.review_completed
Sent when a product's review status changes.
{
"event": "product.review_completed",
"data": {
"products": [
{
"workspaceId": "01HABC...",
"productId": "01HXYZ...",
"projectId": "my-project-01",
"productName": "겨울 패딩 자켓",
"status": "REVIEW_COMPLETE",
"category": "OUTER",
"productType": "FASHION",
"createdId": "01HUSR...",
"createdAt": "2025-01-01T00:00:00+09:00"
}
]
}
}
| Field | Type | Description |
|---|---|---|
products | Array | List of products whose status changed |
products[].workspaceId | String | Workspace ID |
products[].productId | String | Product ID |
products[].projectId | String | Management name |
products[].productName | String | Product name |
products[].status | String | Changed status (BEFORE_REVIEW: before review, IN_REVIEW: in review, REVIEW_COMPLETE: review complete, CREATION_FAILED: creation failed) |
products[].category | String | Category |
products[].productType | String | Product type (FASHION, JEWELRY) |
products[].createdId | String | Creator ID |
products[].createdAt | String | Creation timestamp (ISO 8601) |
product.created
Sent when a product is created.
{
"event": "product.created",
"data": {
"products": [
{
"workspaceId": "01HABC...",
"productId": "01HXYZ...",
"projectId": "my-project-01",
"productName": "겨울 패딩 자켓",
"status": "BEFORE_REVIEW",
"category": "OUTER",
"productType": "FASHION",
"createdId": "01HUSR...",
"createdAt": "2025-01-01T00:00:00+09:00"
}
]
}
}
product.creation_failed
Sent when product creation fails.
{
"event": "product.creation_failed",
"data": {
"products": [
{
"workspaceId": "01HABC...",
"productId": "01HXYZ...",
"projectId": "my-project-01",
"productName": "겨울 패딩 자켓",
"status": "CREATION_FAILED",
"category": "OUTER",
"productType": "FASHION",
"createdId": "01HUSR...",
"createdAt": "2025-01-01T00:00:00+09:00"
}
]
}
}
The payload structure is identical to the status change events. status is set to PRODUCT_STATUS_05 (creation failed).
product.deleted
Sent when a product is deleted.
{
"event": "product.deleted",
"data": {
"products": [
{
"workspaceId": "01HABC...",
"productId": "01HXYZ...",
"productName": "겨울 패딩 자켓"
}
]
}
}
| Field | Type | Description |
|---|---|---|
products | Array | List of deleted products |
products[].workspaceId | String | Workspace ID |
products[].productId | String | Product ID |
products[].productName | String | Product name |
Retry Policy
If a webhook delivery fails, the system automatically retries. Including the initial delivery, it attempts up to 5 times (1 initial + 4 retries), with the interval increasing using exponential backoff.
- Success criterion: HTTP response code
200 ~ 299 - Failure handling: Response codes outside the above range, timeouts (30 seconds), network errors, etc. are all treated as failures.
- If all 5 retries fail, the delivery is treated as a final failure.
- Delivery records for all attempts (HTTP status code, response time, error message) are retained for 30 days.
Webhook Registration Limit
You can register up to 10 webhooks per master. Deleted webhooks are not counted toward the limit, so if you have reached the limit, you can register a new one by deleting an unused webhook.
Signature Verification (Optional)
When a webhook is created, a Signing Secret is automatically issued (in the whsec_{32자 랜덤문자열} format). Signature verification is not required, but it is recommended in production environments to prevent forged requests.
Each webhook request includes the following headers.
| Header | Description |
|---|---|
X-Webhook-Signature | sha256={HMAC-SHA256 서명값} |
X-Webhook-Timestamp | Request delivery time (Unix timestamp) |
| Content-Type | application/json |
Verification method:
- Extract the timestamp from the
X-Webhook-Timestampheader. - Build the message to sign in the
{타임스탬프}.{요청 body}format. - Compute the HMAC-SHA256 signature using the Signing Secret as the key.
- Compare the computed signature with the value of the
X-Webhook-Signatureheader.
// 검증 예시 (Java)
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
public boolean verifyWebhook(String payload, String timestamp, String signature, String secret) {
String message = timestamp + "." + payload;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
byte[] hash = mac.doFinal(message.getBytes());
StringBuilder hex = new StringBuilder();
for (byte b : hash) {
hex.append(String.format("%02x", b));
}
String expected = "sha256=" + hex.toString();
return MessageDigest.isEqual(expected.getBytes(), signature.getBytes());
}
Reissue Secret
Clicking the Reissue Secret button issues a new Signing Secret. The existing secret is immediately invalidated, so you must apply the new secret to your server before using it.
Status Management
A webhook can have the following statuses.
| Status | Description |
|---|---|
ACTIVE | Delivered normally when an event occurs |
INACTIVE | Event delivery is temporarily suspended |
If you want to temporarily stop receiving webhooks, you can deactivate it, and deleting it removes it completely.