> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goyappr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Test Tool Webhook

> Requires the `tools:update` scope. Sends one request to the saved tool's
configured webhook and returns the exact payload plus a sanitized preview
of the downstream response. The test follows the same payload contract
and HTTP semantics as a live call, including the configured method and
`timeout_seconds`.

Omitted extraction arguments receive `<test_name>` placeholders. Supplied
`arguments` may contain only names declared in the tool's effective
`payload_config.extraction_parameters`. When `agent_id` is supplied, that
agent must belong to the API key's company and supplies the standard
`agent_id` / `agent_name` fields.

**Production tool-webhook payload shape** (this is what your webhook URL receives when the agent actually invokes the tool during a call, when `config.payload_config.include_standard_metadata` is `true`, the default):

```json
{
  "company_id": "uuid",
  "agent_id": "uuid",
  "agent_name": "string",
  "call_id": "uuid",
  "call_direction": "outbound" | "inbound" | "web_call",
  "caller_number": "+972...",
  "callee_number": "+972...",
  "call_metadata":  { "...": "exactly what you passed at POST /calls body.metadata" },
  "call_variables": { "LeadName": "...", "AppointmentDate": "..." },
  "<your_extraction_param>": "<what the AI extracted>"
}
```

- **`call_metadata`** — the object you passed at call creation. Use this to carry CRM IDs (appointment_id, contact_id, calendar_id, etc.) that tool receivers need to route updates back to the right record. The agent never sees these keys (they're not injected into the prompt).
- **`call_variables`** — the `{{VariableName}}` values substituted into the system prompt. Useful for echoing context (e.g. Slack alerts: "Noa booked an appointment for {{LeadName}}").
- **Both are forwarded in real-time** — your webhook doesn't need a follow-up `GET /calls/{id}` fetch.

Setting `include_standard_metadata: false` strips the 9 standard-envelope
fields and sends only static + extraction params. For `POST`, `PUT`,
`PATCH`, and `DELETE`, the flat payload is sent as JSON. For `GET`, the
same fields are encoded as query parameters (objects/arrays are compact
JSON strings) and no request body is sent.


Send one test request with the same payload contract and HTTP semantics used when the tool runs during a live call. The tool must already be saved and belong to the API key's company.

Required scope: `tools:update`.

## Basic test

With no request body, Yappr generates a `<test_name>` value for every configured extraction parameter:

```bash theme={null}
curl -X POST "https://api.goyappr.com/tools/TOOL_ID/test" \
  -H "Authorization: Bearer $YAPPR_API_KEY"
```

The delivery uses the tool's saved URL, HTTP method, headers, static parameters, `include_standard_metadata`, and `timeout_seconds` (default 30 seconds; allowed range 1–60).

Configured headers may include authorization and custom content types, but request-routing/framing headers (`Host`, `Content-Length`, `Transfer-Encoding`, `Connection`, `Expect`, `Keep-Alive`, `Proxy-*`, `TE`, `Trailer`, `Upgrade`) are rejected when the tool is saved.

Webhook targets must be final public HTTP(S) endpoints. Localhost, cloud-metadata hosts, non-global literal or DNS-resolved addresses, mixed public/private DNS answers, and redirects are rejected. Configure the final destination URL directly; redirects are not followed.

## Supply representative values

Use an optional JSON body when the receiver needs realistic argument or call-context samples:

```bash theme={null}
curl -X POST "https://api.goyappr.com/tools/TOOL_ID/test" \
  -H "Authorization: Bearer $YAPPR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "11111111-1111-1111-1111-111111111111",
    "arguments": {
      "callerName": "Test Caller",
      "requestedDate": "next Tuesday"
    },
    "context": {
      "call_id": null,
      "call_direction": "outbound",
      "caller_number": "+972500000000",
      "callee_number": "+972500000001",
      "call_metadata": { "contact_id": "test-contact" },
      "call_variables": { "LeadName": "Test Caller" }
    }
  }'
```

| Field                    | Required | Notes                                                                                                                                                                                    |
| ------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent_id`               | no       | Must identify an agent in the API key's company. Supplies `agent_id` and `agent_name` in standard metadata.                                                                              |
| `arguments`              | no       | Flat string values keyed only by names in `config.payload_config.extraction_parameters`. Nested/object values are rejected. Omitted configured names receive `<test_name>` placeholders. |
| `context.call_id`        | no       | UUID or `null`.                                                                                                                                                                          |
| `context.call_direction` | no       | `inbound`, `outbound`, `web_call`, or `null`; defaults to `outbound`.                                                                                                                    |
| `context.caller_number`  | no       | String or `null`.                                                                                                                                                                        |
| `context.callee_number`  | no       | String or `null`.                                                                                                                                                                        |
| `context.call_metadata`  | no       | Object matching the metadata your call creation request would supply.                                                                                                                    |
| `context.call_variables` | no       | Object matching the variables your call creation request would supply.                                                                                                                   |

Unknown top-level, context, or argument names are rejected rather than silently forwarded.

## Production parity

When `include_standard_metadata` is `true` (the default), `payload_sent` includes the real company id, the supplied agent context (or null agent fields), call context, configured static parameters, and extraction arguments. With it set to `false`, only static and extraction parameters are sent.

* `POST`, `PUT`, `PATCH`, and `DELETE` tools receive the flat payload as JSON.
* `GET` tools receive the same fields as query parameters and no request body. Object and array values are compact JSON strings.
* Configured request headers are used for delivery but are never echoed in the API response.

## Success response

Any downstream HTTP `2xx` status returns `200` from this endpoint. Redirects and every other non-2xx status are delivery failures:

```json theme={null}
{
  "success": true,
  "status_code": 200,
  "response_body": "{\"ok\":true}",
  "payload_sent": {
    "company_id": "22222222-2222-2222-2222-222222222222",
    "agent_id": "11111111-1111-1111-1111-111111111111",
    "agent_name": "Scheduling Agent",
    "call_id": null,
    "call_direction": "outbound",
    "caller_number": "+972500000000",
    "callee_number": "+972500000001",
    "call_metadata": { "contact_id": "test-contact" },
    "call_variables": { "LeadName": "Test Caller" },
    "callerName": "Test Caller",
    "requestedDate": "next Tuesday"
  },
  "delivery_id": "33333333-3333-3333-3333-333333333333"
}
```

`payload_sent` is the exact flat object delivered. `delivery_id` can be `null` if delivery logging failed; logging failure does not turn a successful downstream request into a failure.

## Delivery failure

A downstream HTTP or network failure returns `502`; a timeout returns `504`:

```json theme={null}
{
  "error": "Webhook delivery failed",
  "code": "DOWNSTREAM_HTTP_ERROR",
  "details": "Webhook returned HTTP 500",
  "status_code": 500,
  "downstream_response": "{\"error\":\"temporary failure\"}",
  "payload_sent": { "callerName": "Test Caller" },
  "delivery_id": "33333333-3333-3333-3333-333333333333"
}
```

`code` is one of `DOWNSTREAM_HTTP_ERROR`, `WEBHOOK_NETWORK_ERROR`, or `WEBHOOK_TIMEOUT`. Downstream body previews are sanitized and length-capped.

## Errors

| HTTP  | When                                                                                                                                                                                                           |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` | Invalid JSON/body shape, unknown argument/context field, invalid saved configuration, or a local/non-public literal webhook target.                                                                            |
| `401` | Missing/invalid API key or the key lacks `tools:update`.                                                                                                                                                       |
| `404` | Tool not found, or the optional agent does not belong to the company.                                                                                                                                          |
| `502` | Downstream endpoint returned a redirect/other non-2xx status, could not be reached, or resolved to a non-public/mixed DNS answer. Redirects are not followed; configure the final public destination directly. |
| `504` | Downstream endpoint exceeded the configured timeout.                                                                                                                                                           |
| `500` | The internal test-delivery service returned an invalid response.                                                                                                                                               |


## OpenAPI

````yaml POST /tools/{id}/test
openapi: 3.1.0
info:
  title: Yappr API
  description: >
    Create and manage AI voice agents, purchase phone numbers, configure tools,
    and initiate calls — all via REST.
  version: 1.0.0
  contact:
    url: https://goyappr.com
servers:
  - url: https://api.goyappr.com
    description: Production
security:
  - apiKey: []
paths:
  /tools/{id}/test:
    post:
      tags:
        - Tools
      summary: Test tool webhook
      description: >
        Requires the `tools:update` scope. Sends one request to the saved tool's

        configured webhook and returns the exact payload plus a sanitized
        preview

        of the downstream response. The test follows the same payload contract

        and HTTP semantics as a live call, including the configured method and

        `timeout_seconds`.


        Omitted extraction arguments receive `<test_name>` placeholders.
        Supplied

        `arguments` may contain only names declared in the tool's effective

        `payload_config.extraction_parameters`. When `agent_id` is supplied,
        that

        agent must belong to the API key's company and supplies the standard

        `agent_id` / `agent_name` fields.


        **Production tool-webhook payload shape** (this is what your webhook URL
        receives when the agent actually invokes the tool during a call, when
        `config.payload_config.include_standard_metadata` is `true`, the
        default):


        ```json

        {
          "company_id": "uuid",
          "agent_id": "uuid",
          "agent_name": "string",
          "call_id": "uuid",
          "call_direction": "outbound" | "inbound" | "web_call",
          "caller_number": "+972...",
          "callee_number": "+972...",
          "call_metadata":  { "...": "exactly what you passed at POST /calls body.metadata" },
          "call_variables": { "LeadName": "...", "AppointmentDate": "..." },
          "<your_extraction_param>": "<what the AI extracted>"
        }

        ```


        - **`call_metadata`** — the object you passed at call creation. Use this
        to carry CRM IDs (appointment_id, contact_id, calendar_id, etc.) that
        tool receivers need to route updates back to the right record. The agent
        never sees these keys (they're not injected into the prompt).

        - **`call_variables`** — the `{{VariableName}}` values substituted into
        the system prompt. Useful for echoing context (e.g. Slack alerts: "Noa
        booked an appointment for {{LeadName}}").

        - **Both are forwarded in real-time** — your webhook doesn't need a
        follow-up `GET /calls/{id}` fetch.


        Setting `include_standard_metadata: false` strips the 9
        standard-envelope

        fields and sends only static + extraction params. For `POST`, `PUT`,

        `PATCH`, and `DELETE`, the flat payload is sent as JSON. For `GET`, the

        same fields are encoded as query parameters (objects/arrays are compact

        JSON strings) and no request body is sent.
      operationId: testTool
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                agent_id:
                  type: string
                  format: uuid
                  description: >-
                    Optional company-owned agent used for agent_id and
                    agent_name in standard metadata.
                arguments:
                  type: object
                  additionalProperties:
                    type: string
                  description: >-
                    Flat string sample values keyed only by configured
                    extraction-parameter names. Omitted configured names receive
                    <test_name> placeholders.
                context:
                  type: object
                  additionalProperties: false
                  properties:
                    call_id:
                      type:
                        - string
                        - 'null'
                      format: uuid
                    call_direction:
                      type:
                        - string
                        - 'null'
                      enum:
                        - inbound
                        - outbound
                        - web_call
                        - null
                      example: outbound
                    caller_number:
                      type:
                        - string
                        - 'null'
                      example: '+972500000000'
                    callee_number:
                      type:
                        - string
                        - 'null'
                      example: '+972500000001'
                    call_metadata:
                      type: object
                      additionalProperties: true
                    call_variables:
                      type: object
                      additionalProperties: true
            example:
              agent_id: 11111111-1111-1111-1111-111111111111
              arguments:
                callerName: Test Caller
              context:
                call_direction: outbound
                caller_number: '+972500000000'
                callee_number: '+972500000001'
                call_metadata:
                  contact_id: test-contact
                call_variables:
                  LeadName: Test Caller
      responses:
        '200':
          description: Downstream endpoint returned an HTTP 2xx status
          content:
            application/json:
              schema:
                type: object
                required:
                  - success
                  - status_code
                  - response_body
                  - payload_sent
                  - delivery_id
                properties:
                  success:
                    type: boolean
                    const: true
                  status_code:
                    type: integer
                  response_body:
                    type: string
                    description: >-
                      Sanitized, length-capped preview of the downstream
                      response body.
                  payload_sent:
                    type: object
                    additionalProperties: true
                    description: Exact flat payload delivered to the tool's webhook URL.
                  delivery_id:
                    type:
                      - string
                      - 'null'
                    format: uuid
                    description: >-
                      Webhook-delivery log ID. Null only if logging failed;
                      logging does not change delivery success.
        '400':
          description: >-
            Invalid JSON, context, arguments, saved configuration, or
            local/non-public literal webhook target
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                properties:
                  error:
                    type: string
        '401':
          description: Missing or invalid API key, or API key lacks tools:update
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                properties:
                  error:
                    type: string
                  code:
                    type: string
        '404':
          description: Tool not found, or supplied agent not found in the API key's company
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                properties:
                  error:
                    type: string
        '500':
          description: Internal webhook-test service returned an invalid response
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                properties:
                  error:
                    type: string
        '502':
          description: >-
            Redirect or other downstream non-2xx status, network failure, or
            non-public/mixed DNS resolution
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ToolTestFailure'
        '504':
          description: Webhook timed out after the configured timeout_seconds
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ToolTestFailure'
components:
  schemas:
    ToolTestFailure:
      type: object
      required:
        - error
        - code
        - details
        - status_code
        - downstream_response
        - payload_sent
        - delivery_id
      properties:
        error:
          type: string
          const: Webhook delivery failed
        code:
          type: string
          enum:
            - DOWNSTREAM_HTTP_ERROR
            - WEBHOOK_NETWORK_ERROR
            - WEBHOOK_TIMEOUT
        details:
          type: string
        status_code:
          type:
            - integer
            - 'null'
          description: Downstream HTTP status, or null when no response was received.
        downstream_response:
          type: string
          description: >-
            Sanitized, length-capped downstream response preview. Configured
            request headers are never returned.
        payload_sent:
          type: object
          additionalProperties: true
        delivery_id:
          type:
            - string
            - 'null'
          format: uuid
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: >-
        Your Yappr API key (e.g. `ypr_live_...`). Generate one in the dashboard
        under **Settings → API Keys**.

````