{"paths":{"/v1/webhook-endpoints/{id}":{"delete":{"x-docs-public":true,"summary":"Delete a webhook endpoint","security":[{"apiKey":[]}],"description":"Delete a webhook endpoint","x-required-scope":"manage","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X DELETE \"$NORIA_API_URL/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\",\n    method=\"DELETE\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'DELETE',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"DELETE\", baseURL + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\")\nrequest = Net::HTTP::Delete.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"DELETE\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}","lang":"Java"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"DELETE\"), baseUrl + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"tags":["Webhooks"],"operationId":"deleteWebhookEndpoint","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"format":"uuid","type":"string"}}],"responses":{"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}},"summary":"Request conflicts with existing state"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"message":"An internal error occurred","request_id":"req_docs_invoice_1042","code":"internal_error"}}}}}}},"204":{"description":"Deleted"},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"message":"Provide a valid API key","request_id":"req_docs_invoice_1042","code":"unauthorized"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}},"summary":"Insufficient access"}}}}}}},"patch":{"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X PATCH \"$NORIA_API_URL/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  --data-raw '{\n  \"enabled\": true\n}'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"enabled\": true\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"enabled\": true\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"enabled\": True\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\",\n    method=\"PATCH\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'PATCH',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'enabled' => true\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);","lang":"PHP"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"enabled\\\": true\\n}\")\n    req, err := http.NewRequest(\"PATCH\", baseURL + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\")\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = JSON.generate({\n  \"enabled\" => true\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result","lang":"Ruby","label":"Ruby"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"enabled\\\": true\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"PATCH\"), baseUrl + \"/v1/webhook-endpoints/0197d1f0-21a0-7000-8000-000000000006\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Content = new StringContent(@\"{\n  \"\"enabled\"\": true\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"tags":["Webhooks"],"operationId":"updateWebhookEndpoint","summary":"Update a webhook endpoint","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchWebhookEndpointRequest"},"examples":{"resume":{"summary":"Resume queued deliveries","value":{"enabled":true}}}}}},"responses":{"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"request_id":"req_docs_invoice_1042","code":"unauthorized","message":"Provide a valid API key"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"examples":{"default":{"summary":"Insufficient access","value":{"error":{"message":"This key cannot select another project","request_id":"req_docs_invoice_1042","code":"forbidden"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"provider_not_configured","message":"Configure a compatible gateway account for this project"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"request_id":"req_docs_invoice_1042","code":"rate_limited","message":"Too many requests; wait before retrying"}}}}}}},"200":{"description":"Success","content":{"application/json":{"examples":{"default":{"summary":"Success","value":{"source":"billgrid","url":"https://billgrid.example.com/webhooks/noria-pay","description":"Invoice reconciliation","event_types":["succeeded","failed","outcome_unknown","reversed"],"id":"0197d1f0-21a0-7000-8000-000000000006","object":"webhook_endpoint","application_id":"0197d1f0-21a0-7000-8000-000000000002","enabled":true,"include_provider_payload":false,"created_at":"2026-10-20T09:00:00Z"}}},"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}},"description":"Request conflicts with existing state"},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"validation_error","message":"Invalid request parameters","request_id":"req_docs_invoice_1042"}}}}}}}},"x-docs-public":true,"security":[{"apiKey":[]}],"description":"Update a webhook endpoint; what it omits is left alone","x-required-scope":"manage"}},"/v1/webhook-deliveries":{"get":{"x-required-scope":"read","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/webhook-deliveries?status=pending&limit=20\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-deliveries?status=pending&limit=20\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-deliveries?status=pending&limit=20\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/webhook-deliveries?status=pending&limit=20\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/webhook-deliveries?status=pending&limit=20');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/webhook-deliveries?status=pending&limit=20\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}","lang":"Go","label":"Go"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/webhook-deliveries?status=pending&limit=20\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/webhook-deliveries?status=pending&limit=20\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/webhook-deliveries?status=pending&limit=20\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"tags":["Webhooks"],"operationId":"listWebhookDeliveries","summary":"List webhook deliveries","parameters":[{"schema":{"maximum":100,"default":25,"type":"integer","minimum":1},"name":"limit","in":"query"},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["pending","delivering","delivered","failed"]}},{"name":"endpoint_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}}],"description":"Inspect project webhook delivery and retry status","security":[{"apiKey":[]}],"responses":{"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042","code":"rate_limited"}}}}}}},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"validation_error","message":"Invalid request parameters","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"provider_not_configured","message":"Configure a compatible gateway account for this project"}}}}}}},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}},"summary":"Request error"}}}},"description":"Request error"},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryList"},"examples":{"default":{"summary":"Success","value":{"object":"list","data":[{"endpoint_url":"https://billgrid.example.com/webhooks/noria-pay","event_id":"0197d1f0-21a0-7000-8000-000000000007","max_attempts":8,"delivered_at":null,"created_at":"2026-10-20T09:00:18Z","endpoint_enabled":true,"event_type":"succeeded","attempts":1,"response_status":503,"workspace_id":"0197d1f0-21a0-7000-8000-000000000001","application_id":"0197d1f0-21a0-7000-8000-000000000002","status":"pending","id":"0197d1f0-21a0-7000-8000-000000000008","object":"webhook_delivery","endpoint_id":"0197d1f0-21a0-7000-8000-000000000006","transaction_id":"0197d1f0-21a0-7000-8000-000000000003","last_error":"Webhook delivery failed","next_attempt_at":"2026-10-20T09:00:23Z"}],"next_cursor":null}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042","code":"idempotency_mismatch"}}}}}}}},"x-docs-public":true}},"/v1/webhook-deliveries/{id}/retry":{"post":{"summary":"Retry a delivery","responses":{"400":{"content":{"application/json":{"examples":{"default":{"summary":"Request error","value":{"error":{"code":"validation_error","message":"Invalid request parameters","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request error"},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"code":"provider_not_configured","message":"Configure a compatible gateway account for this project","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042","code":"rate_limited"}}}}}}},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryRetry"},"examples":{"default":{"summary":"Success","value":{"status":"pending","id":"0197d1f0-21a0-7000-8000-000000000008","object":"webhook_delivery"}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}}},"x-docs-public":true,"description":"Queue a pending or failed event again without redispatching its payment","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008/retry\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008/retry\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"JavaScript"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008/retry\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008/retry\",\n    method=\"POST\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error","lang":"Python","label":"Python"},{"source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008/retry');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);","lang":"PHP","label":"PHP"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008/retry\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008/retry\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008/retry\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008/retry\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"tags":["Webhooks"],"operationId":"retryWebhookDelivery","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application","in":"header","required":false}],"x-required-scope":"manage"}},"/v1/charges":{"post":{"summary":"Create a charge","parameters":[{"description":"Required on every write that moves money. The same key with the same body replays the original response; the same key with a different body is rejected.","name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":255}},{"schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChargeRequest"},"examples":{"mpesa":{"summary":"M-Pesa invoice collection","value":{"metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"amount_minor":125000,"provider":"daraja","reference":"BG-INV-2026-1042","description":"BillGrid invoice 1042","payer_phone":"254712345678","payer_name":"Amina Otieno","currency":"KES","channel":"mpesa","mode":"sandbox"}},"card":{"summary":"Card checkout redirect","value":{"payer_name":"Amina Otieno","metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"payer_email":"amina@example.com","channel":"card","mode":"sandbox","return_url":"https://billgrid.example.com/invoices/1042/payment-result","amount_minor":125000,"currency":"KES","provider":"paystack","reference":"BG-INV-2026-1042","description":"BillGrid invoice 1042"}}}}}},"responses":{"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"request_id":"req_docs_invoice_1042","code":"rate_limited","message":"Too many requests; wait before retrying"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transaction"},"examples":{"accepted":{"summary":"Accepted; waiting for the payer","value":{"object":"transaction","channel":"mpesa","payer_email":null,"provider":"daraja","currency":"KES","settled_minor":null,"payer_phone":"254712345678","expires_at":"2026-10-20T09:05:00Z","source":"billgrid","amount_minor":125000,"next_action":{"type":"await_payer","provider_ref":"ws_CO_20102026090000123456789012","payer_message":"Complete the M-Pesa prompt on your phone.","expires_at":"2026-10-20T09:05:00Z"},"payer_name":"Amina Otieno","failure_reason":null,"fee_minor":null,"id":"0197d1f0-21a0-7000-8000-000000000003","direction":"collection","reference":"BG-INV-2026-1042","parent_transaction_id":null,"workspace_id":"0197d1f0-21a0-7000-8000-000000000001","recipient_account":null,"metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"created_at":"2026-10-20T09:00:00Z","mode":"sandbox","payment_link_id":null,"provider_ref":"ws_CO_20102026090000123456789012","recipient_phone":null,"provider_receipt":null,"recipient_name":null,"application_id":"0197d1f0-21a0-7000-8000-000000000002","status":"processing","description":"BillGrid invoice 1042","settled_at":null}},"card":{"summary":"Card checkout redirect","value":{"provider_receipt":null,"workspace_id":"0197d1f0-21a0-7000-8000-000000000001","fee_minor":null,"failure_reason":null,"next_action":{"url":"https://checkout.paystack.com/documentation-example","provider_ref":"bg_inv_1042_attempt_1","expires_at":"2026-10-20T09:05:00Z","type":"redirect"},"created_at":"2026-10-20T09:00:00Z","parent_transaction_id":null,"payer_email":"amina@example.com","recipient_account":null,"application_id":"0197d1f0-21a0-7000-8000-000000000002","status":"processing","payer_name":"Amina Otieno","settled_at":null,"id":"0197d1f0-21a0-7000-8000-000000000003","object":"transaction","provider":"paystack","currency":"KES","settled_minor":null,"recipient_phone":null,"provider_ref":"bg_inv_1042_attempt_1","metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"channel":"card","reference":"BG-INV-2026-1042","payer_phone":"254712345678","expires_at":"2026-10-20T09:05:00Z","source":"billgrid","amount_minor":125000,"description":"BillGrid invoice 1042","recipient_name":null,"direction":"collection","mode":"sandbox","payment_link_id":null}}}}},"description":"Success"},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"validation_error","message":"Invalid request parameters","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"message":"Configure a compatible gateway account for this project","request_id":"req_docs_invoice_1042","code":"provider_not_configured"}}}}}}},"502":{"description":"Gateway unavailable","content":{"application/json":{"examples":{"default":{"value":{"error":{"request_id":"req_docs_invoice_1042","code":"provider_unavailable","message":"The provider is temporarily unavailable"}},"summary":"Gateway unavailable"}},"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Gateway outcome unknown","content":{"application/json":{"examples":{"default":{"summary":"Gateway outcome unknown","value":{"error":{"request_id":"req_docs_invoice_1042","code":"outcome_unknown","message":"The provider outcome is unknown; reconcile the original transaction"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}},"summary":"Authentication required"}}}}}},"x-required-scope":"charge","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/charges\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -H 'Idempotency-Key: bg-invoice-1042-mpesa-attempt-1' \\\n  --data-raw '{\n  \"amount_minor\": 125000,\n  \"currency\": \"KES\",\n  \"channel\": \"mpesa\",\n  \"provider\": \"daraja\",\n  \"mode\": \"sandbox\",\n  \"reference\": \"BG-INV-2026-1042\",\n  \"description\": \"BillGrid invoice 1042\",\n  \"payer_phone\": \"254712345678\",\n  \"payer_name\": \"Amina Otieno\",\n  \"metadata\": {\n    \"invoice_id\": \"1042\",\n    \"customer_id\": \"cust_ami_204\"\n  }\n}'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/charges\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": \"bg-invoice-1042-mpesa-attempt-1\",\n  },\n  body: JSON.stringify({\n    \"amount_minor\": 125000,\n    \"currency\": \"KES\",\n    \"channel\": \"mpesa\",\n    \"provider\": \"daraja\",\n    \"mode\": \"sandbox\",\n    \"reference\": \"BG-INV-2026-1042\",\n    \"description\": \"BillGrid invoice 1042\",\n    \"payer_phone\": \"254712345678\",\n    \"payer_name\": \"Amina Otieno\",\n    \"metadata\": {\n      \"invoice_id\": \"1042\",\n      \"customer_id\": \"cust_ami_204\"\n    }\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/charges\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": \"bg-invoice-1042-mpesa-attempt-1\",\n  },\n  body: JSON.stringify({\n    \"amount_minor\": 125000,\n    \"currency\": \"KES\",\n    \"channel\": \"mpesa\",\n    \"provider\": \"daraja\",\n    \"mode\": \"sandbox\",\n    \"reference\": \"BG-INV-2026-1042\",\n    \"description\": \"BillGrid invoice 1042\",\n    \"payer_phone\": \"254712345678\",\n    \"payer_name\": \"Amina Otieno\",\n    \"metadata\": {\n      \"invoice_id\": \"1042\",\n      \"customer_id\": \"cust_ami_204\"\n    }\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"amount_minor\": 125000,\n  \"currency\": \"KES\",\n  \"channel\": \"mpesa\",\n  \"provider\": \"daraja\",\n  \"mode\": \"sandbox\",\n  \"reference\": \"BG-INV-2026-1042\",\n  \"description\": \"BillGrid invoice 1042\",\n  \"payer_phone\": \"254712345678\",\n  \"payer_name\": \"Amina Otieno\",\n  \"metadata\": {\n    \"invoice_id\": \"1042\",\n    \"customer_id\": \"cust_ami_204\"\n  }\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/charges\",\n    method=\"POST\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n        \"Idempotency-Key\": \"bg-invoice-1042-mpesa-attempt-1\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/charges');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n        'Idempotency-Key: bg-invoice-1042-mpesa-attempt-1',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'amount_minor' => 125000,\n      'currency' => 'KES',\n      'channel' => 'mpesa',\n      'provider' => 'daraja',\n      'mode' => 'sandbox',\n      'reference' => 'BG-INV-2026-1042',\n      'description' => 'BillGrid invoice 1042',\n      'payer_phone' => '254712345678',\n      'payer_name' => 'Amina Otieno',\n      'metadata' => [\n        'invoice_id' => '1042',\n        'customer_id' => 'cust_ami_204'\n      ]\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"amount_minor\\\": 125000,\\n  \\\"currency\\\": \\\"KES\\\",\\n  \\\"channel\\\": \\\"mpesa\\\",\\n  \\\"provider\\\": \\\"daraja\\\",\\n  \\\"mode\\\": \\\"sandbox\\\",\\n  \\\"reference\\\": \\\"BG-INV-2026-1042\\\",\\n  \\\"description\\\": \\\"BillGrid invoice 1042\\\",\\n  \\\"payer_phone\\\": \\\"254712345678\\\",\\n  \\\"payer_name\\\": \\\"Amina Otieno\\\",\\n  \\\"metadata\\\": {\\n    \\\"invoice_id\\\": \\\"1042\\\",\\n    \\\"customer_id\\\": \\\"cust_ami_204\\\"\\n  }\\n}\")\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/charges\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    req.Header.Set(\"Idempotency-Key\", \"bg-invoice-1042-mpesa-attempt-1\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/charges\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest[\"Idempotency-Key\"] = \"bg-invoice-1042-mpesa-attempt-1\"\nrequest.body = JSON.generate({\n  \"amount_minor\" => 125000,\n  \"currency\" => \"KES\",\n  \"channel\" => \"mpesa\",\n  \"provider\" => \"daraja\",\n  \"mode\" => \"sandbox\",\n  \"reference\" => \"BG-INV-2026-1042\",\n  \"description\" => \"BillGrid invoice 1042\",\n  \"payer_phone\" => \"254712345678\",\n  \"payer_name\" => \"Amina Otieno\",\n  \"metadata\" => {\n    \"invoice_id\" => \"1042\",\n    \"customer_id\" => \"cust_ami_204\"\n  }\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/charges\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .header(\"Idempotency-Key\", \"bg-invoice-1042-mpesa-attempt-1\")\n        .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"amount_minor\\\": 125000,\\n  \\\"currency\\\": \\\"KES\\\",\\n  \\\"channel\\\": \\\"mpesa\\\",\\n  \\\"provider\\\": \\\"daraja\\\",\\n  \\\"mode\\\": \\\"sandbox\\\",\\n  \\\"reference\\\": \\\"BG-INV-2026-1042\\\",\\n  \\\"description\\\": \\\"BillGrid invoice 1042\\\",\\n  \\\"payer_phone\\\": \\\"254712345678\\\",\\n  \\\"payer_name\\\": \\\"Amina Otieno\\\",\\n  \\\"metadata\\\": {\\n    \\\"invoice_id\\\": \\\"1042\\\",\\n    \\\"customer_id\\\": \\\"cust_ami_204\\\"\\n  }\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}","lang":"Java"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/charges\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Headers.Add(\"Idempotency-Key\", \"bg-invoice-1042-mpesa-attempt-1\");\nrequest.Content = new StringContent(@\"{\n  \"\"amount_minor\"\": 125000,\n  \"\"currency\"\": \"\"KES\"\",\n  \"\"channel\"\": \"\"mpesa\"\",\n  \"\"provider\"\": \"\"daraja\"\",\n  \"\"mode\"\": \"\"sandbox\"\",\n  \"\"reference\"\": \"\"BG-INV-2026-1042\"\",\n  \"\"description\"\": \"\"BillGrid invoice 1042\"\",\n  \"\"payer_phone\"\": \"\"254712345678\"\",\n  \"\"payer_name\"\": \"\"Amina Otieno\"\",\n  \"\"metadata\"\": {\n    \"\"invoice_id\"\": \"\"1042\"\",\n    \"\"customer_id\"\": \"\"cust_ami_204\"\"\n  }\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"tags":["Charges"],"operationId":"createCharge","description":"Start a payment using the authenticated project’s configured gateway credentials. Project keys infer project ownership and source automatically; a conflicting source is refused. Callback updates commit with durable delivery jobs and wake workers immediately; scheduled status queries are a recovery fallback.","security":[{"apiKey":[]}],"x-docs-public":true}},"/v1/payouts":{"post":{"security":[{"apiKey":[]}],"x-docs-public":true,"description":"Send money to a recipient","x-required-scope":"payout","summary":"Create a payout","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":255},"description":"Required on every write that moves money. The same key with the same body replays the original response; the same key with a different body is rejected."},{"in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePayoutRequest"},"examples":{"mobile":{"summary":"Pay a supplier","value":{"amount_minor":85000,"channel":"mpesa","provider":"sasapay","mode":"sandbox","reference":"BG-SUP-2026-0071","description":"Supplier settlement","recipient_phone":"254722345678","metadata":{"supplier_id":"supplier_71"},"currency":"KES","recipient_name":"Njeri Supplies"}}}}}},"responses":{"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"code":"provider_not_configured","message":"Configure a compatible gateway account for this project","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"502":{"description":"Gateway unavailable","content":{"application/json":{"examples":{"default":{"summary":"Gateway unavailable","value":{"error":{"message":"The provider is temporarily unavailable","request_id":"req_docs_invoice_1042","code":"provider_unavailable"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"201":{"description":"Success","content":{"application/json":{"examples":{"accepted":{"summary":"Payout accepted","value":{"application_id":"0197d1f0-21a0-7000-8000-000000000002","provider_ref":"ws_CO_20102026090000123456789012","created_at":"2026-10-20T09:00:00Z","workspace_id":"0197d1f0-21a0-7000-8000-000000000001","amount_minor":85000,"reference":"BG-SUP-2026-0071","payer_phone":null,"recipient_account":null,"next_action":{"type":"none"},"expires_at":"2026-10-20T09:05:00Z","source":"billgrid","provider":"sasapay","currency":"KES","settled_minor":null,"fee_minor":null,"payer_name":null,"object":"transaction","direction":"payout","description":"BillGrid invoice 1042","payment_link_id":null,"failure_reason":null,"metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"channel":"mpesa","recipient_phone":"254722345678","id":"0197d1f0-21a0-7000-8000-000000000003","status":"processing","mode":"sandbox","parent_transaction_id":null,"payer_email":null,"recipient_name":"Njeri Supplies","provider_receipt":null,"settled_at":null}}},"schema":{"$ref":"#/components/schemas/Transaction"}}}},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"validation_error","message":"Invalid request parameters","request_id":"req_docs_invoice_1042"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}},"summary":"Request error"}}}}},"504":{"description":"Gateway outcome unknown","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"outcome_unknown","message":"The provider outcome is unknown; reconcile the original transaction","request_id":"req_docs_invoice_1042"}},"summary":"Gateway outcome unknown"}}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}},"description":"Authentication required"}},"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/payouts\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -H 'Idempotency-Key: bg-supplier-71-mobile-attempt-1' \\\n  --data-raw '{\n  \"amount_minor\": 85000,\n  \"currency\": \"KES\",\n  \"channel\": \"mpesa\",\n  \"provider\": \"sasapay\",\n  \"mode\": \"sandbox\",\n  \"reference\": \"BG-SUP-2026-0071\",\n  \"description\": \"Supplier settlement\",\n  \"recipient_name\": \"Njeri Supplies\",\n  \"recipient_phone\": \"254722345678\",\n  \"metadata\": {\n    \"supplier_id\": \"supplier_71\"\n  }\n}'"},{"label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payouts\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": \"bg-supplier-71-mobile-attempt-1\",\n  },\n  body: JSON.stringify({\n    \"amount_minor\": 85000,\n    \"currency\": \"KES\",\n    \"channel\": \"mpesa\",\n    \"provider\": \"sasapay\",\n    \"mode\": \"sandbox\",\n    \"reference\": \"BG-SUP-2026-0071\",\n    \"description\": \"Supplier settlement\",\n    \"recipient_name\": \"Njeri Supplies\",\n    \"recipient_phone\": \"254722345678\",\n    \"metadata\": {\n      \"supplier_id\": \"supplier_71\"\n    }\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"JavaScript"},{"label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payouts\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": \"bg-supplier-71-mobile-attempt-1\",\n  },\n  body: JSON.stringify({\n    \"amount_minor\": 85000,\n    \"currency\": \"KES\",\n    \"channel\": \"mpesa\",\n    \"provider\": \"sasapay\",\n    \"mode\": \"sandbox\",\n    \"reference\": \"BG-SUP-2026-0071\",\n    \"description\": \"Supplier settlement\",\n    \"recipient_name\": \"Njeri Supplies\",\n    \"recipient_phone\": \"254722345678\",\n    \"metadata\": {\n      \"supplier_id\": \"supplier_71\"\n    }\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"amount_minor\": 85000,\n  \"currency\": \"KES\",\n  \"channel\": \"mpesa\",\n  \"provider\": \"sasapay\",\n  \"mode\": \"sandbox\",\n  \"reference\": \"BG-SUP-2026-0071\",\n  \"description\": \"Supplier settlement\",\n  \"recipient_name\": \"Njeri Supplies\",\n  \"recipient_phone\": \"254722345678\",\n  \"metadata\": {\n    \"supplier_id\": \"supplier_71\"\n  }\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/payouts\",\n    method=\"POST\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n        \"Idempotency-Key\": \"bg-supplier-71-mobile-attempt-1\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payouts');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n        'Idempotency-Key: bg-supplier-71-mobile-attempt-1',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'amount_minor' => 85000,\n      'currency' => 'KES',\n      'channel' => 'mpesa',\n      'provider' => 'sasapay',\n      'mode' => 'sandbox',\n      'reference' => 'BG-SUP-2026-0071',\n      'description' => 'Supplier settlement',\n      'recipient_name' => 'Njeri Supplies',\n      'recipient_phone' => '254722345678',\n      'metadata' => [\n        'supplier_id' => 'supplier_71'\n      ]\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"amount_minor\\\": 85000,\\n  \\\"currency\\\": \\\"KES\\\",\\n  \\\"channel\\\": \\\"mpesa\\\",\\n  \\\"provider\\\": \\\"sasapay\\\",\\n  \\\"mode\\\": \\\"sandbox\\\",\\n  \\\"reference\\\": \\\"BG-SUP-2026-0071\\\",\\n  \\\"description\\\": \\\"Supplier settlement\\\",\\n  \\\"recipient_name\\\": \\\"Njeri Supplies\\\",\\n  \\\"recipient_phone\\\": \\\"254722345678\\\",\\n  \\\"metadata\\\": {\\n    \\\"supplier_id\\\": \\\"supplier_71\\\"\\n  }\\n}\")\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/payouts\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    req.Header.Set(\"Idempotency-Key\", \"bg-supplier-71-mobile-attempt-1\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payouts\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest[\"Idempotency-Key\"] = \"bg-supplier-71-mobile-attempt-1\"\nrequest.body = JSON.generate({\n  \"amount_minor\" => 85000,\n  \"currency\" => \"KES\",\n  \"channel\" => \"mpesa\",\n  \"provider\" => \"sasapay\",\n  \"mode\" => \"sandbox\",\n  \"reference\" => \"BG-SUP-2026-0071\",\n  \"description\" => \"Supplier settlement\",\n  \"recipient_name\" => \"Njeri Supplies\",\n  \"recipient_phone\" => \"254722345678\",\n  \"metadata\" => {\n    \"supplier_id\" => \"supplier_71\"\n  }\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payouts\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .header(\"Idempotency-Key\", \"bg-supplier-71-mobile-attempt-1\")\n        .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"amount_minor\\\": 85000,\\n  \\\"currency\\\": \\\"KES\\\",\\n  \\\"channel\\\": \\\"mpesa\\\",\\n  \\\"provider\\\": \\\"sasapay\\\",\\n  \\\"mode\\\": \\\"sandbox\\\",\\n  \\\"reference\\\": \\\"BG-SUP-2026-0071\\\",\\n  \\\"description\\\": \\\"Supplier settlement\\\",\\n  \\\"recipient_name\\\": \\\"Njeri Supplies\\\",\\n  \\\"recipient_phone\\\": \\\"254722345678\\\",\\n  \\\"metadata\\\": {\\n    \\\"supplier_id\\\": \\\"supplier_71\\\"\\n  }\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/payouts\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Headers.Add(\"Idempotency-Key\", \"bg-supplier-71-mobile-attempt-1\");\nrequest.Content = new StringContent(@\"{\n  \"\"amount_minor\"\": 85000,\n  \"\"currency\"\": \"\"KES\"\",\n  \"\"channel\"\": \"\"mpesa\"\",\n  \"\"provider\"\": \"\"sasapay\"\",\n  \"\"mode\"\": \"\"sandbox\"\",\n  \"\"reference\"\": \"\"BG-SUP-2026-0071\"\",\n  \"\"description\"\": \"\"Supplier settlement\"\",\n  \"\"recipient_name\"\": \"\"Njeri Supplies\"\",\n  \"\"recipient_phone\"\": \"\"254722345678\"\",\n  \"\"metadata\"\": {\n    \"\"supplier_id\"\": \"\"supplier_71\"\"\n  }\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"tags":["Payouts"],"operationId":"createPayout"}},"/v1/transactions/{id}":{"get":{"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transaction"},"examples":{"default":{"summary":"Success","value":{"provider":"daraja","amount_minor":125000,"reference":"BG-INV-2026-1042","parent_transaction_id":null,"expires_at":"2026-10-20T09:05:00Z","payer_phone":"254712345678","payer_email":null,"recipient_name":null,"payer_name":"Amina Otieno","workspace_id":"0197d1f0-21a0-7000-8000-000000000001","application_id":"0197d1f0-21a0-7000-8000-000000000002","mode":"sandbox","settled_minor":125000,"fee_minor":null,"recipient_phone":null,"recipient_account":null,"created_at":"2026-10-20T09:00:00Z","id":"0197d1f0-21a0-7000-8000-000000000003","status":"succeeded","payment_link_id":null,"provider_receipt":"UJK7A4M2Q9","failure_reason":null,"object":"transaction","description":"BillGrid invoice 1042","provider_ref":"ws_CO_20102026090000123456789012","next_action":{"type":"none"},"settled_at":"2026-10-20T09:00:18Z","direction":"collection","metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"source":"billgrid","channel":"mpesa","currency":"KES"}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"examples":{"default":{"summary":"Request error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"internal_error","message":"An internal error occurred"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Retrieve one transaction","x-required-scope":"read","operationId":"getTransaction","security":[{"apiKey":[]}],"x-docs-public":true,"x-codeSamples":[{"source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/transactions/0197d1f0-21a0-7000-8000-000000000003\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'","lang":"cURL","label":"cURL"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/transactions/0197d1f0-21a0-7000-8000-000000000003');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#","label":"C#"}],"tags":["Transactions"],"summary":"Get a transaction","parameters":[{"required":true,"schema":{"type":"string"},"name":"id","in":"path"},{"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application","in":"header","required":false}]}},"/v1/payment-methods":{"get":{"operationId":"listPaymentMethods","security":[{"apiKey":[]}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethodList"},"examples":{"default":{"summary":"Success","value":{"object":"list","data":[{"mode":"sandbox","channels":["mpesa"],"enabled":true,"configured":true,"callback_url":"https://pay.example.com/hooks/ra/7GkN2pVbR4tHc9WdZ","retired_at":null,"id":"0197d1f0-21a0-7000-8000-000000000004","provider":"daraja","label":"BillGrid M-Pesa sandbox","config_hint":{"shortcode":"174379","consumer_key":"…demo","consumer_secret":"…demo","passkey":"…demo"},"last_verified_at":"2026-10-20T09:00:00Z","created_at":"2026-10-20T09:00:00Z","object":"payment_method","application_id":"0197d1f0-21a0-7000-8000-000000000002"}],"next_cursor":null}}}}},"description":"Success"},"401":{"description":"Authentication required","content":{"application/json":{"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}},"summary":"Request conflicts with existing state"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"message":"An internal error occurred","request_id":"req_docs_invoice_1042","code":"internal_error"}}}}}}}},"description":"List configured providers; credentials are never returned","x-required-scope":"read","tags":["Payment methods"],"summary":"List gateway accounts","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}},{"name":"include_retired","in":"query","required":false,"schema":{"type":"boolean"},"description":"Include historical credential versions for statement account selection."}],"x-docs-public":true,"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/payment-methods?include_retired=true\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods?include_retired=true\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods?include_retired=true\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-methods?include_retired=true\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-methods?include_retired=true');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/payment-methods?include_retired=true\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-methods?include_retired=true\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-methods?include_retired=true\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/payment-methods?include_retired=true\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}]}},"/v1/payment-methods/{provider}/verify":{"post":{"parameters":[{"required":true,"schema":{"type":"string"},"name":"provider","in":"path"},{"in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application"},{"schema":{"enum":["live","sandbox"],"type":"string"},"description":"Select the exact environment for inspection or mutation.","name":"mode","in":"query","required":false}],"responses":{"404":{"description":"Resource not found","content":{"application/json":{"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"request_id":"req_docs_invoice_1042","code":"idempotency_mismatch","message":"This idempotency key was already used with a different request"}}}}}}},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}},"description":"Rate limited"},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"internal_error","message":"An internal error occurred"}}}}}}},"502":{"description":"Gateway unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Gateway unavailable","value":{"error":{"code":"provider_unavailable","message":"The provider is temporarily unavailable","request_id":"req_docs_invoice_1042"}}}}}}},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethod"},"examples":{"default":{"summary":"Success","value":{"id":"0197d1f0-21a0-7000-8000-000000000004","object":"payment_method","channels":["mpesa"],"enabled":true,"configured":true,"config_hint":{"shortcode":"174379","consumer_key":"…demo","consumer_secret":"…demo","passkey":"…demo"},"callback_url":"https://pay.example.com/hooks/ra/7GkN2pVbR4tHc9WdZ","application_id":"0197d1f0-21a0-7000-8000-000000000002","provider":"daraja","mode":"sandbox","label":"BillGrid M-Pesa sandbox","last_verified_at":"2026-10-20T09:00:00Z","retired_at":null,"created_at":"2026-10-20T09:00:00Z"}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"message":"Provide a valid API key","request_id":"req_docs_invoice_1042","code":"unauthorized"}},"summary":"Authentication required"}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"message":"This key cannot select another project","request_id":"req_docs_invoice_1042","code":"forbidden"}}}}}}}},"tags":["Payment methods"],"operationId":"verifyPaymentMethod","summary":"Verify credentials","x-docs-public":true,"description":"Prove the credentials work against the provider","x-required-scope":"manage","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/payment-methods/daraja/verify?mode=sandbox\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods/daraja/verify?mode=sandbox\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"JavaScript","label":"JavaScript"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods/daraja/verify?mode=sandbox\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-methods/daraja/verify?mode=sandbox\",\n    method=\"POST\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-methods/daraja/verify?mode=sandbox');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);","lang":"PHP"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/payment-methods/daraja/verify?mode=sandbox\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-methods/daraja/verify?mode=sandbox\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-methods/daraja/verify?mode=sandbox\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/payment-methods/daraja/verify?mode=sandbox\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"security":[{"apiKey":[]}]}},"/v1/statements/{id}":{"get":{"operationId":"getStatement","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}}],"responses":{"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatementImport"},"examples":{"default":{"value":{"source":"mpesa","currency":"KES","duplicates":0,"unmatched":0,"settled_minor":125000,"application_id":"0197d1f0-21a0-7000-8000-000000000002","unmatched_minor":0,"created_at":"2026-10-20T10:00:00Z","id":"0197d1f0-21a0-7000-8000-000000000009","payment_method_id":"0197d1f0-21a0-7000-8000-000000000004","ambiguous":0,"replayed":false,"object":"statement_import","label":"M-Pesa settlement · 20 October","row_count":1,"settled":1,"rows":[{"detail":"Matched and settled","receipt":"UJK7A4M2Q9","amount_minor":125000,"outcome":"settled","transaction_id":"0197d1f0-21a0-7000-8000-000000000003","reference":"BG-INV-2026-1042"}]},"summary":"Success"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}},"description":"Insufficient access"},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-docs-public":true,"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/statements/0197d1f0-21a0-7000-8000-000000000009\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/statements/0197d1f0-21a0-7000-8000-000000000009\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/statements/0197d1f0-21a0-7000-8000-000000000009\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript","label":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/statements/0197d1f0-21a0-7000-8000-000000000009\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/statements/0197d1f0-21a0-7000-8000-000000000009');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/statements/0197d1f0-21a0-7000-8000-000000000009\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}","lang":"Go"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/statements/0197d1f0-21a0-7000-8000-000000000009\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/statements/0197d1f0-21a0-7000-8000-000000000009\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/statements/0197d1f0-21a0-7000-8000-000000000009\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"tags":["Statements"],"summary":"Get statement results","security":[{"apiKey":[]}],"description":"One import and what came of every row","x-required-scope":"read"}},"/v1/webhook-endpoints":{"post":{"operationId":"createWebhookEndpoint","x-docs-public":true,"x-required-scope":"manage","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/webhook-endpoints\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  --data-raw '{\n  \"url\": \"https://billgrid.example.com/webhooks/noria-pay\",\n  \"description\": \"Invoice reconciliation\",\n  \"event_types\": [\n    \"succeeded\",\n    \"failed\",\n    \"outcome_unknown\",\n    \"reversed\"\n  ],\n  \"include_provider_payload\": false\n}'"},{"source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-endpoints\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"url\": \"https://billgrid.example.com/webhooks/noria-pay\",\n    \"description\": \"Invoice reconciliation\",\n    \"event_types\": [\n      \"succeeded\",\n      \"failed\",\n      \"outcome_unknown\",\n      \"reversed\"\n    ],\n    \"include_provider_payload\": false\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"JavaScript","label":"JavaScript"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-endpoints\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"url\": \"https://billgrid.example.com/webhooks/noria-pay\",\n    \"description\": \"Invoice reconciliation\",\n    \"event_types\": [\n      \"succeeded\",\n      \"failed\",\n      \"outcome_unknown\",\n      \"reversed\"\n    ],\n    \"include_provider_payload\": false\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"url\": \"https://billgrid.example.com/webhooks/noria-pay\",\n  \"description\": \"Invoice reconciliation\",\n  \"event_types\": [\n    \"succeeded\",\n    \"failed\",\n    \"outcome_unknown\",\n    \"reversed\"\n  ],\n  \"include_provider_payload\": False\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/webhook-endpoints\",\n    method=\"POST\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/webhook-endpoints');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'url' => 'https://billgrid.example.com/webhooks/noria-pay',\n      'description' => 'Invoice reconciliation',\n      'event_types' => [\n        'succeeded',\n        'failed',\n        'outcome_unknown',\n        'reversed'\n      ],\n      'include_provider_payload' => false\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);","lang":"PHP"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"url\\\": \\\"https://billgrid.example.com/webhooks/noria-pay\\\",\\n  \\\"description\\\": \\\"Invoice reconciliation\\\",\\n  \\\"event_types\\\": [\\n    \\\"succeeded\\\",\\n    \\\"failed\\\",\\n    \\\"outcome_unknown\\\",\\n    \\\"reversed\\\"\\n  ],\\n  \\\"include_provider_payload\\\": false\\n}\")\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/webhook-endpoints\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/webhook-endpoints\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = JSON.generate({\n  \"url\" => \"https://billgrid.example.com/webhooks/noria-pay\",\n  \"description\" => \"Invoice reconciliation\",\n  \"event_types\" => [\n    \"succeeded\",\n    \"failed\",\n    \"outcome_unknown\",\n    \"reversed\"\n  ],\n  \"include_provider_payload\" => false\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/webhook-endpoints\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"url\\\": \\\"https://billgrid.example.com/webhooks/noria-pay\\\",\\n  \\\"description\\\": \\\"Invoice reconciliation\\\",\\n  \\\"event_types\\\": [\\n    \\\"succeeded\\\",\\n    \\\"failed\\\",\\n    \\\"outcome_unknown\\\",\\n    \\\"reversed\\\"\\n  ],\\n  \\\"include_provider_payload\\\": false\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/webhook-endpoints\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Content = new StringContent(@\"{\n  \"\"url\"\": \"\"https://billgrid.example.com/webhooks/noria-pay\"\",\n  \"\"description\"\": \"\"Invoice reconciliation\"\",\n  \"\"event_types\"\": [\n    \"\"succeeded\"\",\n    \"\"failed\"\",\n    \"\"outcome_unknown\"\",\n    \"\"reversed\"\"\n  ],\n  \"\"include_provider_payload\"\": false\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#"}],"tags":["Webhooks"],"summary":"Create a webhook endpoint","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertWebhookEndpointRequest"},"examples":{"invoice":{"value":{"url":"https://billgrid.example.com/webhooks/noria-pay","description":"Invoice reconciliation","event_types":["succeeded","failed","outcome_unknown","reversed"],"include_provider_payload":false},"summary":"Receive normalized settlement events"}}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointCreated"},"examples":{"default":{"summary":"Success","value":{"secret":"whsec_documentation_example_store_yours_once","id":"0197d1f0-21a0-7000-8000-000000000006","object":"webhook_endpoint","application_id":"0197d1f0-21a0-7000-8000-000000000002","url":"https://billgrid.example.com/webhooks/noria-pay","created_at":"2026-10-20T09:00:00Z","source":"billgrid","description":"Invoice reconciliation","event_types":["succeeded","failed","outcome_unknown","reversed"],"enabled":true,"include_provider_payload":false}}}}}},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"validation_error","message":"Invalid request parameters","request_id":"req_docs_invoice_1042"}}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"request_id":"req_docs_invoice_1042","code":"unauthorized","message":"Provide a valid API key"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"message":"Configure a compatible gateway account for this project","request_id":"req_docs_invoice_1042","code":"provider_not_configured"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"examples":{"default":{"value":{"error":{"message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042","code":"rate_limited"}},"summary":"Rate limited"}},"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}},"summary":"Request error"}}}}}},"parameters":[{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}}],"description":"Register a settlement webhook; the secret is shown once"},"get":{"tags":["Webhooks"],"operationId":"listWebhookEndpoints","summary":"List webhook endpoints","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointList"},"examples":{"default":{"summary":"Success","value":{"object":"list","data":[{"created_at":"2026-10-20T09:00:00Z","id":"0197d1f0-21a0-7000-8000-000000000006","application_id":"0197d1f0-21a0-7000-8000-000000000002","source":"billgrid","event_types":["succeeded","failed","outcome_unknown","reversed"],"object":"webhook_endpoint","url":"https://billgrid.example.com/webhooks/noria-pay","description":"Invoice reconciliation","enabled":true,"include_provider_payload":false}],"next_cursor":null}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"examples":{"default":{"summary":"Authentication required","value":{"error":{"message":"Provide a valid API key","request_id":"req_docs_invoice_1042","code":"unauthorized"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}},"summary":"Request conflicts with existing state"}}}},"description":"Request conflicts with existing state"},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}}},"x-docs-public":true,"x-required-scope":"read","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/webhook-endpoints\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-endpoints\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-endpoints\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/webhook-endpoints\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/webhook-endpoints');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/webhook-endpoints\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}","lang":"Go","label":"Go"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/webhook-endpoints\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/webhook-endpoints\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}","lang":"Java"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/webhook-endpoints\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"security":[{"apiKey":[]}],"description":"List webhook endpoints"}},"/v1/channel-codes/refresh":{"post":{"responses":{"200":{"description":"Refreshed bank channel codes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelCodeCatalog"},"examples":{"default":{"summary":"Refreshed bank channel codes","value":{"bank":[{"code":"BillGrid invoice 1042","name":"BillGrid"}],"source":"billgrid","fetched_at":"2026-10-20T09:00:18Z","object":"channel_codes"}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}},"description":"Insufficient access"},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}},"description":"Rate limited"},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"request_id":"req_docs_invoice_1042","code":"internal_error","message":"An internal error occurred"}},"summary":"Request error"}}}}},"502":{"description":"Gateway unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"message":"The provider is temporarily unavailable","request_id":"req_docs_invoice_1042","code":"provider_unavailable"}},"summary":"Gateway unavailable"}}}}},"503":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}}},"description":"Refresh the cached payout bank codes from SasaPay","x-required-scope":"manage","tags":["Payment methods"],"operationId":"postV1ChannelCodesRefresh","x-docs-public":true,"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/channel-codes/refresh\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/channel-codes/refresh\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/channel-codes/refresh\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript","label":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/channel-codes/refresh\",\n    method=\"POST\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/channel-codes/refresh');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/channel-codes/refresh\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/channel-codes/refresh\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/channel-codes/refresh\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/channel-codes/refresh\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"summary":"Refresh bank codes","security":[{"apiKey":[]}]}},"/v1/charges/record":{"post":{"tags":["Charges"],"description":"A cash payment, a bank transfer, a receipt read off a statement. Succeeds immediately; no provider is called.","security":[{"apiKey":[]}],"parameters":[{"in":"header","required":true,"schema":{"type":"string","maxLength":255},"description":"Required on every write that moves money. The same key with the same body replays the original response; the same key with a different body is rejected.","name":"Idempotency-Key"},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}}],"x-docs-public":true,"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/charges/record\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -H 'Idempotency-Key: bg-cash-receipt-1042-cash-attempt-1' \\\n  --data-raw '{\n  \"amount_minor\": 125000,\n  \"currency\": \"KES\",\n  \"channel\": \"cash\",\n  \"reference\": \"BG-INV-2026-1042\",\n  \"description\": \"Cash received at BillGrid office\",\n  \"receipt\": \"CASH-1042\",\n  \"payer_name\": \"Amina Otieno\",\n  \"metadata\": {\n    \"invoice_id\": \"1042\",\n    \"customer_id\": \"cust_ami_204\"\n  }\n}'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/charges/record\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": \"bg-cash-receipt-1042-cash-attempt-1\",\n  },\n  body: JSON.stringify({\n    \"amount_minor\": 125000,\n    \"currency\": \"KES\",\n    \"channel\": \"cash\",\n    \"reference\": \"BG-INV-2026-1042\",\n    \"description\": \"Cash received at BillGrid office\",\n    \"receipt\": \"CASH-1042\",\n    \"payer_name\": \"Amina Otieno\",\n    \"metadata\": {\n      \"invoice_id\": \"1042\",\n      \"customer_id\": \"cust_ami_204\"\n    }\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/charges/record\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": \"bg-cash-receipt-1042-cash-attempt-1\",\n  },\n  body: JSON.stringify({\n    \"amount_minor\": 125000,\n    \"currency\": \"KES\",\n    \"channel\": \"cash\",\n    \"reference\": \"BG-INV-2026-1042\",\n    \"description\": \"Cash received at BillGrid office\",\n    \"receipt\": \"CASH-1042\",\n    \"payer_name\": \"Amina Otieno\",\n    \"metadata\": {\n      \"invoice_id\": \"1042\",\n      \"customer_id\": \"cust_ami_204\"\n    }\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"amount_minor\": 125000,\n  \"currency\": \"KES\",\n  \"channel\": \"cash\",\n  \"reference\": \"BG-INV-2026-1042\",\n  \"description\": \"Cash received at BillGrid office\",\n  \"receipt\": \"CASH-1042\",\n  \"payer_name\": \"Amina Otieno\",\n  \"metadata\": {\n    \"invoice_id\": \"1042\",\n    \"customer_id\": \"cust_ami_204\"\n  }\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/charges/record\",\n    method=\"POST\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n        \"Idempotency-Key\": \"bg-cash-receipt-1042-cash-attempt-1\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/charges/record');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n        'Idempotency-Key: bg-cash-receipt-1042-cash-attempt-1',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'amount_minor' => 125000,\n      'currency' => 'KES',\n      'channel' => 'cash',\n      'reference' => 'BG-INV-2026-1042',\n      'description' => 'Cash received at BillGrid office',\n      'receipt' => 'CASH-1042',\n      'payer_name' => 'Amina Otieno',\n      'metadata' => [\n        'invoice_id' => '1042',\n        'customer_id' => 'cust_ami_204'\n      ]\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);","lang":"PHP","label":"PHP"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"amount_minor\\\": 125000,\\n  \\\"currency\\\": \\\"KES\\\",\\n  \\\"channel\\\": \\\"cash\\\",\\n  \\\"reference\\\": \\\"BG-INV-2026-1042\\\",\\n  \\\"description\\\": \\\"Cash received at BillGrid office\\\",\\n  \\\"receipt\\\": \\\"CASH-1042\\\",\\n  \\\"payer_name\\\": \\\"Amina Otieno\\\",\\n  \\\"metadata\\\": {\\n    \\\"invoice_id\\\": \\\"1042\\\",\\n    \\\"customer_id\\\": \\\"cust_ami_204\\\"\\n  }\\n}\")\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/charges/record\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    req.Header.Set(\"Idempotency-Key\", \"bg-cash-receipt-1042-cash-attempt-1\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/charges/record\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest[\"Idempotency-Key\"] = \"bg-cash-receipt-1042-cash-attempt-1\"\nrequest.body = JSON.generate({\n  \"amount_minor\" => 125000,\n  \"currency\" => \"KES\",\n  \"channel\" => \"cash\",\n  \"reference\" => \"BG-INV-2026-1042\",\n  \"description\" => \"Cash received at BillGrid office\",\n  \"receipt\" => \"CASH-1042\",\n  \"payer_name\" => \"Amina Otieno\",\n  \"metadata\" => {\n    \"invoice_id\" => \"1042\",\n    \"customer_id\" => \"cust_ami_204\"\n  }\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/charges/record\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .header(\"Idempotency-Key\", \"bg-cash-receipt-1042-cash-attempt-1\")\n        .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"amount_minor\\\": 125000,\\n  \\\"currency\\\": \\\"KES\\\",\\n  \\\"channel\\\": \\\"cash\\\",\\n  \\\"reference\\\": \\\"BG-INV-2026-1042\\\",\\n  \\\"description\\\": \\\"Cash received at BillGrid office\\\",\\n  \\\"receipt\\\": \\\"CASH-1042\\\",\\n  \\\"payer_name\\\": \\\"Amina Otieno\\\",\\n  \\\"metadata\\\": {\\n    \\\"invoice_id\\\": \\\"1042\\\",\\n    \\\"customer_id\\\": \\\"cust_ami_204\\\"\\n  }\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/charges/record\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Headers.Add(\"Idempotency-Key\", \"bg-cash-receipt-1042-cash-attempt-1\");\nrequest.Content = new StringContent(@\"{\n  \"\"amount_minor\"\": 125000,\n  \"\"currency\"\": \"\"KES\"\",\n  \"\"channel\"\": \"\"cash\"\",\n  \"\"reference\"\": \"\"BG-INV-2026-1042\"\",\n  \"\"description\"\": \"\"Cash received at BillGrid office\"\",\n  \"\"receipt\"\": \"\"CASH-1042\"\",\n  \"\"payer_name\"\": \"\"Amina Otieno\"\",\n  \"\"metadata\"\": {\n    \"\"invoice_id\"\": \"\"1042\"\",\n    \"\"customer_id\"\": \"\"cust_ami_204\"\"\n  }\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"operationId":"recordCharge","summary":"Record a manual charge","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordChargeRequest"},"examples":{"cash":{"summary":"Record a cash receipt","value":{"currency":"KES","channel":"cash","reference":"BG-INV-2026-1042","description":"Cash received at BillGrid office","receipt":"CASH-1042","payer_name":"Amina Otieno","metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"amount_minor":125000}}}}}},"responses":{"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}},"description":"Request error"},"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transaction"},"examples":{"cash":{"summary":"Cash recorded as settled","value":{"payer_name":"Amina Otieno","recipient_account":null,"provider_ref":null,"workspace_id":"0197d1f0-21a0-7000-8000-000000000001","currency":"KES","amount_minor":125000,"description":"BillGrid invoice 1042","provider_receipt":"CASH-1042","failure_reason":null,"next_action":{"type":"none"},"channel":"cash","settled_minor":125000,"recipient_phone":null,"metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"created_at":"2026-10-20T09:00:00Z","object":"transaction","status":"succeeded","provider":"manual","mode":"sandbox","payer_email":null,"reference":"BG-INV-2026-1042","parent_transaction_id":null,"payer_phone":"254712345678","recipient_name":null,"direction":"collection","expires_at":"2026-10-20T09:05:00Z","fee_minor":null,"payment_link_id":null,"settled_at":"2026-10-20T09:00:18Z","id":"0197d1f0-21a0-7000-8000-000000000003","application_id":"0197d1f0-21a0-7000-8000-000000000002","source":"billgrid"}}}}}},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"validation_error","message":"Invalid request parameters","request_id":"req_docs_invoice_1042"}}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Insufficient access","content":{"application/json":{"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"provider_not_configured","message":"Configure a compatible gateway account for this project","request_id":"req_docs_invoice_1042"}},"summary":"Validation or gateway configuration error"}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042","code":"rate_limited"}}}}}}}},"x-required-scope":"reconcile"}},"/v1/transactions":{"get":{"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/transactions?limit=20&reference=BG-INV-2026-1042\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/transactions?limit=20&reference=BG-INV-2026-1042\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/transactions?limit=20&reference=BG-INV-2026-1042\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/transactions?limit=20&reference=BG-INV-2026-1042\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error","lang":"Python"},{"source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/transactions?limit=20&reference=BG-INV-2026-1042');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);","lang":"PHP","label":"PHP"},{"label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/transactions?limit=20&reference=BG-INV-2026-1042\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}","lang":"Go"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/transactions?limit=20&reference=BG-INV-2026-1042\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/transactions?limit=20&reference=BG-INV-2026-1042\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/transactions?limit=20&reference=BG-INV-2026-1042\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#"}],"operationId":"listTransactions","summary":"List transactions","security":[{"apiKey":[]}],"responses":{"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"message":"An internal error occurred","request_id":"req_docs_invoice_1042","code":"internal_error"}}}}}}},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionList"},"examples":{"default":{"summary":"Success","value":{"object":"list","data":[{"channel":"mpesa","mode":"sandbox","settled_minor":125000,"reference":"BG-INV-2026-1042","description":"BillGrid invoice 1042","parent_transaction_id":null,"next_action":{"type":"none"},"currency":"KES","recipient_phone":null,"recipient_account":null,"failure_reason":null,"expires_at":"2026-10-20T09:05:00Z","provider":"daraja","provider_receipt":"UJK7A4M2Q9","workspace_id":"0197d1f0-21a0-7000-8000-000000000001","payment_link_id":null,"payer_name":"Amina Otieno","application_id":"0197d1f0-21a0-7000-8000-000000000002","source":"billgrid","direction":"collection","payer_phone":"254712345678","recipient_name":null,"provider_ref":"ws_CO_20102026090000123456789012","amount_minor":125000,"metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"settled_at":"2026-10-20T09:00:18Z","id":"0197d1f0-21a0-7000-8000-000000000003","object":"transaction","status":"succeeded","fee_minor":null,"payer_email":null,"created_at":"2026-10-20T09:00:00Z"}],"next_cursor":null}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"message":"Provide a valid API key","request_id":"req_docs_invoice_1042","code":"unauthorized"}}}}}}},"403":{"content":{"application/json":{"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Insufficient access"},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}}},"description":"List collections, payouts and refunds together","tags":["Transactions"],"parameters":[{"in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"name":"limit"},{"name":"cursor","in":"query","schema":{"type":"string"}},{"schema":{"type":"string","enum":["collection","payout","refund"]},"name":"direction","in":"query"},{"name":"status","in":"query","schema":{"type":"string","enum":["pending","processing","succeeded","failed","cancelled","expired","reversed","unknown"]}},{"name":"provider","in":"query","schema":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","maxLength":64}},{"name":"channel","in":"query","schema":{"type":"string","enum":["mpesa","airtel","card","bank","wallet","cash"]}},{"name":"reference","in":"query","schema":{"type":"string"}},{"name":"receipt","in":"query","schema":{"type":"string"}},{"name":"payment_link_id","in":"query","schema":{"type":"string"},"description":"Only transactions collected through this payment link."},{"description":"Only refunds of this transaction.","name":"parent_transaction_id","in":"query","schema":{"type":"string"}},{"name":"source","in":"query","required":false,"schema":{"type":"string","pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64,"description":"Originating application, matching a registered webhook endpoint source."}},{"required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"format":"uuid","type":"string"},"name":"X-Noria-Application","in":"header"}],"x-docs-public":true,"x-required-scope":"read"}},"/v1/transactions/{id}/events":{"get":{"operationId":"listTransactionEvents","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string"},"name":"id","in":"path","required":true},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"schema":{"type":"string"},"name":"cursor","in":"query"},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionEventList"},"examples":{"default":{"summary":"Success","value":{"object":"list","data":[{"payload":{"provider_receipt":"UJK7A4M2Q9"},"occurred_at":"2026-10-20T09:00:18Z","id":"0197d1f0-21a0-7000-8000-000000000007","object":"transaction.event","transaction_id":"0197d1f0-21a0-7000-8000-000000000003","type":"succeeded","sequence":3}],"next_cursor":null}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}},"description":"Resource not found"},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}},"summary":"Rate limited"}}}},"description":"Rate limited"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}},"summary":"Request error"}}}},"description":"Request error"}},"x-docs-public":true,"x-required-scope":"read","tags":["Transactions"],"summary":"List transaction events","description":"The full audit trail, including every raw provider exchange","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/transactions/0197d1f0-21a0-7000-8000-000000000003/events\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003/events\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003/events\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript","label":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003/events\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/transactions/0197d1f0-21a0-7000-8000-000000000003/events');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003/events\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003/events\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003/events\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/transactions/0197d1f0-21a0-7000-8000-000000000003/events\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}]}},"/v1/payment-methods/{provider}":{"put":{"operationId":"upsertPaymentMethod","summary":"Configure gateway credentials","description":"Credentials are sealed at rest and never read back. The response carries the callback URL to register with the provider.","responses":{"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"code":"provider_not_configured","message":"Configure a compatible gateway account for this project","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"message":"An internal error occurred","request_id":"req_docs_invoice_1042","code":"internal_error"}},"summary":"Request error"}}}}},"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethod"},"examples":{"default":{"value":{"retired_at":null,"created_at":"2026-10-20T09:00:00Z","id":"0197d1f0-21a0-7000-8000-000000000004","object":"payment_method","provider":"daraja","mode":"sandbox","enabled":true,"config_hint":{"shortcode":"174379","consumer_key":"…demo","consumer_secret":"…demo","passkey":"…demo"},"callback_url":"https://pay.example.com/hooks/ra/7GkN2pVbR4tHc9WdZ","last_verified_at":"2026-10-20T09:00:00Z","application_id":"0197d1f0-21a0-7000-8000-000000000002","label":"BillGrid M-Pesa sandbox","channels":["mpesa"],"configured":true},"summary":"Success"}}}},"description":"Success"},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"validation_error","message":"Invalid request parameters"}}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"request_id":"req_docs_invoice_1042","code":"unauthorized","message":"Provide a valid API key"}}}}}}},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}},"summary":"Insufficient access"}}}},"description":"Insufficient access"},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}}},"x-required-scope":"manage","tags":["Payment methods"],"security":[{"apiKey":[]}],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"format":"uuid","type":"string"},"name":"X-Noria-Application"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertPaymentMethodRequest"},"examples":{"sandbox":{"summary":"Configure the project's Daraja sandbox account","value":{"mode":"sandbox","label":"BillGrid M-Pesa sandbox","channels":["mpesa"],"enabled":true,"config":{"consumer_key":"replace_with_sandbox_consumer_key","consumer_secret":"replace_with_sandbox_consumer_secret","passkey":"replace_with_sandbox_passkey","provider":"daraja","shortcode":"174379"}}}}}},"required":true},"x-docs-public":true,"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X PUT \"$NORIA_API_URL/v1/payment-methods/daraja\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  --data-raw '{\n  \"mode\": \"sandbox\",\n  \"label\": \"BillGrid M-Pesa sandbox\",\n  \"channels\": [\n    \"mpesa\"\n  ],\n  \"enabled\": true,\n  \"config\": {\n    \"provider\": \"daraja\",\n    \"shortcode\": \"174379\",\n    \"consumer_key\": \"replace_with_sandbox_consumer_key\",\n    \"consumer_secret\": \"replace_with_sandbox_consumer_secret\",\n    \"passkey\": \"replace_with_sandbox_passkey\"\n  }\n}'"},{"label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods/daraja\", {\n  method: \"PUT\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"mode\": \"sandbox\",\n    \"label\": \"BillGrid M-Pesa sandbox\",\n    \"channels\": [\n      \"mpesa\"\n    ],\n    \"enabled\": true,\n    \"config\": {\n      \"provider\": \"daraja\",\n      \"shortcode\": \"174379\",\n      \"consumer_key\": \"replace_with_sandbox_consumer_key\",\n      \"consumer_secret\": \"replace_with_sandbox_consumer_secret\",\n      \"passkey\": \"replace_with_sandbox_passkey\"\n    }\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"JavaScript"},{"label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods/daraja\", {\n  method: \"PUT\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"mode\": \"sandbox\",\n    \"label\": \"BillGrid M-Pesa sandbox\",\n    \"channels\": [\n      \"mpesa\"\n    ],\n    \"enabled\": true,\n    \"config\": {\n      \"provider\": \"daraja\",\n      \"shortcode\": \"174379\",\n      \"consumer_key\": \"replace_with_sandbox_consumer_key\",\n      \"consumer_secret\": \"replace_with_sandbox_consumer_secret\",\n      \"passkey\": \"replace_with_sandbox_passkey\"\n    }\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"mode\": \"sandbox\",\n  \"label\": \"BillGrid M-Pesa sandbox\",\n  \"channels\": [\n    \"mpesa\"\n  ],\n  \"enabled\": True,\n  \"config\": {\n    \"provider\": \"daraja\",\n    \"shortcode\": \"174379\",\n    \"consumer_key\": \"replace_with_sandbox_consumer_key\",\n    \"consumer_secret\": \"replace_with_sandbox_consumer_secret\",\n    \"passkey\": \"replace_with_sandbox_passkey\"\n  }\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-methods/daraja\",\n    method=\"PUT\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-methods/daraja');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'PUT',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'mode' => 'sandbox',\n      'label' => 'BillGrid M-Pesa sandbox',\n      'channels' => [\n        'mpesa'\n      ],\n      'enabled' => true,\n      'config' => [\n        'provider' => 'daraja',\n        'shortcode' => '174379',\n        'consumer_key' => 'replace_with_sandbox_consumer_key',\n        'consumer_secret' => 'replace_with_sandbox_consumer_secret',\n        'passkey' => 'replace_with_sandbox_passkey'\n      ]\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"mode\\\": \\\"sandbox\\\",\\n  \\\"label\\\": \\\"BillGrid M-Pesa sandbox\\\",\\n  \\\"channels\\\": [\\n    \\\"mpesa\\\"\\n  ],\\n  \\\"enabled\\\": true,\\n  \\\"config\\\": {\\n    \\\"provider\\\": \\\"daraja\\\",\\n    \\\"shortcode\\\": \\\"174379\\\",\\n    \\\"consumer_key\\\": \\\"replace_with_sandbox_consumer_key\\\",\\n    \\\"consumer_secret\\\": \\\"replace_with_sandbox_consumer_secret\\\",\\n    \\\"passkey\\\": \\\"replace_with_sandbox_passkey\\\"\\n  }\\n}\")\n    req, err := http.NewRequest(\"PUT\", baseURL + \"/v1/payment-methods/daraja\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-methods/daraja\")\nrequest = Net::HTTP::Put.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = JSON.generate({\n  \"mode\" => \"sandbox\",\n  \"label\" => \"BillGrid M-Pesa sandbox\",\n  \"channels\" => [\n    \"mpesa\"\n  ],\n  \"enabled\" => true,\n  \"config\" => {\n    \"provider\" => \"daraja\",\n    \"shortcode\" => \"174379\",\n    \"consumer_key\" => \"replace_with_sandbox_consumer_key\",\n    \"consumer_secret\" => \"replace_with_sandbox_consumer_secret\",\n    \"passkey\" => \"replace_with_sandbox_passkey\"\n  }\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-methods/daraja\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .method(\"PUT\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"mode\\\": \\\"sandbox\\\",\\n  \\\"label\\\": \\\"BillGrid M-Pesa sandbox\\\",\\n  \\\"channels\\\": [\\n    \\\"mpesa\\\"\\n  ],\\n  \\\"enabled\\\": true,\\n  \\\"config\\\": {\\n    \\\"provider\\\": \\\"daraja\\\",\\n    \\\"shortcode\\\": \\\"174379\\\",\\n    \\\"consumer_key\\\": \\\"replace_with_sandbox_consumer_key\\\",\\n    \\\"consumer_secret\\\": \\\"replace_with_sandbox_consumer_secret\\\",\\n    \\\"passkey\\\": \\\"replace_with_sandbox_passkey\\\"\\n  }\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}","lang":"Java","label":"Java"},{"source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"PUT\"), baseUrl + \"/v1/payment-methods/daraja\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Content = new StringContent(@\"{\n  \"\"mode\"\": \"\"sandbox\"\",\n  \"\"label\"\": \"\"BillGrid M-Pesa sandbox\"\",\n  \"\"channels\"\": [\n    \"\"mpesa\"\"\n  ],\n  \"\"enabled\"\": true,\n  \"\"config\"\": {\n    \"\"provider\"\": \"\"daraja\"\",\n    \"\"shortcode\"\": \"\"174379\"\",\n    \"\"consumer_key\"\": \"\"replace_with_sandbox_consumer_key\"\",\n    \"\"consumer_secret\"\": \"\"replace_with_sandbox_consumer_secret\"\",\n    \"\"passkey\"\": \"\"replace_with_sandbox_passkey\"\"\n  }\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#","label":"C#"}]},"patch":{"tags":["Payment methods"],"operationId":"setPaymentMethodEnabled","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPaymentMethodEnabledRequest"},"examples":{"pause":{"summary":"Pause new payments through this account","value":{"enabled":false}}}}}},"responses":{"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethod"},"examples":{"paused":{"summary":"Gateway account paused","value":{"last_verified_at":"2026-10-20T09:00:00Z","created_at":"2026-10-20T09:00:00Z","id":"0197d1f0-21a0-7000-8000-000000000004","object":"payment_method","provider":"daraja","mode":"sandbox","label":"BillGrid M-Pesa sandbox","configured":true,"config_hint":{"shortcode":"174379","consumer_key":"…demo","consumer_secret":"…demo","passkey":"…demo"},"retired_at":null,"application_id":"0197d1f0-21a0-7000-8000-000000000002","channels":["mpesa"],"enabled":false,"callback_url":"https://pay.example.com/hooks/ra/7GkN2pVbR4tHc9WdZ"}}}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}},"description":"Authentication required"},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"message":"Transaction not found","request_id":"req_docs_invoice_1042","code":"not_found"}}}}}},"description":"Resource not found"},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"content":{"application/json":{"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"code":"provider_not_configured","message":"Configure a compatible gateway account for this project","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation or gateway configuration error"},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042","code":"rate_limited"}}}}}}}},"x-docs-public":true,"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X PATCH \"$NORIA_API_URL/v1/payment-methods/daraja\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  --data-raw '{\n  \"enabled\": false\n}'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods/daraja\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"enabled\": false\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods/daraja\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"enabled\": false\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"enabled\": False\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-methods/daraja\",\n    method=\"PATCH\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-methods/daraja');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'PATCH',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'enabled' => false\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"enabled\\\": false\\n}\")\n    req, err := http.NewRequest(\"PATCH\", baseURL + \"/v1/payment-methods/daraja\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-methods/daraja\")\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = JSON.generate({\n  \"enabled\" => false\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-methods/daraja\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"enabled\\\": false\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}","lang":"Java"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"PATCH\"), baseUrl + \"/v1/payment-methods/daraja\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Content = new StringContent(@\"{\n  \"\"enabled\"\": false\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"summary":"Enable or pause an account","description":"Keeps the credentials. A provider that is off takes no new payments; one already under way still settles.","parameters":[{"required":true,"schema":{"type":"string"},"name":"provider","in":"path"},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}},{"name":"mode","in":"query","required":false,"schema":{"type":"string","enum":["live","sandbox"]},"description":"Select the exact environment for inspection or mutation."}],"x-required-scope":"manage"},"delete":{"tags":["Payment methods"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects."},{"name":"mode","in":"query","required":false,"schema":{"type":"string","enum":["live","sandbox"]},"description":"Select the exact environment for inspection or mutation."}],"x-docs-public":true,"description":"Remove a provider","operationId":"removePaymentMethod","summary":"Retire a gateway account","security":[{"apiKey":[]}],"responses":{"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}},"summary":"Request error"}}}}},"204":{"description":"Removed"},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"request_id":"req_docs_invoice_1042","code":"forbidden","message":"This key cannot select another project"}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"message":"Transaction not found","request_id":"req_docs_invoice_1042","code":"not_found"}}}}}}}},"x-required-scope":"manage","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X DELETE \"$NORIA_API_URL/v1/payment-methods/daraja?mode=sandbox\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods/daraja?mode=sandbox\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"JavaScript","label":"JavaScript"},{"label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods/daraja?mode=sandbox\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-methods/daraja?mode=sandbox\",\n    method=\"DELETE\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-methods/daraja?mode=sandbox');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'DELETE',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"DELETE\", baseURL + \"/v1/payment-methods/daraja?mode=sandbox\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-methods/daraja?mode=sandbox\")\nrequest = Net::HTTP::Delete.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-methods/daraja?mode=sandbox\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"DELETE\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"DELETE\"), baseUrl + \"/v1/payment-methods/daraja?mode=sandbox\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#"}]}},"/v1/payment-links":{"post":{"tags":["Payment links"],"operationId":"createPaymentLink","parameters":[{"in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"format":"uuid","type":"string"},"name":"X-Noria-Application"}],"description":"Create a hosted checkout link","x-required-scope":"manage","summary":"Create a payment link","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertPaymentLinkRequest"},"examples":{"invoice":{"summary":"Create an invoice checkout","value":{"reference":"BG-INV-2026-1042","return_url":"https://billgrid.example.com/invoices/1042","metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"title":"BillGrid invoice 1042","description":"October service subscription","amount_minor":125000,"currency":"KES","channels":["mpesa","card"]}}}}}},"responses":{"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"201":{"description":"Success","content":{"application/json":{"examples":{"default":{"summary":"Success","value":{"description":"October service subscription","currency":"KES","max_amount_minor":null,"reference":"BG-INV-2026-1042","active":true,"paid_count":3,"source":"billgrid","url":"https://pay.example.com/p/pl_billgrid_invoice_1042_example","object":"payment_link","title":"BillGrid invoice 1042","min_amount_minor":null,"channels":["mpesa","card"],"collected_minor":375000,"id":"0197d1f0-21a0-7000-8000-000000000005","application_id":"0197d1f0-21a0-7000-8000-000000000002","token":"pl_billgrid_invoice_1042_example","amount_minor":125000,"allow_custom_amount":false,"created_at":"2026-10-20T09:00:00Z"}}},"schema":{"$ref":"#/components/schemas/PaymentLink"}}}},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"validation_error","message":"Invalid request parameters","request_id":"req_docs_invoice_1042"}}}}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}},"description":"Authentication required"},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"message":"This key cannot select another project","request_id":"req_docs_invoice_1042","code":"forbidden"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"code":"provider_not_configured","message":"Configure a compatible gateway account for this project","request_id":"req_docs_invoice_1042"}}}}}}}},"x-docs-public":true,"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/payment-links\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  --data-raw '{\n  \"title\": \"BillGrid invoice 1042\",\n  \"description\": \"October service subscription\",\n  \"amount_minor\": 125000,\n  \"currency\": \"KES\",\n  \"channels\": [\n    \"mpesa\",\n    \"card\"\n  ],\n  \"reference\": \"BG-INV-2026-1042\",\n  \"return_url\": \"https://billgrid.example.com/invoices/1042\",\n  \"metadata\": {\n    \"invoice_id\": \"1042\",\n    \"customer_id\": \"cust_ami_204\"\n  }\n}'"},{"source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"title\": \"BillGrid invoice 1042\",\n    \"description\": \"October service subscription\",\n    \"amount_minor\": 125000,\n    \"currency\": \"KES\",\n    \"channels\": [\n      \"mpesa\",\n      \"card\"\n    ],\n    \"reference\": \"BG-INV-2026-1042\",\n    \"return_url\": \"https://billgrid.example.com/invoices/1042\",\n    \"metadata\": {\n      \"invoice_id\": \"1042\",\n      \"customer_id\": \"cust_ami_204\"\n    }\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"JavaScript","label":"JavaScript"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"title\": \"BillGrid invoice 1042\",\n    \"description\": \"October service subscription\",\n    \"amount_minor\": 125000,\n    \"currency\": \"KES\",\n    \"channels\": [\n      \"mpesa\",\n      \"card\"\n    ],\n    \"reference\": \"BG-INV-2026-1042\",\n    \"return_url\": \"https://billgrid.example.com/invoices/1042\",\n    \"metadata\": {\n      \"invoice_id\": \"1042\",\n      \"customer_id\": \"cust_ami_204\"\n    }\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"title\": \"BillGrid invoice 1042\",\n  \"description\": \"October service subscription\",\n  \"amount_minor\": 125000,\n  \"currency\": \"KES\",\n  \"channels\": [\n    \"mpesa\",\n    \"card\"\n  ],\n  \"reference\": \"BG-INV-2026-1042\",\n  \"return_url\": \"https://billgrid.example.com/invoices/1042\",\n  \"metadata\": {\n    \"invoice_id\": \"1042\",\n    \"customer_id\": \"cust_ami_204\"\n  }\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-links\",\n    method=\"POST\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error","lang":"Python","label":"Python"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-links');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'title' => 'BillGrid invoice 1042',\n      'description' => 'October service subscription',\n      'amount_minor' => 125000,\n      'currency' => 'KES',\n      'channels' => [\n        'mpesa',\n        'card'\n      ],\n      'reference' => 'BG-INV-2026-1042',\n      'return_url' => 'https://billgrid.example.com/invoices/1042',\n      'metadata' => [\n        'invoice_id' => '1042',\n        'customer_id' => 'cust_ami_204'\n      ]\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"title\\\": \\\"BillGrid invoice 1042\\\",\\n  \\\"description\\\": \\\"October service subscription\\\",\\n  \\\"amount_minor\\\": 125000,\\n  \\\"currency\\\": \\\"KES\\\",\\n  \\\"channels\\\": [\\n    \\\"mpesa\\\",\\n    \\\"card\\\"\\n  ],\\n  \\\"reference\\\": \\\"BG-INV-2026-1042\\\",\\n  \\\"return_url\\\": \\\"https://billgrid.example.com/invoices/1042\\\",\\n  \\\"metadata\\\": {\\n    \\\"invoice_id\\\": \\\"1042\\\",\\n    \\\"customer_id\\\": \\\"cust_ami_204\\\"\\n  }\\n}\")\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/payment-links\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-links\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = JSON.generate({\n  \"title\" => \"BillGrid invoice 1042\",\n  \"description\" => \"October service subscription\",\n  \"amount_minor\" => 125000,\n  \"currency\" => \"KES\",\n  \"channels\" => [\n    \"mpesa\",\n    \"card\"\n  ],\n  \"reference\" => \"BG-INV-2026-1042\",\n  \"return_url\" => \"https://billgrid.example.com/invoices/1042\",\n  \"metadata\" => {\n    \"invoice_id\" => \"1042\",\n    \"customer_id\" => \"cust_ami_204\"\n  }\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result","lang":"Ruby","label":"Ruby"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-links\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"title\\\": \\\"BillGrid invoice 1042\\\",\\n  \\\"description\\\": \\\"October service subscription\\\",\\n  \\\"amount_minor\\\": 125000,\\n  \\\"currency\\\": \\\"KES\\\",\\n  \\\"channels\\\": [\\n    \\\"mpesa\\\",\\n    \\\"card\\\"\\n  ],\\n  \\\"reference\\\": \\\"BG-INV-2026-1042\\\",\\n  \\\"return_url\\\": \\\"https://billgrid.example.com/invoices/1042\\\",\\n  \\\"metadata\\\": {\\n    \\\"invoice_id\\\": \\\"1042\\\",\\n    \\\"customer_id\\\": \\\"cust_ami_204\\\"\\n  }\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/payment-links\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Content = new StringContent(@\"{\n  \"\"title\"\": \"\"BillGrid invoice 1042\"\",\n  \"\"description\"\": \"\"October service subscription\"\",\n  \"\"amount_minor\"\": 125000,\n  \"\"currency\"\": \"\"KES\"\",\n  \"\"channels\"\": [\n    \"\"mpesa\"\",\n    \"\"card\"\"\n  ],\n  \"\"reference\"\": \"\"BG-INV-2026-1042\"\",\n  \"\"return_url\"\": \"\"https://billgrid.example.com/invoices/1042\"\",\n  \"\"metadata\"\": {\n    \"\"invoice_id\"\": \"\"1042\"\",\n    \"\"customer_id\"\": \"\"cust_ami_204\"\"\n  }\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}]},"get":{"x-docs-public":true,"x-required-scope":"read","operationId":"listPaymentLinks","responses":{"200":{"description":"Success","content":{"application/json":{"examples":{"default":{"value":{"data":[{"channels":["mpesa","card"],"object":"payment_link","source":"billgrid","token":"pl_billgrid_invoice_1042_example","title":"BillGrid invoice 1042","description":"October service subscription","amount_minor":125000,"min_amount_minor":null,"max_amount_minor":null,"allow_custom_amount":false,"reference":"BG-INV-2026-1042","active":true,"paid_count":3,"application_id":"0197d1f0-21a0-7000-8000-000000000002","url":"https://pay.example.com/p/pl_billgrid_invoice_1042_example","collected_minor":375000,"id":"0197d1f0-21a0-7000-8000-000000000005","currency":"KES","created_at":"2026-10-20T09:00:00Z"}],"next_cursor":null,"object":"list"},"summary":"Success"}},"schema":{"$ref":"#/components/schemas/PaymentLinkList"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"request_id":"req_docs_invoice_1042","code":"unauthorized","message":"Provide a valid API key"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}}},"description":"List payment links","x-codeSamples":[{"label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/payment-links\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'","lang":"cURL"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-links\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-links');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/payment-links\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}","lang":"Go"},{"source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-links\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result","lang":"Ruby","label":"Ruby"},{"source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-links\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}","lang":"Java","label":"Java"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/payment-links\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"tags":["Payment links"],"summary":"List payment links","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application","in":"header"}]}},"/v1/payment-links/{id}/analytics":{"get":{"description":"Returns payment attempts, outcomes, settled totals and the last 30 days of successful volume for one link.","security":[{"apiKey":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLinkAnalytics"},"examples":{"default":{"summary":"Success","value":{"average_minor":125000,"object":"payment_link.analytics","currency":"KES","gross_minor":125000,"settled_minor":125000,"last_payment_at":"2026-10-20T09:00:18Z","daily":[{"day":"2026-10-20","count":1,"amount_minor":125000}],"payment_link_id":"0197d1f0-21a0-7000-8000-000000000005","attempts":2,"succeeded":1,"pending":0,"failed":1,"unknown":0,"fees_minor":0}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}},"summary":"Authentication required"}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"request_id":"req_docs_invoice_1042","code":"not_found","message":"Transaction not found"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"request_id":"req_docs_invoice_1042","code":"idempotency_mismatch","message":"This idempotency key was already used with a different request"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}}},"x-docs-public":true,"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/payment-links/undefined/analytics\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links/undefined/analytics\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links/undefined/analytics\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-links/undefined/analytics\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-links/undefined/analytics');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/payment-links/undefined/analytics\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-links/undefined/analytics\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-links/undefined/analytics\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/payment-links/undefined/analytics\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#"}],"tags":["Payment links"],"operationId":"getPaymentLinkAnalytics","summary":"Payment link analytics","parameters":[{"in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application"}],"x-required-scope":"read"}},"/v1/channel-codes":{"get":{"tags":["Payment methods"],"summary":"List bank codes","operationId":"getV1ChannelCodes","description":"List cached payout bank codes","security":[{"apiKey":[]}],"responses":{"200":{"description":"Bank channel codes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelCodeCatalog"},"examples":{"default":{"value":{"source":"billgrid","fetched_at":"2026-10-20T09:00:18Z","object":"channel_codes","bank":[{"code":"BillGrid invoice 1042","name":"BillGrid"}]},"summary":"Bank channel codes"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"message":"This key cannot select another project","request_id":"req_docs_invoice_1042","code":"forbidden"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"examples":{"default":{"summary":"Rate limited","value":{"error":{"message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042","code":"rate_limited"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"internal_error","message":"An internal error occurred"}}}}}}},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}},"description":"Request error"}},"x-docs-public":true,"x-required-scope":"read","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/channel-codes\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/channel-codes\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/channel-codes\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript"},{"label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/channel-codes\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error","lang":"Python"},{"source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/channel-codes');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);","lang":"PHP","label":"PHP"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/channel-codes\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/channel-codes\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/channel-codes\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/channel-codes\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}]}},"/v1/transactions/summary":{"get":{"tags":["Transactions"],"summary":"Payment activity summary","description":"Unresolved counts and complete 14-day settlement totals for the authenticated project, or authorized workspace when no project is selected.","x-docs-public":true,"operationId":"getTransactionSummary","security":[{"apiKey":[]}],"responses":{"403":{"description":"Insufficient access","content":{"application/json":{"examples":{"default":{"summary":"Insufficient access","value":{"error":{"message":"This key cannot select another project","request_id":"req_docs_invoice_1042","code":"forbidden"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042","code":"idempotency_mismatch"}}}}}},"description":"Request conflicts with existing state"},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"200":{"description":"Workspace payment summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionSummary"},"examples":{"default":{"summary":"Workspace payment summary","value":{"object":"transaction_summary","currency":"KES","period_days":14,"unknown":0,"waiting":1,"daily":[{"day":"2026-10-20","collected":125000,"paid_out":0}]}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"message":"Provide a valid API key","request_id":"req_docs_invoice_1042","code":"unauthorized"}}}}}}}},"parameters":[{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"format":"uuid","type":"string"}}],"x-required-scope":"read","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/transactions/summary\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/transactions/summary\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/transactions/summary\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/transactions/summary\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/transactions/summary');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/transactions/summary\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}","lang":"Go"},{"label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/transactions/summary\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result","lang":"Ruby"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/transactions/summary\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/transactions/summary\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}]}},"/v1/refunds":{"post":{"tags":["Refunds"],"operationId":"createRefund","summary":"Create a refund","description":"Omit amount_minor to refund what settled. Partial refunds are allowed up to the settled amount less anything already refunded.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":255},"description":"Required on every write that moves money. The same key with the same body replays the original response; the same key with a different body is rejected."},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRefundRequest"},"examples":{"partial":{"summary":"Partial refund of a settled invoice","value":{"transaction_id":"0197d1f0-21a0-7000-8000-000000000003","amount_minor":25000,"reason":"Unused service credit"}}}}}},"x-docs-public":true,"x-required-scope":"payout","security":[{"apiKey":[]}],"responses":{"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"provider_not_configured","message":"Configure a compatible gateway account for this project"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"502":{"description":"Gateway unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Gateway unavailable","value":{"error":{"code":"provider_unavailable","message":"The provider is temporarily unavailable","request_id":"req_docs_invoice_1042"}}}}}}},"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transaction"},"examples":{"accepted":{"value":{"channel":"mpesa","currency":"KES","payer_email":null,"recipient_phone":null,"created_at":"2026-10-20T09:00:00Z","id":"0197d1f0-21a0-7000-8000-000000000010","reference":"BG-REFUND-1042","provider_ref":"ws_CO_20102026090000123456789012","application_id":"0197d1f0-21a0-7000-8000-000000000002","mode":"sandbox","description":"Unused service credit","payer_name":"Amina Otieno","recipient_account":null,"object":"transaction","amount_minor":25000,"settled_at":null,"workspace_id":"0197d1f0-21a0-7000-8000-000000000001","provider":"daraja","payer_phone":"254712345678","recipient_name":null,"status":"processing","settled_minor":null,"parent_transaction_id":"0197d1f0-21a0-7000-8000-000000000003","provider_receipt":null,"failure_reason":null,"direction":"refund","metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"expires_at":"2026-10-20T09:05:00Z","source":"billgrid","fee_minor":null,"payment_link_id":null,"next_action":{"type":"none"}},"summary":"Refund initiated with the original credentials"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042","code":"idempotency_mismatch"}},"summary":"Request conflicts with existing state"}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}},"504":{"description":"Gateway outcome unknown","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Gateway outcome unknown","value":{"error":{"message":"The provider outcome is unknown; reconcile the original transaction","request_id":"req_docs_invoice_1042","code":"outcome_unknown"}}}}}}},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"validation_error","message":"Invalid request parameters"}}}}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"request_id":"req_docs_invoice_1042","code":"unauthorized","message":"Provide a valid API key"}}}}}},"description":"Authentication required"},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}}},"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/refunds\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -H 'Idempotency-Key: bg-refund-1042-partial-attempt-1' \\\n  --data-raw '{\n  \"transaction_id\": \"0197d1f0-21a0-7000-8000-000000000003\",\n  \"amount_minor\": 25000,\n  \"reason\": \"Unused service credit\"\n}'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/refunds\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": \"bg-refund-1042-partial-attempt-1\",\n  },\n  body: JSON.stringify({\n    \"transaction_id\": \"0197d1f0-21a0-7000-8000-000000000003\",\n    \"amount_minor\": 25000,\n    \"reason\": \"Unused service credit\"\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/refunds\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": \"bg-refund-1042-partial-attempt-1\",\n  },\n  body: JSON.stringify({\n    \"transaction_id\": \"0197d1f0-21a0-7000-8000-000000000003\",\n    \"amount_minor\": 25000,\n    \"reason\": \"Unused service credit\"\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"transaction_id\": \"0197d1f0-21a0-7000-8000-000000000003\",\n  \"amount_minor\": 25000,\n  \"reason\": \"Unused service credit\"\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/refunds\",\n    method=\"POST\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n        \"Idempotency-Key\": \"bg-refund-1042-partial-attempt-1\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/refunds');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n        'Idempotency-Key: bg-refund-1042-partial-attempt-1',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'transaction_id' => '0197d1f0-21a0-7000-8000-000000000003',\n      'amount_minor' => 25000,\n      'reason' => 'Unused service credit'\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"transaction_id\\\": \\\"0197d1f0-21a0-7000-8000-000000000003\\\",\\n  \\\"amount_minor\\\": 25000,\\n  \\\"reason\\\": \\\"Unused service credit\\\"\\n}\")\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/refunds\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    req.Header.Set(\"Idempotency-Key\", \"bg-refund-1042-partial-attempt-1\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/refunds\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest[\"Idempotency-Key\"] = \"bg-refund-1042-partial-attempt-1\"\nrequest.body = JSON.generate({\n  \"transaction_id\" => \"0197d1f0-21a0-7000-8000-000000000003\",\n  \"amount_minor\" => 25000,\n  \"reason\" => \"Unused service credit\"\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/refunds\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .header(\"Idempotency-Key\", \"bg-refund-1042-partial-attempt-1\")\n        .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"transaction_id\\\": \\\"0197d1f0-21a0-7000-8000-000000000003\\\",\\n  \\\"amount_minor\\\": 25000,\\n  \\\"reason\\\": \\\"Unused service credit\\\"\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/refunds\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Headers.Add(\"Idempotency-Key\", \"bg-refund-1042-partial-attempt-1\");\nrequest.Content = new StringContent(@\"{\n  \"\"transaction_id\"\": \"\"0197d1f0-21a0-7000-8000-000000000003\"\",\n  \"\"amount_minor\"\": 25000,\n  \"\"reason\"\": \"\"Unused service credit\"\"\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#"}]}},"/v1/payment-methods/{provider}/balance":{"get":{"operationId":"getPaymentMethodBalance","summary":"Get account balance","description":"Retrieve balances from the selected project's configured gateway account. Availability depends on the provider; this does not move money.","security":[{"apiKey":[]}],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"format":"uuid","type":"string"},"name":"X-Noria-Application","in":"header"},{"required":false,"schema":{"type":"string","enum":["live","sandbox"]},"description":"Select the exact environment for inspection or mutation.","name":"mode","in":"query"}],"responses":{"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"request_id":"req_docs_invoice_1042","code":"idempotency_mismatch","message":"This idempotency key was already used with a different request"}}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"provider_not_configured","message":"Configure a compatible gateway account for this project"}}}}}},"description":"Validation or gateway configuration error"},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042","code":"rate_limited"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"internal_error","message":"An internal error occurred"}}}}}}},"502":{"description":"Gateway unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Gateway unavailable","value":{"error":{"request_id":"req_docs_invoice_1042","code":"provider_unavailable","message":"The provider is temporarily unavailable"}}}}}}},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"},"examples":{"default":{"value":{"mode":"sandbox","available":[{"currency":"KES","amount_minor":4200000}],"retrieved_at":"2026-10-20T09:00:00Z","object":"balance","provider":"paystack"},"summary":"Success"}}}}},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}},"summary":"Insufficient access"}}}},"description":"Insufficient access"}},"x-docs-public":true,"x-required-scope":"read","tags":["Payment methods"],"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/payment-methods/paystack/balance?mode=sandbox\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods/paystack/balance?mode=sandbox\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-methods/paystack/balance?mode=sandbox\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript"},{"label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-methods/paystack/balance?mode=sandbox\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error","lang":"Python"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-methods/paystack/balance?mode=sandbox');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/payment-methods/paystack/balance?mode=sandbox\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}","lang":"Go"},{"label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-methods/paystack/balance?mode=sandbox\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result","lang":"Ruby"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-methods/paystack/balance?mode=sandbox\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/payment-methods/paystack/balance?mode=sandbox\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#"}]}},"/v1/payment-links/{id}":{"get":{"x-docs-public":true,"description":"Retrieve a payment link","x-required-scope":"read","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"TypeScript"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);","lang":"PHP"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result","lang":"Ruby","label":"Ruby"},{"label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}","lang":"Java"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"tags":["Payment links"],"summary":"Get a payment link","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"format":"uuid","type":"string"},"name":"X-Noria-Application","in":"header"}],"operationId":"getPaymentLink","responses":{"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"request_id":"req_docs_invoice_1042","code":"rate_limited","message":"Too many requests; wait before retrying"}},"summary":"Rate limited"}}}},"description":"Rate limited"},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLink"},"examples":{"default":{"summary":"Success","value":{"created_at":"2026-10-20T09:00:00Z","paid_count":3,"allow_custom_amount":false,"channels":["mpesa","card"],"object":"payment_link","amount_minor":125000,"min_amount_minor":null,"active":true,"collected_minor":375000,"title":"BillGrid invoice 1042","id":"0197d1f0-21a0-7000-8000-000000000005","source":"billgrid","token":"pl_billgrid_invoice_1042_example","description":"October service subscription","max_amount_minor":null,"reference":"BG-INV-2026-1042","application_id":"0197d1f0-21a0-7000-8000-000000000002","url":"https://pay.example.com/p/pl_billgrid_invoice_1042_example","currency":"KES"}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}}}},"patch":{"tags":["Payment links"],"summary":"Update a payment link","security":[{"apiKey":[]}],"parameters":[{"in":"path","required":true,"schema":{"type":"string"},"name":"id"},{"in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application"}],"responses":{"400":{"content":{"application/json":{"examples":{"default":{"summary":"Request error","value":{"error":{"code":"validation_error","message":"Invalid request parameters","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Request error"},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}},"200":{"description":"Success","content":{"application/json":{"examples":{"closed":{"summary":"Checkout closed","value":{"reference":"BG-INV-2026-1042","max_amount_minor":null,"allow_custom_amount":false,"object":"payment_link","source":"billgrid","token":"pl_billgrid_invoice_1042_example","url":"https://pay.example.com/p/pl_billgrid_invoice_1042_example","active":false,"created_at":"2026-10-20T09:00:00Z","amount_minor":125000,"paid_count":3,"collected_minor":375000,"channels":["mpesa","card"],"id":"0197d1f0-21a0-7000-8000-000000000005","application_id":"0197d1f0-21a0-7000-8000-000000000002","title":"BillGrid invoice 1042","min_amount_minor":null,"description":"October service subscription","currency":"KES"}}},"schema":{"$ref":"#/components/schemas/PaymentLink"}}}},"401":{"description":"Authentication required","content":{"application/json":{"examples":{"default":{"summary":"Authentication required","value":{"error":{"message":"Provide a valid API key","request_id":"req_docs_invoice_1042","code":"unauthorized"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"message":"Configure a compatible gateway account for this project","request_id":"req_docs_invoice_1042","code":"provider_not_configured"}}}}}},"description":"Validation or gateway configuration error"}},"x-docs-public":true,"x-required-scope":"manage","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X PATCH \"$NORIA_API_URL/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  --data-raw '{\n  \"active\": false\n}'"},{"label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"active\": false\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"JavaScript"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"active\": false\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"active\": False\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\",\n    method=\"PATCH\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'PATCH',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'active' => false\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"active\\\": false\\n}\")\n    req, err := http.NewRequest(\"PATCH\", baseURL + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\")\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = JSON.generate({\n  \"active\" => false\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result","lang":"Ruby","label":"Ruby"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"active\\\": false\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"PATCH\"), baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Content = new StringContent(@\"{\n  \"\"active\"\": false\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"operationId":"updatePaymentLink","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchPaymentLinkRequest"},"examples":{"close":{"summary":"Close an invoice checkout","value":{"active":false}}}}}},"description":"Update a payment link"},"delete":{"summary":"Close a payment link","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects."}],"responses":{"204":{"description":"Closed"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}},"description":"Authentication required"},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"request_id":"req_docs_invoice_1042","code":"forbidden","message":"This key cannot select another project"}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}},"description":"Request conflicts with existing state"},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}},"description":"Request error"}},"x-required-scope":"manage","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X DELETE \"$NORIA_API_URL/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\",\n    method=\"DELETE\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error","lang":"Python"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'DELETE',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"DELETE\", baseURL + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\")\nrequest = Net::HTTP::Delete.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"DELETE\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}","lang":"Java"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"DELETE\"), baseUrl + \"/v1/payment-links/0197d1f0-21a0-7000-8000-000000000005\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"tags":["Payment links"],"operationId":"closePaymentLink","security":[{"apiKey":[]}],"x-docs-public":true,"description":"Close a payment link"}},"/v1/providers":{"get":{"x-codeSamples":[{"source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/providers\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'","lang":"cURL","label":"cURL"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/providers\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/providers\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/providers\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/providers');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);","lang":"PHP","label":"PHP"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/providers\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/providers\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/providers\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/providers\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"summary":"List supported gateways","security":[{"apiKey":[]}],"responses":{"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}},"200":{"description":"Provider catalog","content":{"application/json":{"schema":{"properties":{"object":{"type":"string","const":"list"},"data":{"items":{"$ref":"#/components/schemas/ProviderDefinition"},"type":"array"},"next_cursor":{"type":"null"}},"required":["object","data","next_cursor"],"type":"object"},"examples":{"default":{"summary":"Provider catalog","value":{"object":"list","data":[{"provider":"daraja","name":"BillGrid","configurable":true,"modes":["BillGrid invoice 1042"],"confirm_unsigned_collections":true,"icon":"BillGrid invoice 1042","channels":["BillGrid invoice 1042"],"currencies":["BillGrid invoice 1042"],"fields":[{"name":"BillGrid","label":"BillGrid"}],"capabilities":{"refunds":true,"status":true,"balances":true,"collections":["BillGrid invoice 1042"],"payouts":["BillGrid invoice 1042"]},"config_schema":{"properties":{},"required":["BillGrid invoice 1042"],"x-property-order":["BillGrid invoice 1042"],"type":"object"},"description":"Invoice collections for BillGrid","priority":{}}],"next_cursor":null}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"request_id":"req_docs_invoice_1042","code":"forbidden","message":"This key cannot select another project"}}}}}},"description":"Insufficient access"},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"request_id":"req_docs_invoice_1042","code":"idempotency_mismatch","message":"This idempotency key was already used with a different request"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042","code":"rate_limited"}}}}}}}},"operationId":"getV1Providers","x-docs-public":true,"description":"List registered provider adapters and setup metadata","x-required-scope":"read"}},"/v1/webhook-deliveries/{id}":{"get":{"security":[{"apiKey":[]}],"responses":{"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDelivery"},"examples":{"default":{"summary":"Success","value":{"max_attempts":8,"id":"0197d1f0-21a0-7000-8000-000000000008","object":"webhook_delivery","endpoint_url":"https://billgrid.example.com/webhooks/noria-pay","event_type":"succeeded","status":"pending","delivered_at":null,"endpoint_id":"0197d1f0-21a0-7000-8000-000000000006","transaction_id":"0197d1f0-21a0-7000-8000-000000000003","attempts":1,"response_status":503,"last_error":"Webhook delivery failed","next_attempt_at":"2026-10-20T09:00:23Z","workspace_id":"0197d1f0-21a0-7000-8000-000000000001","endpoint_enabled":true,"event_id":"0197d1f0-21a0-7000-8000-000000000007","application_id":"0197d1f0-21a0-7000-8000-000000000002","created_at":"2026-10-20T09:00:18Z"}}}}}},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"request_id":"req_docs_invoice_1042","code":"validation_error","message":"Invalid request parameters"}}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"code":"unauthorized","message":"Provide a valid API key","request_id":"req_docs_invoice_1042"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"message":"This key cannot select another project","request_id":"req_docs_invoice_1042","code":"forbidden"}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"examples":{"default":{"summary":"Resource not found","value":{"error":{"message":"Transaction not found","request_id":"req_docs_invoice_1042","code":"not_found"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-docs-public":true,"description":"One delivery of an event to an endpoint, with its attempts, last response and next retry","tags":["Webhooks"],"operationId":"getWebhookDelivery","parameters":[{"schema":{"type":"string","format":"uuid"},"name":"id","in":"path","required":true},{"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application","in":"header","required":false}],"x-required-scope":"read","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"JavaScript","label":"JavaScript"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result","lang":"Ruby","label":"Ruby"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/webhook-deliveries/0197d1f0-21a0-7000-8000-000000000008\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#","label":"C#"}],"summary":"Get a delivery"}},"/v1/charges/{id}/cancel":{"post":{"security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}}],"x-required-scope":"charge","x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/charges/0197d1f0-21a0-7000-8000-000000000003/cancel\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/charges/0197d1f0-21a0-7000-8000-000000000003/cancel\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/charges/0197d1f0-21a0-7000-8000-000000000003/cancel\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/charges/0197d1f0-21a0-7000-8000-000000000003/cancel\",\n    method=\"POST\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/charges/0197d1f0-21a0-7000-8000-000000000003/cancel');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/charges/0197d1f0-21a0-7000-8000-000000000003/cancel\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}","lang":"Go"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/charges/0197d1f0-21a0-7000-8000-000000000003/cancel\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/charges/0197d1f0-21a0-7000-8000-000000000003/cancel\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"lang":"C#","label":"C#","source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/charges/0197d1f0-21a0-7000-8000-000000000003/cancel\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);"}],"summary":"Cancel a charge","responses":{"409":{"description":"Request conflicts with existing state","content":{"application/json":{"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}},"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}}},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transaction"},"examples":{"unknown":{"summary":"Cancellation requested; provider confirmation is pending","value":{"id":"0197d1f0-21a0-7000-8000-000000000003","description":"BillGrid invoice 1042","payer_name":"Amina Otieno","settled_at":null,"direction":"collection","recipient_name":null,"failure_reason":null,"next_action":{"type":"none"},"application_id":"0197d1f0-21a0-7000-8000-000000000002","channel":"mpesa","payer_phone":"254712345678","provider_receipt":null,"created_at":"2026-10-20T09:00:00Z","workspace_id":"0197d1f0-21a0-7000-8000-000000000001","source":"billgrid","payer_email":null,"object":"transaction","provider":"daraja","settled_minor":null,"currency":"KES","payment_link_id":null,"recipient_account":null,"provider_ref":"ws_CO_20102026090000123456789012","status":"unknown","amount_minor":125000,"expires_at":"2026-10-20T09:05:00Z","mode":"sandbox","fee_minor":null,"reference":"BG-INV-2026-1042","parent_transaction_id":null,"recipient_phone":null,"metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"}}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"message":"Provide a valid API key","request_id":"req_docs_invoice_1042","code":"unauthorized"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"message":"Transaction not found","request_id":"req_docs_invoice_1042","code":"not_found"}}}}}}}},"x-docs-public":true,"tags":["Charges"],"operationId":"cancelCharge","description":"Requests local cancellation of a processing charge. The provider may still take payment, so the outcome becomes unknown and remains polled until confirmed. Verified late collection settlements are recorded."}},"/v1/statements":{"post":{"summary":"Import a statement","description":"Import a statement for the selected project and payment_method_id (required for independent projects). Match only that gateway account’s existing charges. A statement never creates a payment. Duplicate imports replay the stored result.","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"examples":{"mpesa":{"summary":"Reconcile an M-Pesa account statement","value":{"rows":[{"receipt":"UJK7A4M2Q9","amount_minor":125000,"paid_at":"2026-10-20T09:00:18Z","reference":"BG-INV-2026-1042","payer_phone":"254712345678"}],"payment_method_id":"0197d1f0-21a0-7000-8000-000000000004","source":"mpesa","label":"M-Pesa settlement · 20 October","currency":"KES"}}},"schema":{"$ref":"#/components/schemas/ImportStatementRequest"}}}},"x-docs-public":true,"x-codeSamples":[{"lang":"cURL","label":"cURL","source":"curl --fail-with-body --max-time 30 -X POST \"$NORIA_API_URL/v1/statements\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  --data-raw '{\n  \"payment_method_id\": \"0197d1f0-21a0-7000-8000-000000000004\",\n  \"source\": \"mpesa\",\n  \"label\": \"M-Pesa settlement · 20 October\",\n  \"currency\": \"KES\",\n  \"rows\": [\n    {\n      \"receipt\": \"UJK7A4M2Q9\",\n      \"amount_minor\": 125000,\n      \"paid_at\": \"2026-10-20T09:00:18Z\",\n      \"reference\": \"BG-INV-2026-1042\",\n      \"payer_phone\": \"254712345678\"\n    }\n  ]\n}'"},{"lang":"JavaScript","label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/statements\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"payment_method_id\": \"0197d1f0-21a0-7000-8000-000000000004\",\n    \"source\": \"mpesa\",\n    \"label\": \"M-Pesa settlement · 20 October\",\n    \"currency\": \"KES\",\n    \"rows\": [\n      {\n        \"receipt\": \"UJK7A4M2Q9\",\n        \"amount_minor\": 125000,\n        \"paid_at\": \"2026-10-20T09:00:18Z\",\n        \"reference\": \"BG-INV-2026-1042\",\n        \"payer_phone\": \"254712345678\"\n      }\n    ]\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/statements\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"payment_method_id\": \"0197d1f0-21a0-7000-8000-000000000004\",\n    \"source\": \"mpesa\",\n    \"label\": \"M-Pesa settlement · 20 October\",\n    \"currency\": \"KES\",\n    \"rows\": [\n      {\n        \"receipt\": \"UJK7A4M2Q9\",\n        \"amount_minor\": 125000,\n        \"paid_at\": \"2026-10-20T09:00:18Z\",\n        \"reference\": \"BG-INV-2026-1042\",\n        \"payer_phone\": \"254712345678\"\n      }\n    ]\n  }),\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\npayload = {\n  \"payment_method_id\": \"0197d1f0-21a0-7000-8000-000000000004\",\n  \"source\": \"mpesa\",\n  \"label\": \"M-Pesa settlement · 20 October\",\n  \"currency\": \"KES\",\n  \"rows\": [\n    {\n      \"receipt\": \"UJK7A4M2Q9\",\n      \"amount_minor\": 125000,\n      \"paid_at\": \"2026-10-20T09:00:18Z\",\n      \"reference\": \"BG-INV-2026-1042\",\n      \"payer_phone\": \"254712345678\"\n    }\n  ]\n}\nrequest = urllib.request.Request(\n    base_url + \"/v1/statements\",\n    method=\"POST\",\n    data=json.dumps(payload).encode(\"utf-8\"),\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/statements');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'POST',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n        'Content-Type: application/json',\n    ],\n    CURLOPT_POSTFIELDS => json_encode([\n      'payment_method_id' => '0197d1f0-21a0-7000-8000-000000000004',\n      'source' => 'mpesa',\n      'label' => 'M-Pesa settlement · 20 October',\n      'currency' => 'KES',\n      'rows' => [\n        [\n          'receipt' => 'UJK7A4M2Q9',\n          'amount_minor' => 125000,\n          'paid_at' => '2026-10-20T09:00:18Z',\n          'reference' => 'BG-INV-2026-1042',\n          'payer_phone' => '254712345678'\n        ]\n      ]\n    ], JSON_THROW_ON_ERROR),\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"strings\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    body := strings.NewReader(\"{\\n  \\\"payment_method_id\\\": \\\"0197d1f0-21a0-7000-8000-000000000004\\\",\\n  \\\"source\\\": \\\"mpesa\\\",\\n  \\\"label\\\": \\\"M-Pesa settlement · 20 October\\\",\\n  \\\"currency\\\": \\\"KES\\\",\\n  \\\"rows\\\": [\\n    {\\n      \\\"receipt\\\": \\\"UJK7A4M2Q9\\\",\\n      \\\"amount_minor\\\": 125000,\\n      \\\"paid_at\\\": \\\"2026-10-20T09:00:18Z\\\",\\n      \\\"reference\\\": \\\"BG-INV-2026-1042\\\",\\n      \\\"payer_phone\\\": \\\"254712345678\\\"\\n    }\\n  ]\\n}\")\n    req, err := http.NewRequest(\"POST\", baseURL + \"/v1/statements\", body)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    req.Header.Set(\"Content-Type\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/statements\")\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = JSON.generate({\n  \"payment_method_id\" => \"0197d1f0-21a0-7000-8000-000000000004\",\n  \"source\" => \"mpesa\",\n  \"label\" => \"M-Pesa settlement · 20 October\",\n  \"currency\" => \"KES\",\n  \"rows\" => [\n    {\n      \"receipt\" => \"UJK7A4M2Q9\",\n      \"amount_minor\" => 125000,\n      \"paid_at\" => \"2026-10-20T09:00:18Z\",\n      \"reference\" => \"BG-INV-2026-1042\",\n      \"payer_phone\" => \"254712345678\"\n    }\n  ]\n})\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/statements\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .header(\"Content-Type\", \"application/json\")\n        .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"{\\n  \\\"payment_method_id\\\": \\\"0197d1f0-21a0-7000-8000-000000000004\\\",\\n  \\\"source\\\": \\\"mpesa\\\",\\n  \\\"label\\\": \\\"M-Pesa settlement · 20 October\\\",\\n  \\\"currency\\\": \\\"KES\\\",\\n  \\\"rows\\\": [\\n    {\\n      \\\"receipt\\\": \\\"UJK7A4M2Q9\\\",\\n      \\\"amount_minor\\\": 125000,\\n      \\\"paid_at\\\": \\\"2026-10-20T09:00:18Z\\\",\\n      \\\"reference\\\": \\\"BG-INV-2026-1042\\\",\\n      \\\"payer_phone\\\": \\\"254712345678\\\"\\n    }\\n  ]\\n}\"))\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}","lang":"Java"},{"source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"POST\"), baseUrl + \"/v1/statements\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nrequest.Content = new StringContent(@\"{\n  \"\"payment_method_id\"\": \"\"0197d1f0-21a0-7000-8000-000000000004\"\",\n  \"\"source\"\": \"\"mpesa\"\",\n  \"\"label\"\": \"\"M-Pesa settlement · 20 October\"\",\n  \"\"currency\"\": \"\"KES\"\",\n  \"\"rows\"\": [\n    {\n      \"\"receipt\"\": \"\"UJK7A4M2Q9\"\",\n      \"\"amount_minor\"\": 125000,\n      \"\"paid_at\"\": \"\"2026-10-20T09:00:18Z\"\",\n      \"\"reference\"\": \"\"BG-INV-2026-1042\"\",\n      \"\"payer_phone\"\": \"\"254712345678\"\"\n    }\n  ]\n}\", Encoding.UTF8, \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#","label":"C#"}],"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatementImport"},"examples":{"default":{"summary":"Success","value":{"object":"statement_import","duplicates":0,"settled_minor":125000,"application_id":"0197d1f0-21a0-7000-8000-000000000002","row_count":1,"unmatched":0,"replayed":false,"rows":[{"receipt":"UJK7A4M2Q9","amount_minor":125000,"outcome":"settled","transaction_id":"0197d1f0-21a0-7000-8000-000000000003","reference":"BG-INV-2026-1042","detail":"Matched and settled"}],"created_at":"2026-10-20T10:00:00Z","id":"0197d1f0-21a0-7000-8000-000000000009","source":"mpesa","label":"M-Pesa settlement · 20 October","ambiguous":0,"unmatched_minor":0,"payment_method_id":"0197d1f0-21a0-7000-8000-000000000004","currency":"KES","settled":1}}}}}},"400":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"validation_error","message":"Invalid request parameters","request_id":"req_docs_invoice_1042"}}}}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Resource not found","value":{"error":{"code":"not_found","message":"Transaction not found","request_id":"req_docs_invoice_1042"}}}}}}},"500":{"description":"Request error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}},"summary":"Request error"}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Authentication required","value":{"error":{"message":"Provide a valid API key","request_id":"req_docs_invoice_1042","code":"unauthorized"}}}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Insufficient access","value":{"error":{"code":"forbidden","message":"This key cannot select another project","request_id":"req_docs_invoice_1042"}}}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}},"422":{"description":"Validation or gateway configuration error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Validation or gateway configuration error","value":{"error":{"code":"provider_not_configured","message":"Configure a compatible gateway account for this project","request_id":"req_docs_invoice_1042"}}}}}}},"429":{"description":"Rate limited","content":{"application/json":{"examples":{"default":{"value":{"error":{"code":"rate_limited","message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042"}},"summary":"Rate limited"}},"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"},"name":"X-Noria-Application"}],"x-required-scope":"reconcile","tags":["Statements"],"operationId":"importStatement"},"get":{"description":"List statement imports","tags":["Statements"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"name":"limit","in":"query"},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"X-Noria-Application","in":"header","required":false,"description":"Project ID for browser sessions. Project API keys infer their project and cannot override it. Writes require a project; workspace reads without one aggregate authorized projects.","schema":{"type":"string","format":"uuid"}}],"x-docs-public":true,"x-required-scope":"read","x-codeSamples":[{"label":"cURL","source":"curl --fail-with-body --max-time 30 -X GET \"$NORIA_API_URL/v1/statements\" \\\n  -H \"Authorization: Bearer $NORIA_API_KEY\" \\\n  -H 'Accept: application/json'","lang":"cURL"},{"label":"JavaScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/statements\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);","lang":"JavaScript"},{"lang":"TypeScript","label":"TypeScript","source":"const baseUrl = process.env.NORIA_API_URL ?? \"https://pay.example.com\";\nconst response = await fetch(baseUrl + \"/v1/statements\", {\n  method: \"GET\",\n  headers: {\n    Authorization: `Bearer ${process.env.NORIA_API_KEY}`,\n    \"Accept\": \"application/json\",\n  },\n});\nconst text = await response.text();\nif (!response.ok) throw new Error(`Noria Pay ${response.status}: ${text}`);\nconst result = text ? JSON.parse(text) : null;\nconsole.log(result);"},{"lang":"Python","label":"Python","source":"import json\nimport os\nimport urllib.error\nimport urllib.request\n\nbase_url = os.environ.get(\"NORIA_API_URL\", \"https://pay.example.com\")\nrequest = urllib.request.Request(\n    base_url + \"/v1/statements\",\n    method=\"GET\",\n    headers={\n        \"Authorization\": \"Bearer \" + os.environ[\"NORIA_API_KEY\"],\n        \"Accept\": \"application/json\",\n    },\n)\ntry:\n    with urllib.request.urlopen(request, timeout=30) as response:\n        raw = response.read()\n        print(json.loads(raw) if raw else None)\nexcept urllib.error.HTTPError as error:\n    raise RuntimeError(error.read().decode(\"utf-8\")) from error"},{"lang":"PHP","label":"PHP","source":"<?php\n$baseUrl = getenv('NORIA_API_URL') ?: 'https://pay.example.com';\n$ch = curl_init($baseUrl . '/v1/statements');\ncurl_setopt_array($ch, [\n    CURLOPT_RETURNTRANSFER => true,\n    CURLOPT_TIMEOUT => 30,\n    CURLOPT_CUSTOMREQUEST => 'GET',\n    CURLOPT_HTTPHEADER => [\n        'Authorization: Bearer ' . getenv('NORIA_API_KEY'),\n        'Accept: application/json',\n    ],\n]);\n$raw = curl_exec($ch);\nif ($raw === false) throw new RuntimeException(curl_error($ch));\n$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\ncurl_close($ch);\nif ($status >= 400) throw new RuntimeException($raw);\n$result = $raw !== '' ? json_decode($raw, true, 512, JSON_THROW_ON_ERROR) : null;\nprint_r($result);"},{"lang":"Go","label":"Go","source":"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n    \"time\"\n)\n\nfunc main() {\n    baseURL := os.Getenv(\"NORIA_API_URL\")\n    if baseURL == \"\" { baseURL = \"https://pay.example.com\" }\n    req, err := http.NewRequest(\"GET\", baseURL + \"/v1/statements\", nil)\n    if err != nil { panic(err) }\n    req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"NORIA_API_KEY\"))\n    req.Header.Set(\"Accept\", \"application/json\")\n    client := &http.Client{Timeout: 30 * time.Second}\n    res, err := client.Do(req)\n    if err != nil { panic(err) }\n    defer res.Body.Close()\n    data, err := io.ReadAll(res.Body)\n    if err != nil { panic(err) }\n    if res.StatusCode >= 400 { panic(string(data)) }\n    fmt.Println(string(data))\n}"},{"lang":"Ruby","label":"Ruby","source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nuri = URI(ENV.fetch(\"NORIA_API_URL\", \"https://pay.example.com\") + \"/v1/statements\")\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer #{ENV.fetch('NORIA_API_KEY')}\"\nrequest[\"Accept\"] = \"application/json\"\nresponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == \"https\", open_timeout: 10, read_timeout: 30) do |http|\n  http.request(request)\nend\nraise \"Noria Pay #{response.code}: #{response.body}\" unless response.is_a?(Net::HTTPSuccess)\nresult = response.body.to_s.empty? ? nil : JSON.parse(response.body)\nputs result"},{"lang":"Java","label":"Java","source":"import java.net.URI;\nimport java.net.http.*;\nimport java.time.Duration;\n\nclass Example {\n  public static void main(String[] args) throws Exception {\n    String baseUrl = System.getenv().getOrDefault(\"NORIA_API_URL\", \"https://pay.example.com\");\n    HttpRequest request = HttpRequest.newBuilder()\n        .uri(URI.create(baseUrl + \"/v1/statements\"))\n        .timeout(Duration.ofSeconds(30))\n        .header(\"Authorization\", \"Bearer \" + System.getenv(\"NORIA_API_KEY\"))\n        .header(\"Accept\", \"application/json\")\n        .method(\"GET\", HttpRequest.BodyPublishers.noBody())\n        .build();\n    HttpResponse<String> response = HttpClient.newHttpClient()\n        .send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() >= 400) throw new RuntimeException(response.body());\n    System.out.println(response.body());\n  }\n}"},{"source":"using System;\nusing System.Net.Http;\nusing System.Text;\n\nvar baseUrl = Environment.GetEnvironmentVariable(\"NORIA_API_URL\") ?? \"https://pay.example.com\";\nusing var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };\nusing var request = new HttpRequestMessage(new HttpMethod(\"GET\"), baseUrl + \"/v1/statements\");\nrequest.Headers.Add(\"Authorization\", \"Bearer \" + Environment.GetEnvironmentVariable(\"NORIA_API_KEY\"));\nrequest.Headers.Add(\"Accept\", \"application/json\");\nusing var response = await client.SendAsync(request);\nvar raw = await response.Content.ReadAsStringAsync();\nif (!response.IsSuccessStatusCode) throw new Exception(raw);\nConsole.WriteLine(raw);","lang":"C#","label":"C#"}],"operationId":"listStatements","summary":"List statement imports","responses":{"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Rate limited","value":{"error":{"message":"Too many requests; wait before retrying","request_id":"req_docs_invoice_1042","code":"rate_limited"}}}}}}},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request error","value":{"error":{"code":"internal_error","message":"An internal error occurred","request_id":"req_docs_invoice_1042"}}}}}},"description":"Request error"},"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatementImportList"},"examples":{"default":{"summary":"Success","value":{"object":"list","data":[{"unmatched_minor":0,"created_at":"2026-10-20T10:00:00Z","id":"0197d1f0-21a0-7000-8000-000000000009","settled":1,"duplicates":0,"unmatched":0,"ambiguous":0,"source":"mpesa","rows":[{"receipt":"UJK7A4M2Q9","amount_minor":125000,"outcome":"settled","transaction_id":"0197d1f0-21a0-7000-8000-000000000003","reference":"BG-INV-2026-1042","detail":"Matched and settled"}],"label":"M-Pesa settlement · 20 October","row_count":1,"replayed":false,"object":"statement_import","application_id":"0197d1f0-21a0-7000-8000-000000000002","payment_method_id":"0197d1f0-21a0-7000-8000-000000000004","currency":"KES","settled_minor":125000}],"next_cursor":null}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"message":"Provide a valid API key","request_id":"req_docs_invoice_1042","code":"unauthorized"}},"summary":"Authentication required"}}}}},"403":{"description":"Insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"value":{"error":{"message":"This key cannot select another project","request_id":"req_docs_invoice_1042","code":"forbidden"}},"summary":"Insufficient access"}}}}},"409":{"description":"Request conflicts with existing state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"default":{"summary":"Request conflicts with existing state","value":{"error":{"code":"idempotency_mismatch","message":"This idempotency key was already used with a different request","request_id":"req_docs_invoice_1042"}}}}}}}}}}},"x-generated":"Generated by make generate. Edit schemas.json, spec.source.json and scripts/docs/examples.mjs.","webhooks":{"paymentUpdate":{"post":{"parameters":[{"example":"t=1792486818,v1=<hmac-sha256-hex>","name":"Pay-Signature","in":"header","required":true,"schema":{"type":"string"}},{"name":"Pay-Event-Id","in":"header","required":true,"schema":{"type":"string"},"example":"0197d1f0-21a0-7000-8000-000000000007"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPayload"},"examples":{"succeeded":{"value":{"sequence":3,"created_at":"2026-10-20T09:00:18Z","data":{"provider_receipt":"UJK7A4M2Q9","metadata":{"invoice_id":"1042","customer_id":"cust_ami_204"},"detail":{"provider_receipt":"UJK7A4M2Q9"},"application_id":"0197d1f0-21a0-7000-8000-000000000002","channel":"mpesa","underpaid":false,"settled_at":"2026-10-20T09:00:18Z","transaction_id":"0197d1f0-21a0-7000-8000-000000000003","direction":"collection","status":"succeeded","amount_minor":125000,"fee_minor":null,"failure_reason":null,"source":"billgrid","mode":"sandbox","currency":"KES","parent_transaction_id":null,"payment_link_id":null,"provider_ref":"ws_CO_20102026090000123456789012","workspace_id":"0197d1f0-21a0-7000-8000-000000000001","provider":"daraja","settled_minor":125000,"reference":"BG-INV-2026-1042"},"id":"0197d1f0-21a0-7000-8000-000000000007","type":"succeeded"},"summary":"Invoice settled"}}}}},"responses":{"204":{"description":"Receiver durably accepted the event"}},"summary":"Normalized payment update","description":"Pay sends this request to the originating project's endpoint after its event commits. Verify Pay-Signature against the raw bytes. Persist before acknowledging; deduplicate by event ID and reconcile by transaction ID and sequence."}}},"openapi":"3.1.0","info":{"title":"Noria Pay","version":"0.1.0","description":"Collections, payouts and refunds through one integration per project. A workspace contains independently credentialed projects; application keys cannot access sibling project data. Payment outcomes use one normalized callback contract."},"servers":[{"url":"/"}],"tags":[{"description":"Collect money from a payer","name":"Charges"},{"name":"Payouts","description":"Send money to a recipient"},{"description":"Return money against a collection","name":"Refunds"},{"description":"One ledger for everything this service has moved, in any direction","name":"Transactions"},{"name":"Payment methods","description":"Per-workspace provider credentials"},{"description":"Hosted checkout a payer can open","name":"Payment links"},{"description":"Reconcile money that arrived without a charge behind it","name":"Statements"},{"name":"Webhooks","description":"Settlement event fan-out, and the provider callback sink"}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Workspace API key: pay_live_… or pay_test_…"}},"schemas":{"RecordChargeRequest":{"type":"object","properties":{"metadata":{"propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"type":"string","maxLength":512},"default":{},"type":"object"},"source":{"type":"string","pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused."},"amount_minor":{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000},"currency":{"default":"KES","type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$"},"channel":{"type":"string","enum":["mpesa","airtel","card","bank","wallet","cash"]},"reference":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9 ._/-]*$"},"receipt":{"type":"string","minLength":1,"maxLength":64},"payer_phone":{"type":"string","minLength":7,"maxLength":20,"pattern":"^\\+?[\\d\\s()-]+$"},"paid_at":{"format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","type":"string"},"note":{"type":"string","maxLength":1024},"description":{"minLength":1,"maxLength":256,"type":"string"},"payer_name":{"type":"string","maxLength":256}},"required":["amount_minor","channel","reference","description"],"x-contract-schema":"recordChargeSchema"},"PaymentLink":{"x-contract-schema":"paymentLinkSchema","type":"object","properties":{"object":{"type":"string","const":"payment_link"},"token":{"type":"string"},"max_amount_minor":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"allow_custom_amount":{"type":"boolean"},"reference":{"type":["string","null"]},"paid_count":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Successful payments collected through this link in the modes the caller can see."},"application_id":{"type":"string","format":"uuid"},"id":{"type":"string"},"url":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"collected_minor":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Settled amount collected through this link, in minor units of the link currency."},"currency":{"type":"string"},"amount_minor":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"created_at":{"type":"string"},"min_amount_minor":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"channels":{"type":"array","items":{"type":"string","enum":["mpesa","airtel","card","bank","wallet","cash"]}},"active":{"type":"boolean"},"source":{"type":["string","null"],"pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused."}},"required":["id","object","token","url","title","description","currency","amount_minor","min_amount_minor","max_amount_minor","allow_custom_amount","channels","reference","active","created_at","paid_count","collected_minor"]},"WebhookEndpoint":{"type":"object","properties":{"description":{"type":["string","null"]},"created_at":{"type":"string"},"source":{"type":["string","null"],"pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused."},"application_id":{"type":"string","format":"uuid"},"id":{"type":"string"},"secret":{"description":"Signing secret returned once when creating the endpoint; omitted from reads and updates.","type":["string","null"]},"event_types":{"type":"array","items":{"type":"string","enum":["created","dispatched","callback_received","queried","succeeded","failed","cancelled","expired","outcome_unknown","reversed"]}},"enabled":{"type":"boolean"},"include_provider_payload":{"type":"boolean","description":"Include redacted gateway callback data alongside normalized payment fields."},"object":{"type":"string","const":"webhook_endpoint"},"url":{"type":"string"}},"required":["id","object","url","description","event_types","enabled","created_at"],"x-contract-schema":"webhookEndpointSchema"},"WebhookEndpointCreated":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string","description":"Store securely; shown once."}}}]},"CreatePayoutRequest":{"type":"object","properties":{"currency":{"default":"KES","type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$"},"channel":{"type":"string","enum":["mpesa","airtel","card","bank","wallet","cash"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","maxLength":64},"mode":{"type":"string","enum":["live","sandbox"]},"description":{"minLength":1,"maxLength":256,"type":"string"},"recipient_phone":{"type":"string","minLength":7,"maxLength":20,"pattern":"^\\+?[\\d\\s()-]+$"},"metadata":{"default":{},"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"type":"string","maxLength":512}},"amount_minor":{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000},"reference":{"pattern":"^[A-Za-z0-9][A-Za-z0-9 ._/-]*$","type":"string","minLength":1,"maxLength":64},"recipient_name":{"type":"string","minLength":1,"maxLength":256},"recipient_account":{"type":"string","minLength":1,"maxLength":64},"recipient_channel_code":{"maxLength":16,"type":"string","minLength":1},"source":{"type":"string","pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused."}},"required":["amount_minor","channel","reference","description","recipient_name"],"x-contract-schema":"createPayoutSchema"},"TransactionList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Transaction"}},"next_cursor":{"type":["string","null"]},"object":{"type":"string","const":"list"}},"required":["object","data","next_cursor"],"$schema":"https://json-schema.org/draft/2020-12/schema"},"TransactionEventList":{"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TransactionEvent"}},"next_cursor":{"type":["string","null"]},"object":{"const":"list","type":"string"}},"required":["object","data","next_cursor"],"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object"},"ImportStatementRequest":{"type":"object","properties":{"rows":{"minItems":1,"maxItems":5000,"type":"array","items":{"type":"object","properties":{"payer_phone":{"minLength":7,"maxLength":20,"pattern":"^\\+?[\\d\\s()-]+$","type":"string"},"payer_name":{"type":"string","maxLength":256},"narrative":{"type":"string","maxLength":512},"receipt":{"maxLength":64,"type":"string","minLength":1},"amount_minor":{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000},"paid_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"reference":{"type":"string","maxLength":64}},"required":["receipt","amount_minor","paid_at"]}},"payment_method_id":{"type":"string","format":"uuid"},"source":{"type":"string","enum":["mpesa","bank","sasapay","paystack","other"]},"label":{"maxLength":256,"type":"string","minLength":1},"currency":{"minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$","default":"KES","type":"string"}},"required":["source","label","rows"],"x-contract-schema":"importStatementSchema"},"PatchWebhookEndpointRequest":{"x-contract-schema":"patchWebhookEndpointSchema","type":"object","properties":{"enabled":{"type":"boolean"},"source":{"type":["string","null"],"pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused."},"include_provider_payload":{"type":"boolean","description":"Include redacted gateway callback data alongside normalized payment fields."},"url":{"type":"string","maxLength":2000,"format":"uri"},"description":{"type":"string","maxLength":500},"event_types":{"type":"array","items":{"type":"string","enum":["created","dispatched","callback_received","queried","succeeded","failed","cancelled","expired","outcome_unknown","reversed"]}}}},"WebhookEndpointList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"next_cursor":{"type":["string","null"]},"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}},"required":["object","data","next_cursor"]},"WebhookDeliveryRetry":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"object":{"const":"webhook_delivery","type":"string"},"status":{"const":"pending","type":"string"}},"required":["id","object","status"]},"CreateChargeRequest":{"type":"object","properties":{"currency":{"default":"KES","type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$"},"channel":{"type":"string","enum":["mpesa","airtel","card","bank","wallet","cash"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","maxLength":64},"description":{"maxLength":256,"type":"string","minLength":1},"payer_name":{"type":"string","maxLength":256},"return_url":{"type":"string","maxLength":2000,"format":"uri","pattern":"^https://[^\\s/?#@]+(?:[/?#]\\S*)?$"},"mode":{"type":"string","enum":["live","sandbox"]},"reference":{"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9 ._/-]*$","type":"string","minLength":1},"payer_phone":{"type":"string","minLength":7,"maxLength":20,"pattern":"^\\+?[\\d\\s()-]+$"},"payer_email":{"type":"string","minLength":3,"maxLength":320,"pattern":"^[^\\s@]+@[^\\s@.]+\\.[^\\s@]+$"},"expires_in_seconds":{"default":300,"type":"integer","minimum":30,"maximum":86400},"metadata":{"default":{},"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"type":"string","maxLength":512}},"source":{"type":"string","pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused."},"amount_minor":{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000}},"required":["amount_minor","channel","reference","description"],"x-contract-schema":"createChargeSchema"},"TransactionEvent":{"x-contract-schema":"transactionEventSchema","type":"object","properties":{"object":{"type":"string","const":"transaction.event"},"transaction_id":{"type":["string","null"]},"type":{"type":"string","enum":["created","dispatched","callback_received","queried","succeeded","failed","cancelled","expired","outcome_unknown","reversed"]},"payload":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"occurred_at":{"type":"string"},"sequence":{"type":"integer","minimum":0},"id":{"type":"string"}},"required":["id","object","transaction_id","type","payload","occurred_at"]},"SetPaymentMethodEnabledRequest":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"],"x-contract-schema":"setPaymentMethodEnabledSchema"},"UpsertPaymentLinkRequest":{"required":["title"],"x-contract-schema":"upsertPaymentLinkSchema","type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":256},"description":{"type":"string","maxLength":1024},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$","default":"KES"},"min_amount_minor":{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000},"max_amount_minor":{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000},"allow_custom_amount":{"default":false,"type":"boolean"},"channels":{"default":[],"type":"array","items":{"enum":["mpesa","airtel","card","bank","wallet","cash"],"type":"string"}},"reference":{"pattern":"^[A-Za-z0-9][A-Za-z0-9 ._/-]*$","type":"string","minLength":1,"maxLength":64},"amount_minor":{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000},"return_url":{"format":"uri","pattern":"^https://[^\\s/?#@]+(?:[/?#]\\S*)?$","type":"string","maxLength":2000},"active":{"default":true,"type":"boolean"},"metadata":{"additionalProperties":{"type":"string","maxLength":512},"default":{},"type":"object","propertyNames":{"maxLength":64,"type":"string"}},"source":{"maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused.","type":"string","pattern":"^[a-z][a-z0-9_-]{0,63}$"}}},"ChannelCode":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}},"required":["code","name"]},"Transaction":{"required":["id","object","workspace_id","direction","status","channel","provider","mode","currency","amount_minor","settled_minor","fee_minor","reference","description","parent_transaction_id","payment_link_id","payer_name","payer_phone","payer_email","recipient_name","recipient_phone","recipient_account","provider_ref","provider_receipt","failure_reason","next_action","metadata","expires_at","settled_at","created_at"],"x-contract-schema":"transactionSchema","type":"object","properties":{"currency":{"type":"string"},"fee_minor":{"anyOf":[{"minimum":-9007199254740991,"maximum":9007199254740991,"type":"integer"},{"type":"null"}]},"application_id":{"type":"string","format":"uuid"},"id":{"type":"string"},"channel":{"type":"string","enum":["mpesa","airtel","card","bank","wallet","cash"]},"parent_transaction_id":{"type":["string","null"]},"metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"object":{"type":"string","const":"transaction"},"mode":{"enum":["live","sandbox"],"type":"string"},"provider_ref":{"type":["string","null"]},"next_action":{"oneOf":[{"type":"object","properties":{"expires_at":{"type":"string"},"type":{"const":"await_payer","type":"string"},"provider_ref":{"type":"string"},"payer_message":{"type":["string","null"]}},"required":["type","provider_ref","payer_message","expires_at"]},{"required":["type","url","provider_ref","expires_at"],"type":"object","properties":{"expires_at":{"type":"string"},"type":{"type":"string","const":"redirect"},"url":{"type":"string"},"provider_ref":{"type":"string"}}},{"type":"object","properties":{"type":{"type":"string","const":"none"}},"required":["type"]}]},"amount_minor":{"maximum":9007199254740991,"type":"integer","minimum":-9007199254740991},"settled_at":{"type":["string","null"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","maxLength":64},"reference":{"type":"string"},"recipient_phone":{"type":["string","null"]},"expires_at":{"type":["string","null"]},"workspace_id":{"type":"string"},"direction":{"type":"string","enum":["collection","payout","refund"]},"description":{"type":"string"},"payer_email":{"type":["string","null"]},"recipient_name":{"type":["string","null"]},"source":{"maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused.","type":["string","null"],"pattern":"^[a-z][a-z0-9_-]{0,63}$"},"settled_minor":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"payment_link_id":{"type":["string","null"]},"payer_phone":{"type":["string","null"]},"provider_receipt":{"type":["string","null"]},"status":{"enum":["pending","processing","succeeded","failed","cancelled","expired","reversed","unknown"],"type":"string"},"payer_name":{"type":["string","null"]},"recipient_account":{"type":["string","null"]},"failure_reason":{"type":["string","null"]},"created_at":{"type":"string"}}},"PatchPaymentLinkRequest":{"type":"object","properties":{"source":{"description":"The project slug. Pay sets it from the project; a different value is refused.","type":["string","null"],"pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64},"title":{"type":"string","minLength":1,"maxLength":256},"description":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}]},"currency":{"maxLength":3,"pattern":"^[A-Z]{3}$","type":"string","minLength":3},"max_amount_minor":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000},{"type":"null"}]},"reference":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}],"pattern":"^[A-Za-z0-9][A-Za-z0-9 ._/-]*$"},"return_url":{"anyOf":[{"type":"string","maxLength":2000,"format":"uri","pattern":"^https://[^\\s/?#@]+(?:[/?#]\\S*)?$"},{"type":"null"}]},"amount_minor":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000},{"type":"null"}]},"min_amount_minor":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000},{"type":"null"}]},"allow_custom_amount":{"type":"boolean"},"channels":{"items":{"type":"string","enum":["mpesa","airtel","card","bank","wallet","cash"]},"type":"array"},"active":{"type":"boolean"},"metadata":{"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"maxLength":512,"type":"string"}}},"x-contract-schema":"patchPaymentLinkSchema"},"ProviderDefinition":{"type":"object","properties":{"modes":{"type":"array","items":{"type":"string"}},"fields":{"type":"array","items":{"properties":{"public":{"type":"boolean"},"secret":{"type":"boolean"},"long":{"type":"boolean"},"toggle":{"type":"boolean"},"name":{"type":"string"},"hint":{"type":"string"},"mode_pattern":{"type":"string"},"options":{"type":"array","items":{"type":"string"}},"optional":{"type":"boolean"},"label":{"type":"string"},"example":{"type":"string"},"mode_values":{"additionalProperties":{"type":"string"},"type":"object"}},"required":["name","label"],"type":"object"}},"description":{"type":"string"},"name":{"type":"string"},"config_schema":{"properties":{"required":{"type":"array","items":{"type":"string"}},"x-property-order":{"type":"array","items":{"type":"string"}},"type":{"type":"string","const":"object"},"properties":{"type":"object","additionalProperties":{"type":"object","properties":{"enum":{"type":"array","items":{"type":"string"}},"default":{"type":["string","boolean"]},"pattern":{"type":"string"},"minLength":{"type":"integer"},"maxLength":{"type":"integer"},"type":{"type":"string"},"const":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true,"type":"object","description":"JSON Schema for the credentials this gateway accepts."},"confirm_payouts":{"type":"boolean","description":"Payout results are confirmed with the provider before they are applied."},"icon":{"type":"string"},"registers":{"type":"string"},"provider":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","maxLength":64},"currencies":{"type":"array","items":{"type":"string"}},"reference_length":{"type":"integer"},"priority":{"additionalProperties":{"type":"integer"},"type":"object"},"configurable":{"type":"boolean"},"channels":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","properties":{"status":{"type":"boolean"},"balances":{"type":"boolean"},"collections":{"type":"array","items":{"type":"string"}},"payouts":{"type":"array","items":{"type":"string"}},"refunds":{"type":"boolean"}},"required":["collections","payouts","refunds","status","balances"]},"confirm_unsigned_collections":{"type":"boolean"}},"required":["provider","name","configurable","channels","modes","currencies","fields","capabilities","config_schema","confirm_unsigned_collections","icon","description","priority"]},"PaymentLinkAnalytics":{"type":"object","properties":{"gross_minor":{"type":"integer"},"settled_minor":{"type":"integer"},"attempts":{"type":"integer"},"unknown":{"type":"integer"},"fees_minor":{"type":"integer"},"average_minor":{"type":"integer"},"last_payment_at":{"type":["string","null"]},"daily":{"type":"array","items":{"properties":{"day":{"type":"string"},"count":{"type":"integer"},"amount_minor":{"type":"integer"}},"required":["day","count","amount_minor"],"type":"object"}},"object":{"type":"string","const":"payment_link.analytics"},"payment_link_id":{"type":"string"},"currency":{"type":"string"},"succeeded":{"type":"integer"},"pending":{"type":"integer"},"failed":{"type":"integer"}},"required":["object","payment_link_id","currency","attempts","succeeded","pending","failed","unknown","gross_minor","settled_minor","fees_minor","average_minor","last_payment_at","daily"]},"PaymentMethodList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"object":{"const":"list","type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PaymentMethod"}},"next_cursor":{"type":["string","null"]}},"required":["object","data","next_cursor"]},"PaymentMethod":{"x-contract-schema":"paymentMethodSchema","type":"object","properties":{"retired_at":{"type":["string","null"]},"last_verified_at":{"type":["string","null"]},"created_at":{"type":"string"},"application_id":{"type":"string","format":"uuid"},"mode":{"type":"string","enum":["live","sandbox"]},"configured":{"type":"boolean"},"config_hint":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"callback_url":{"type":"string"},"id":{"type":"string"},"channels":{"type":"array","items":{"type":"string","enum":["mpesa","airtel","card","bank","wallet","cash"]}},"object":{"type":"string","const":"payment_method"},"provider":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","maxLength":64},"label":{"type":"string"},"enabled":{"type":"boolean"},"ipn_url":{"type":["string","null"],"description":"Register this as the IPN URL on the provider app to receive payments customers make outside a Pay request. Null when the provider has none."}},"required":["id","object","provider","mode","label","channels","enabled","configured","config_hint","callback_url","last_verified_at","created_at"]},"UpsertWebhookEndpointRequest":{"type":"object","properties":{"description":{"type":"string","maxLength":500},"event_types":{"default":[],"type":"array","items":{"type":"string","enum":["created","dispatched","callback_received","queried","succeeded","failed","cancelled","expired","outcome_unknown","reversed"]}},"enabled":{"default":true,"type":"boolean"},"source":{"pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused.","type":["string","null"]},"include_provider_payload":{"type":"boolean","description":"Include redacted gateway callback data alongside normalized payment fields."},"url":{"type":"string","maxLength":2000,"format":"uri"}},"required":["url"],"x-contract-schema":"upsertWebhookEndpointSchema"},"ChannelCodeCatalog":{"type":"object","properties":{"fetched_at":{"type":"string"},"stale":{"type":"boolean"},"object":{"type":"string","const":"channel_codes"},"bank":{"type":"array","items":{"$ref":"#/components/schemas/ChannelCode"}},"source":{"type":"string"}},"required":["object","bank","source","fetched_at"]},"WebhookDeliveryList":{"properties":{"object":{"const":"list","type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"next_cursor":{"type":["string","null"]}},"required":["object","data","next_cursor"],"type":"object"},"Error":{"properties":{"error":{"required":["code","message"],"type":"object","properties":{"request_id":{"type":"string"},"code":{"type":"string","enum":["validation_error","unauthorized","forbidden","not_found","conflict","idempotency_required","idempotency_mismatch","rate_limited","provider_not_configured","provider_rejected","provider_unavailable","outcome_unknown","amount_not_settleable","duplicate_receipt","not_refundable","insufficient_balance","link_closed","unsupported","internal_error","application_archived","application_required"]},"message":{"type":"string"},"details":{}}}},"required":["error"],"x-contract-schema":"errorSchema","type":"object"},"UpsertPaymentMethodRequest":{"required":["label","channels","config"],"x-contract-schema":"upsertPaymentMethodSchema","type":"object","properties":{"config":{"$ref":"#/components/schemas/ProviderConfig"},"label":{"type":"string","minLength":1,"maxLength":128},"mode":{"type":"string","enum":["live","sandbox"],"default":"live"},"channels":{"minItems":1,"type":"array","items":{"type":"string","enum":["mpesa","airtel","card","bank","wallet","cash"]}},"enabled":{"default":true,"type":"boolean"}}},"Balance":{"type":"object","properties":{"object":{"const":"balance","type":"string"},"provider":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","maxLength":64},"mode":{"type":"string","enum":["live","sandbox"]},"available":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"amount_minor":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["currency","amount_minor"]}},"retrieved_at":{"type":"string"}},"required":["object","provider","mode","available","retrieved_at"],"x-contract-schema":"balanceSchema"},"PaymentLinkList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PaymentLink"}},"next_cursor":{"type":["string","null"]}},"required":["object","data","next_cursor"]},"StatementImport":{"type":"object","properties":{"ambiguous":{"minimum":-9007199254740991,"maximum":9007199254740991,"type":"integer"},"created_at":{"type":"string"},"application_id":{"type":"string","format":"uuid"},"id":{"type":"string"},"object":{"type":"string","const":"statement_import"},"settled_minor":{"minimum":-9007199254740991,"maximum":9007199254740991,"type":"integer"},"rows":{"type":"array","items":{"type":"object","properties":{"receipt":{"type":"string"},"amount_minor":{"minimum":-9007199254740991,"maximum":9007199254740991,"type":"integer"},"outcome":{"enum":["settled","duplicate","unmatched","ambiguous"],"type":"string"},"transaction_id":{"type":["string","null"]},"reference":{"type":["string","null"]},"detail":{"type":["string","null"]}},"required":["receipt","amount_minor","outcome","transaction_id","reference","detail"]}},"label":{"type":"string"},"currency":{"type":"string"},"settled":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"duplicates":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"replayed":{"type":"boolean"},"payment_method_id":{"type":["string","null"],"format":"uuid"},"unmatched":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"unmatched_minor":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"source":{"type":"string"},"row_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","object","source","label","currency","row_count","settled","duplicates","unmatched","ambiguous","settled_minor","unmatched_minor","replayed","rows","created_at"],"x-contract-schema":"statementImportSchema"},"WebhookDelivery":{"required":["object","id","workspace_id","application_id","endpoint_id","event_id","transaction_id","event_type","endpoint_url","endpoint_enabled","status","attempts","max_attempts","response_status","last_error","next_attempt_at","delivered_at","created_at"],"x-contract-schema":"webhookDeliverySchema","type":"object","properties":{"endpoint_enabled":{"type":"boolean"},"delivered_at":{"type":["string","null"]},"max_attempts":{"type":"integer"},"workspace_id":{"type":"string","format":"uuid"},"application_id":{"type":"string","format":"uuid"},"endpoint_url":{"type":"string"},"status":{"type":"string","enum":["pending","delivering","delivered","failed"]},"next_attempt_at":{"type":"string"},"last_error":{"type":["string","null"]},"created_at":{"type":"string"},"response_status":{"type":["integer","null"]},"id":{"format":"uuid","type":"string"},"endpoint_id":{"type":"string","format":"uuid"},"event_type":{"type":"string"},"attempts":{"type":"integer"},"object":{"type":"string","const":"webhook_delivery"},"event_id":{"type":"string","format":"uuid"},"transaction_id":{"type":"string","format":"uuid"}}},"ProviderConfig":{"oneOf":[{"properties":{"initiator_name":{"maxLength":64,"minLength":1,"type":"string"},"initiator_password":{"minLength":1,"type":"string","maxLength":256},"passkey":{"maxLength":256,"minLength":8,"type":"string"},"provider":{"const":"daraja","type":"string"},"shortcode":{"pattern":"^\\d{5,10}$","type":"string"},"certificate":{"minLength":64,"type":"string","maxLength":8192},"transaction_type":{"default":"CustomerPayBillOnline","enum":["CustomerPayBillOnline","CustomerBuyGoodsOnline"],"type":"string"},"consumer_key":{"maxLength":256,"minLength":8,"type":"string"},"consumer_secret":{"minLength":8,"type":"string","maxLength":256}},"required":["provider","consumer_key","consumer_secret","shortcode","passkey"],"type":"object","title":"M-Pesa Daraja"},{"properties":{"consumer_key":{"maxLength":256,"minLength":8,"type":"string"},"consumer_secret":{"maxLength":256,"minLength":8,"type":"string"},"debit_account":{"maxLength":10,"minLength":1,"type":"string"},"pass_key":{"maxLength":256,"minLength":8,"type":"string"},"provider":{"const":"kcb_buni","type":"string"},"route_code":{"maxLength":32,"minLength":1,"type":"string"},"shared_short_code":{"default":false,"type":"boolean"},"api_key":{"maxLength":256,"minLength":8,"type":"string"},"ipn_public_key":{"maxLength":4096,"minLength":64,"type":"string"},"short_code":{"pattern":"^\\d{5,10}$","type":"string"},"company_code":{"maxLength":15,"minLength":1,"type":"string"}},"required":["provider","consumer_key","consumer_secret","api_key","short_code","pass_key","route_code","ipn_public_key"],"type":"object","title":"KCB Buni"},{"properties":{"provider":{"const":"paystack","type":"string"},"public_key":{"minLength":16,"type":"string","maxLength":256},"secret_key":{"maxLength":256,"minLength":16,"type":"string"}},"required":["provider","secret_key"],"type":"object","title":"Paystack"},{"properties":{"client_secret":{"maxLength":256,"minLength":8,"type":"string"},"merchant_code":{"maxLength":32,"minLength":3,"type":"string"},"provider":{"const":"sasapay","type":"string"},"client_id":{"maxLength":256,"minLength":8,"type":"string"}},"required":["provider","client_id","client_secret","merchant_code"],"type":"object","title":"SasaPay"}]},"CreateRefundRequest":{"type":"object","properties":{"transaction_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"amount_minor":{"maximum":1000000000000,"type":"integer","exclusiveMinimum":0},"reason":{"type":"string","minLength":1,"maxLength":512},"metadata":{"propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"type":"string","maxLength":512},"default":{},"type":"object"},"source":{"type":"string","pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused."}},"required":["transaction_id","reason"],"x-contract-schema":"createRefundSchema"},"WebhookPayload":{"x-contract-schema":"webhookPayloadSchema","type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["created","dispatched","callback_received","queried","succeeded","failed","cancelled","expired","outcome_unknown","reversed"]},"created_at":{"type":"string"},"data":{"type":"object","properties":{"payment_link_id":{"type":["string","null"]},"status":{"type":"string","enum":["pending","processing","succeeded","failed","cancelled","expired","reversed","unknown"]},"currency":{"type":"string"},"parent_transaction_id":{"type":["string","null"]},"application_id":{"type":"string","format":"uuid"},"provider_receipt":{"type":["string","null"]},"transaction_id":{"type":["string","null"]},"workspace_id":{"type":"string"},"channel":{"type":"string","enum":["mpesa","airtel","card","bank","wallet","cash"]},"underpaid":{"type":"boolean"},"fee_minor":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"detail":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"mode":{"type":"string","enum":["live","sandbox"]},"amount_minor":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"reference":{"type":"string"},"failure_reason":{"type":["string","null"]},"metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"provider_ref":{"type":["string","null"]},"settled_at":{"type":["string","null"]},"provider_payload":{"type":"object","additionalProperties":{},"description":"Optional redacted gateway callback, included only when enabled on the destination."},"direction":{"type":"string","enum":["collection","payout","refund"]},"provider":{"pattern":"^[a-z][a-z0-9_]{0,63}$","maxLength":64,"type":"string"},"settled_minor":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"source":{"type":["string","null"],"pattern":"^[a-z][a-z0-9_-]{0,63}$","maxLength":64,"description":"The project slug. Pay sets it from the project; a different value is refused."}},"required":["transaction_id","workspace_id","direction","status","provider","channel","currency","amount_minor","settled_minor","underpaid","fee_minor","reference","provider_receipt","failure_reason","metadata","detail"]},"sequence":{"type":"integer","minimum":0,"description":"Increasing sequence within a transaction. Deduplicate by event id and ignore older sequences."}},"required":["id","type","created_at","data"]},"StatementImportList":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"object":{"const":"list","type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/StatementImport"}},"next_cursor":{"type":["string","null"]}},"required":["object","data","next_cursor"]},"TransactionSummary":{"required":["object","currency","period_days","unknown","waiting","daily"],"type":"object","properties":{"waiting":{"type":"integer"},"daily":{"type":"array","items":{"properties":{"day":{"type":"string","format":"date"},"collected":{"type":"integer"},"paid_out":{"type":"integer"}},"required":["day","collected","paid_out"],"type":"object"}},"object":{"const":"transaction_summary","type":"string"},"currency":{"type":"string"},"period_days":{"type":"integer"},"unknown":{"type":"integer"}}}}}}