{
    "openapi": "3.0.3",
    "info": {
        "title": "Echo \u2014 Ksty.ch",
        "version": "1.0",
        "description": "Echoes your request back \u2014 the hello-world of Ksty.ch, free to try.\n\n# Echo API\n\nThe Echo API sends back whatever you send it, wrapped in the metadata the gateway saw while processing your request. It runs no upstream, touches no third party, and always succeeds \u2014 which is exactly what makes it the fastest way to prove your integration works end to end before you wire in a real API.\n\n## Why an echo endpoint exists\n\nEvery call on Ksty.ch passes through the same pipeline \u2014 key authentication, scope and region checks, rate limiting, then credit metering \u2014 before it reaches a handler. When you are setting up a new client, a failure anywhere in that chain looks identical from the outside: a non-200 response. Echo lets you isolate *your* half of the problem. If Echo returns `200` with your payload intact, then your key, headers, base URL and request encoding are all correct, and any failure against a real API is about that API's parameters rather than your plumbing.\n\n## What comes back\n\nA successful call returns your parsed parameters under `echo`, alongside the request context the gateway attached:\n\n```\nPOST /api/v1/echo\nX-Api-Key: ksty_test_...\nContent-Type: application/json\n\n{\"hello\": \"world\"}\n```\n\n```json\n{\n  \"echo\": { \"hello\": \"world\" },\n  \"endpoint\": \"\",\n  \"mode\": \"test\",\n  \"request_id\": \"req_8f21c...\"\n}\n```\n\n- `echo` \u2014 your parameters exactly as the gateway parsed them, so you can confirm a GET query string or a POST body decodes the way you expect.\n- `endpoint` \u2014 which sub-endpoint ran (empty for the root, `delay` for the delay endpoint).\n- `mode` \u2014 `test` for a `ksty_test_` key or `live` for a `ksty_live_` key, so you can verify which credential you actually sent.\n- `request_id` \u2014 the stable id for this call; log it and quote it to support.\n\n## Test vs live mode\n\nEcho is sandbox-enabled, so a `ksty_test_` key calls it for free and the response reports `\"mode\": \"test\"`. Swap in a `ksty_live_` key and the identical call is metered against your balance and reports `\"mode\": \"live\"`. Running the same request under both keys is the simplest way to confirm your production and sandbox credentials are wired to the right environments before you flip a real integration live.\n\n## The delay endpoint\n\n`echo.delay` echoes your payload after pausing for a number of `seconds` (0\u20135). Use it to exercise the timeout, backoff and retry behaviour of your client against a slow-but-successful response, without needing a real API that happens to be slow.\n\n> The delay endpoint costs 2 credits rather than 1, because it holds a worker for the duration of the pause. Keep `seconds` small in automated tests so you are not paying \u2014 or waiting \u2014 more than the scenario needs.\n\n## When to use it\n\n- The first call from a new SDK, language or environment, to confirm auth and encoding.\n- Verifying a key's mode (`test` vs `live`) and capturing the `request_id` format for your logs.\n- Load-testing your own client's timeout and retry paths via `echo.delay`.",
        "x-credit-cost": 1,
        "x-free-grant": {
            "type": "monthly",
            "amount": 100
        },
        "x-sandbox": true
    },
    "servers": [
        {
            "url": "https://ksty.ch/api/v1"
        }
    ],
    "security": [
        {
            "ApiKeyAuth": []
        }
    ],
    "paths": {
        "/echo": {
            "get": {
                "operationId": "echo_get",
                "summary": "Echo the request payload and metadata",
                "x-credit-cost": 1,
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "echo": {
                                        "message": "hello"
                                    },
                                    "endpoint": "",
                                    "mode": "test",
                                    "request_id": "req_8f21c4a0"
                                },
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "echo": {
                                            "type": "object",
                                            "description": "Your parameters exactly as the gateway parsed them \u2014 query string and JSON body are merged into one map."
                                        },
                                        "endpoint": {
                                            "type": "string",
                                            "description": "Which endpoint answered. Empty string for the root endpoint, \"delay\" for echo.delay."
                                        },
                                        "mode": {
                                            "type": "string",
                                            "description": "Whether the key that made the call was live or test \u2014 the quickest way to confirm you are using the key you meant to."
                                        },
                                        "request_id": {
                                            "type": "string",
                                            "description": "Identifier for this call, also returned in the X-Request-Id header and shown in your usage log."
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Insufficient credits \u2014 nothing is charged and the API is never reached",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "The key is not scoped to this API, or a test key was used on an API without sandbox support",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded for this key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "502": {
                        "description": "The API failed after being charged; credits are refunded automatically",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "message",
                        "in": "query",
                        "required": false,
                        "description": "Any payload to echo back",
                        "schema": {
                            "type": "string"
                        }
                    }
                ]
            },
            "post": {
                "operationId": "echo_post",
                "summary": "Echo the request payload and metadata",
                "x-credit-cost": 1,
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "echo": {
                                        "message": "hello"
                                    },
                                    "endpoint": "",
                                    "mode": "test",
                                    "request_id": "req_8f21c4a0"
                                },
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "echo": {
                                            "type": "object",
                                            "description": "Your parameters exactly as the gateway parsed them \u2014 query string and JSON body are merged into one map."
                                        },
                                        "endpoint": {
                                            "type": "string",
                                            "description": "Which endpoint answered. Empty string for the root endpoint, \"delay\" for echo.delay."
                                        },
                                        "mode": {
                                            "type": "string",
                                            "description": "Whether the key that made the call was live or test \u2014 the quickest way to confirm you are using the key you meant to."
                                        },
                                        "request_id": {
                                            "type": "string",
                                            "description": "Identifier for this call, also returned in the X-Request-Id header and shown in your usage log."
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Insufficient credits \u2014 nothing is charged and the API is never reached",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "The key is not scoped to this API, or a test key was used on an API without sandbox support",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded for this key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "502": {
                        "description": "The API failed after being charged; credits are refunded automatically",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "message": {
                                        "type": "string",
                                        "description": "Any payload to echo back"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/echo.delay": {
            "get": {
                "operationId": "echo_delay_get",
                "summary": "Echo after N seconds (max 5) \u2014 for testing client timeouts",
                "x-credit-cost": 2,
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "echo": {
                                        "seconds": 1
                                    },
                                    "delayed": 1
                                },
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "echo": {
                                            "type": "object",
                                            "description": "Your parameters, echoed back unchanged."
                                        },
                                        "delayed": {
                                            "type": "integer",
                                            "description": "Seconds actually waited before answering \u2014 the clamped value, so you can see when 9 became 5."
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Insufficient credits \u2014 nothing is charged and the API is never reached",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "The key is not scoped to this API, or a test key was used on an API without sandbox support",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded for this key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "502": {
                        "description": "The API failed after being charged; credits are refunded automatically",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "description": "Holds the response open for a fixed number of seconds before answering, so you can exercise the paths\nthat only appear under latency: client read timeouts, retry logic, circuit breakers, and whether your\nworker pool copes with a slow upstream.\n\nValues above 5 are clamped to 5 rather than rejected \u2014 a timeout test should not fail on a validation\nerror. Note the delay happens *after* metering, so a call that your client abandons mid-flight has\nstill been charged; that is the honest simulation of a real slow upstream.",
                "parameters": [
                    {
                        "name": "seconds",
                        "in": "query",
                        "required": false,
                        "description": "Delay in seconds, 0-5 (higher values are clamped)",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ]
            },
            "post": {
                "operationId": "echo_delay_post",
                "summary": "Echo after N seconds (max 5) \u2014 for testing client timeouts",
                "x-credit-cost": 2,
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "example": {
                                    "echo": {
                                        "seconds": 1
                                    },
                                    "delayed": 1
                                },
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "echo": {
                                            "type": "object",
                                            "description": "Your parameters, echoed back unchanged."
                                        },
                                        "delayed": {
                                            "type": "integer",
                                            "description": "Seconds actually waited before answering \u2014 the clamped value, so you can see when 9 became 5."
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid API key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Insufficient credits \u2014 nothing is charged and the API is never reached",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "The key is not scoped to this API, or a test key was used on an API without sandbox support",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded for this key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "502": {
                        "description": "The API failed after being charged; credits are refunded automatically",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "description": "Holds the response open for a fixed number of seconds before answering, so you can exercise the paths\nthat only appear under latency: client read timeouts, retry logic, circuit breakers, and whether your\nworker pool copes with a slow upstream.\n\nValues above 5 are clamped to 5 rather than rejected \u2014 a timeout test should not fail on a validation\nerror. Note the delay happens *after* metering, so a call that your client abandons mid-flight has\nstill been charged; that is the honest simulation of a real slow upstream.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "seconds": {
                                        "type": "integer",
                                        "description": "Delay in seconds, 0-5 (higher values are clamped)",
                                        "default": 1,
                                        "minimum": 0,
                                        "maximum": 5
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "ApiKeyAuth": {
                "type": "apiKey",
                "in": "header",
                "name": "X-Api-Key"
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "properties": {
                    "error": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string",
                                "description": "Stable machine-readable code \u2014 branch on this, never on the message."
                            },
                            "message": {
                                "type": "string"
                            },
                            "request_id": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        }
    }
}