> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.paywithatoa.co.uk/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Endpoint | Webhooks API

> Create endpoint via the Atoa Webhooks API, request parameters, response schema and code samples in cURL, Python, JavaScript, PHP, Go and Java.

Register a webhook endpoint and subscribe to one or more event types in a single call. Use [List event types](/api-reference/Webhook/v2/listEventTypes) to get the full list of available event type names.

<Note>
  **Initial registration only.** Use this endpoint to register a new URL with its initial event subscriptions. To change events or authentication on an existing endpoint, use [Update endpoint](/api-reference/Webhook/v2/updateEndpoint). To change the URL, delete the endpoint and create a new one.
</Note>

### Authorization

Bearer `<token>`

**Request Body Schema**

<ParamField body="url" type="string" required>
  The HTTPS endpoint URL that will receive webhook deliveries. Must return HTTP `200` on success — failures are retried with exponential back-off for up to 24 hours. **This URL cannot be changed after creation** — to use a different URL, delete the endpoint and create a new one.
</ParamField>

<ParamField body="events" type="string[]" required>
  One or more event types to subscribe to. Valid values: `PAYMENTS_STATUS`, `EXPIRED_STATUS`, `REFUND_STATUS`, `POS_PAYMENT_STATUS`. Subscribing to `POS_PAYMENT_STATUS` requires a webhook signing key to be generated first.

  Each event type supports a maximum of **3 registered endpoints**. If an event type already has 3 endpoints, adding another endpoint subscribed to that event returns a `400` error. Use [List endpoints](/api-reference/Webhook/v2/listEndpoints) to check current registrations before creating.
</ParamField>

<ParamField body="description" type="string">
  Optional label for this endpoint (max 100 characters). Shown in the Atoa Dashboard to help identify the endpoint.
</ParamField>

