Skip to main content

Overview

Instead of polling the status endpoint, you can provide a callback_url in your interactive notification request. NextKS will POST the results to your URL when the request finishes.

When callbacks fire

A callback is triggered when:
  • onUserAction mode: All users have responded
  • onTimeout mode: The timeout is reached
  • Either mode: The timeout is reached and some users haven’t responded

Callback payload

NextKS sends a POST request with Content-Type: application/json:
{
  "request_id": "req_a1b2c3d4e5f6",
  "external_reference_id": "deploy-v2.4.1",
  "status": "finished",
  "responses": [
    {
      "user_email": "[email protected]",
      "option_value": "approve",
      "timestamp": "2026-02-11T14:23:01.000Z",
      "expired": false
    },
    {
      "user_email": "[email protected]",
      "option_value": null,
      "timestamp": null,
      "expired": true
    }
  ]
}

Callback URL requirements

Your callback URL is validated when you submit the notification:
RequirementDetails
ProtocolHTTPS only
DNSMust resolve to a public IP address
ReachabilityMust respond to a HEAD request within 5 seconds
Private IPsBlocked (10.x, 172.16-31.x, 192.168.x, 127.x, 169.254.x)
If validation fails, the notification request is rejected with a 400 error.

Retries

If a callback delivery fails (network error or non-2xx response), NextKS retries automatically:
  • Up to 3 retry attempts via the background cron job (runs every minute)
  • Each attempt is logged with the error message
  • Once successful, no further retries are attempted

Handling callbacks

Your endpoint should:
  1. Return a 2xx status code to acknowledge receipt
  2. Process the payload asynchronously if needed — NextKS has a 10-second timeout
  3. Be idempotent — in rare cases, a callback may be delivered more than once
# Example: Flask callback handler
@app.route("/hooks/nextks", methods=["POST"])
def handle_nextks_callback():
    data = request.json
    request_id = data["request_id"]
    ref = data["external_reference_id"]

    approved = all(
        r["option_value"] == "approve"
        for r in data["responses"]
        if not r["expired"]
    )

    if approved:
        trigger_deployment(ref)

    return "", 200