<ParamField body="authentication" type="object">
  Optional. Credentials Atoa will include in every webhook delivery to authenticate against your server. Two methods are supported — choose one. Fields from different methods cannot be mixed.

  <AccordionGroup>
    <Accordion title="None — no authentication">
      Omit the `authentication` field entirely. No credentials will be sent with deliveries. Use [signature verification](/api-reference/Webhook/introduction#signature-verification) to validate requests on your side.
    </Accordion>

    <Accordion title="Basic Auth — username + password">
      Atoa includes an `Authorization: Basic <base64(username:password)>` header in every delivery request. Pass both fields together.

      <ParamField body="username" type="string" required>
        Username for HTTP Basic Authentication.
      </ParamField>

      <ParamField body="password" type="string" required>
        Password for HTTP Basic Authentication.
      </ParamField>
    </Accordion>

    <Accordion title="OAuth 2.0 — client credentials flow">
      Before each delivery, Atoa calls your token endpoint using the `client_credentials` grant to obtain a short-lived access token, then includes it as `Authorization: Bearer <token>` in the delivery request. Pass all three fields together.

      <ParamField body="clientId" type="string" required>
        Client ID registered with your authorisation server.
      </ParamField>

      <ParamField body="clientSecret" type="string" required>
        Client secret for the above client ID.
      </ParamField>

      <ParamField body="authUrl" type="string" required>
        Your token endpoint URL (e.g. `https://auth.your-server.com/oauth/token`). Atoa will POST to this URL to fetch an access token before each delivery.
      </ParamField>
    </Accordion>
  </AccordionGroup>
</ParamField>

**Response**

<ResponseField name="id" type="string">
  Unique identifier for the endpoint.
</ResponseField>

<ResponseField name="url" type="string">
  The registered endpoint URL.
</ResponseField>

<ResponseField name="description" type="string">
  The endpoint description, or `null` if not set.
</ResponseField>

<ResponseField name="environment" type="string">
  `SANDBOX` or `PRODUCTION`, derived from the API key used.
</ResponseField>

<ResponseField name="events" type="array">
  Subscribed event types.

  <Expandable>
    <ResponseField name="event" type="string">Event type name (e.g. `PAYMENTS_STATUS`).</ResponseField>
    <ResponseField name="label" type="string">Human-readable label (e.g. `Payment Status`).</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="hasAuthentication" type="boolean">
  Whether endpoint authentication is configured.
</ResponseField>

<ResponseField name="authenticationType" type="string">
  `BASIC`, `OAUTH2`, or `null` if no authentication is configured.
</ResponseField>

<ResponseField name="failureCount" type="number">
  Consecutive failed deliveries since the last successful delivery. Resets to `0` on success.
</ResponseField>

<ResponseField name="lastDeliveredAt" type="string">
  ISO 8601 timestamp of the last successful delivery, or `null` if never delivered.
</ResponseField>

<RequestExample>
  ```bash No Auth theme={null}
  curl --request POST \
    --url https://api.atoa.me/api/webhook/v2/endpoints \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data '{
      "url": "https://your-server.com/webhooks",
      "events": ["PAYMENTS_STATUS", "REFUND_STATUS"],
      "description": "Production orders endpoint"
    }'
  ```

  ```bash Basic Auth theme={null}
  curl --request POST \
    --url https://api.atoa.me/api/webhook/v2/endpoints \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data '{
      "url": "https://your-server.com/webhooks",
      "events": ["PAYMENTS_STATUS", "REFUND_STATUS"],
      "description": "Production orders endpoint",
      "authentication": {
        "username": "your-username",
        "password": "your-password"
      }
    }'
  ```

  ```bash OAuth 2.0 theme={null}
  curl --request POST \
    --url https://api.atoa.me/api/webhook/v2/endpoints \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data '{
      "url": "https://your-server.com/webhooks",
      "events": ["PAYMENTS_STATUS", "REFUND_STATUS"],
      "description": "Production orders endpoint",
      "authentication": {
        "clientId": "your-client-id",
        "clientSecret": "your-client-secret",
        "authUrl": "https://auth.your-server.com/oauth/token"
      }
    }'
  ```

  ```python Python theme={null}
  import requests

  url = "https://api.atoa.me/api/webhook/v2/endpoints"

  payload = {
      "url": "https://your-server.com/webhooks",
      "events": ["PAYMENTS_STATUS", "REFUND_STATUS"],
      "description": "Production orders endpoint"
  }
  headers = {
      "Authorization": "Bearer <token>",
      "Content-Type": "application/json"
  }

  response = requests.request("POST", url, json=payload, headers=headers)
  print(response.text)
  ```

  ```javascript JavaScript theme={null}
  const options = {
    method: 'POST',
    headers: { Authorization: 'Bearer <token>', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      url: 'https://your-server.com/webhooks',
      events: ['PAYMENTS_STATUS', 'REFUND_STATUS'],
      description: 'Production orders endpoint'
    })
  };

  fetch('https://api.atoa.me/api/webhook/v2/endpoints', options)
    .then(response => response.json())
    .then(response => console.log(response))
    .catch(err => console.error(err));
  ```

  ```php PHP theme={null}
  <?php

  $curl = curl_init();

  curl_setopt_array($curl, [
    CURLOPT_URL => "https://api.atoa.me/api/webhook/v2/endpoints",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_POSTFIELDS => json_encode([
      "url" => "https://your-server.com/webhooks",
      "events" => ["PAYMENTS_STATUS", "REFUND_STATUS"],
      "description" => "Production orders endpoint"
    ]),
    CURLOPT_HTTPHEADER => [
      "Authorization: Bearer <token>",
      "Content-Type: application/json"
    ],
  ]);

  $response = curl_exec($curl);
  $err = curl_error($curl);

  curl_close($curl);

  if ($err) {
    echo "cURL Error #:" . $err;
  } else {
    echo $response;
  }
  ```

  ```go Go theme={null}
  package main

  import (
  	"fmt"
  	"strings"
  	"net/http"
  	"io"
  )

  func main() {

  	url := "https://api.atoa.me/api/webhook/v2/endpoints"

  	payload := strings.NewReader(`{
      "url": "https://your-server.com/webhooks",
      "events": ["PAYMENTS_STATUS", "REFUND_STATUS"],
      "description": "Production orders endpoint"
    }`)

  	req, _ := http.NewRequest("POST", url, payload)

  	req.Header.Add("Authorization", "Bearer <token>")
  	req.Header.Add("Content-Type", "application/json")

  	res, _ := http.DefaultClient.Do(req)

  	defer res.Body.Close()
  	body, _ := io.ReadAll(res.Body)

  	fmt.Println(string(body))

  }
  ```

  ```java Java theme={null}
  HttpResponse<String> response = Unirest.post("https://api.atoa.me/api/webhook/v2/endpoints")
    .header("Authorization", "Bearer <token>")
    .header("Content-Type", "application/json")
    .body("{\n  \"url\": \"https://your-server.com/webhooks\",\n  \"events\": [\"PAYMENTS_STATUS\", \"REFUND_STATUS\"],\n  \"description\": \"Production orders endpoint\"\n}")
    .asString();
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "id": "4a124f25-fe8d-47a0-92c8-16d94cbfe24c",
    "url": "https://your-server.com/webhooks",
    "description": "Production orders endpoint",
    "environment": "PRODUCTION",
    "events": [
      { "event": "PAYMENTS_STATUS", "label": "Payment Status" },
      { "event": "REFUND_STATUS", "label": "Refund Status" }
    ],
    "hasAuthentication": false,
    "authenticationType": null,
    "failureCount": 0,
    "lastDeliveredAt": null
  }
  ```

  ```json 400 (events required) theme={null}
  {
    "name": "BAD_REQUEST",
    "message": "At least one event type is required.",
    "status": 400
  }
  ```

  ```json 400 (duplicate events) theme={null}
  {
    "name": "BAD_REQUEST",
    "message": "Duplicate event types are not allowed.",
    "status": 400
  }
  ```

  ```json 400 (URL not HTTPS) theme={null}
  {
    "name": "BAD_REQUEST",
    "message": "Endpoint URL must use HTTPS.",
    "status": 400
  }
  ```

  ```json 400 (URL is localhost) theme={null}
  {
    "name": "BAD_REQUEST",
    "message": "Endpoint URL cannot point to localhost.",
    "status": 400
  }
  ```

  ```json 400 (URL is IP address) theme={null}
  {
    "name": "BAD_REQUEST",
    "message": "Endpoint URL cannot use an IP address.",
    "status": 400
  }
  ```

  ```json 400 (URL already registered) theme={null}
  {
    "name": "BAD_REQUEST",
    "message": "This webhook URL is already registered. Use the update API to modify the events or authentication details for this endpoint.",
    "status": 400
  }
  ```

  ```json 400 (capacity exceeded) theme={null}
  {
    "name": "BAD_REQUEST",
    "message": "Payment Status has reached the maximum limit of 3 endpoints per event type.",
    "status": 400
  }
  ```

  ```json 400 (POS without signing secret) theme={null}
  {
    "name": "BAD_REQUEST",
    "message": "POS Payment Status requires V2 signing. Please generate a signing secret from the dashboard to proceed.",
    "status": 400
  }
  ```

  ```json 401 theme={null}
  {
    "name": "UNAUTHORIZED",
    "message": "Unauthorized",
    "status": 401,
    "errors": "[]"
  }
  ```
</ResponseExample>
