From b509dbcd0f9c13b77adb7492ac2bfa418b72d48c Mon Sep 17 00:00:00 2001 From: Kevin Edry Date: Fri, 30 Jan 2026 09:36:24 -0800 Subject: [PATCH] feat: add beginner-friendly first request guide Add new "Making Your First Request" documentation page to help non-technical trial users successfully make their first API call. Changes: - Add interactive API key tester component - Add "Run in Postman" button linking to synced Postman collection - Add step-by-step Postman and cURL instructions - Add troubleshooting section for common errors - Update getting-started page with link to new guide - Add GitHub Action to sync OpenAPI spec to Postman on deploy --- .github/workflows/deploy.yml | 12 + README.md | 4 + .../docs/documentation/getting-started.mdx | 6 + .../making-your-first-request.mdx | 145 +++++++++ content/docs/documentation/meta.json | 1 + package.json | 1 + public/postman/collection.json | 301 ++++++++++++++++++ .../generators/postman/postman.generator.ts | 274 ++++++++++++++++ src/app/api/test-proxy/route.ts | 26 ++ src/components/activation/api-key-tester.tsx | 248 +++++++++++++++ src/components/activation/postman-button.tsx | 82 +++++ src/mdx-components.tsx | 2 + 12 files changed, 1102 insertions(+) create mode 100644 content/docs/documentation/making-your-first-request.mdx create mode 100644 public/postman/collection.json create mode 100644 scripts/generators/postman/postman.generator.ts create mode 100644 src/app/api/test-proxy/route.ts create mode 100644 src/components/activation/api-key-tester.tsx create mode 100644 src/components/activation/postman-button.tsx diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 553be12..a8123aa 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -32,6 +32,18 @@ jobs: - name: Generate OpenAPI docs run: bun run generate:openapi + - name: Generate Postman collection + run: bun run generate:postman + + - name: Push collection to Postman + run: | + curl --silent --show-error --fail \ + --request PUT \ + --url "https://api.getpostman.com/collections/${{ vars.POSTMAN_COLLECTION_UID }}" \ + --header "X-Api-Key: ${{ secrets.POSTMAN_API_KEY }}" \ + --header "Content-Type: application/json" \ + --data "{\"collection\": $(cat ./public/postman/collection.json)}" + - name: Build with OpenNext run: bunx opennextjs-cloudflare build diff --git a/README.md b/README.md index ca284d0..8575ca9 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ View Documentation

+

+ Run In Postman +

+ --- ## Development diff --git a/content/docs/documentation/getting-started.mdx b/content/docs/documentation/getting-started.mdx index 0d9bca5..bc8fe0b 100644 --- a/content/docs/documentation/getting-started.mdx +++ b/content/docs/documentation/getting-started.mdx @@ -17,6 +17,12 @@ To access the API, you'll need an API key. Sign up for a **free trial** at [whit for a replacement. + + Follow our [Making Your First + Request](/documentation/making-your-first-request) guide for step-by-step + instructions using Postman—no coding required. + + ## Available APIs diff --git a/content/docs/documentation/making-your-first-request.mdx b/content/docs/documentation/making-your-first-request.mdx new file mode 100644 index 0000000..eb73a81 --- /dev/null +++ b/content/docs/documentation/making-your-first-request.mdx @@ -0,0 +1,145 @@ +--- +title: Making Your First Request +description: A beginner-friendly guide to making your first Whitepages API request +icon: Play +--- + +Ready to test your API key? You can try it right here in your browser, or follow the step-by-step guide using Postman. + +## Quick Test + + + +## Using Postman (No Code Required) + +Postman is a free tool that lets you interact with APIs without writing code. Click the button below to import our pre-configured collection, or follow the manual setup steps. + + + Run In Postman + + +### What You'll Need + +- Your Whitepages API key (received via email after [signing up for a trial](/documentation/getting-trial-api-key)) +- [Postman](https://www.postman.com/downloads/) (free download) + +### Step-by-Step Guide + + + +### Download and Open Postman + +Download Postman from [postman.com/downloads](https://www.postman.com/downloads/) and install it. Create a free account or skip sign-in to continue. + + + + +### Create a New Request + +1. Click the **+** button to open a new request tab +2. Make sure **GET** is selected in the dropdown (it's the default) + + + + +### Enter the API URL + +Paste this URL into the request field: + +``` +https://api.whitepages.com/v1/person?name=John%20Smith&address.city=Seattle&address.state_code=WA +``` + + + + +### Add Your API Key + +1. Click the **Headers** tab below the URL field +2. In the **Key** column, enter: `X-Api-Key` +3. In the **Value** column, paste your API key + + + Make sure to use `X-Api-Key` exactly as shown, with capital X, A, and K. + + + + + +### Send the Request + +Click the **Send** button. You should see a JSON response in the panel below. + +```json title="Example Response" +[ + { + "id": "P1234567890", + "name": "John Smith", + "current_addresses": [ + { + "address": "123 Main St, Seattle, WA 98101" + } + ], + "phones": [ + { + "number": "(206) 555-0198", + "type": "mobile" + } + ] + } +] +``` + +If you see data like this, your API key is working correctly. + + + + +## Using cURL (Command Line) + +If you prefer the command line, you can use cURL: + +```bash +curl 'https://api.whitepages.com/v1/person?name=John%20Smith&address.city=Seattle&address.state_code=WA' \ + --header 'X-Api-Key: YOUR_API_KEY' +``` + +Replace `YOUR_API_KEY` with your actual API key. + +## Troubleshooting + +### 403 Forbidden or "Invalid API Key" + +- Double-check that you copied the entire API key from your email +- Verify the header name is exactly `X-Api-Key` (case-sensitive) +- Make sure there are no extra spaces before or after your API key + +### No Results Found + +A 404 response with no results is normal—it means your API key is working, but no records matched your search. Try different search parameters. + +### Need Help? + +Contact [support@whitepages.com](mailto:support@whitepages.com) if you continue to have issues. + +## Next Steps + +Now that you've made your first request, explore what else you can do: + + + + + diff --git a/content/docs/documentation/meta.json b/content/docs/documentation/meta.json index 0dab51c..cfedc37 100644 --- a/content/docs/documentation/meta.json +++ b/content/docs/documentation/meta.json @@ -5,6 +5,7 @@ "pages": [ "getting-started", "getting-trial-api-key", + "making-your-first-request", "purchasing-api", "---Guides---", "person-search", diff --git a/package.json b/package.json index 3a53c64..e81465e 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "format:check": "bunx prettier . --check", "format:write": "bunx prettier . --write", "generate:openapi": "bun run scripts/generators/openapi/openapi.generator.ts", + "generate:postman": "bun run scripts/generators/postman/postman.generator.ts", "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", "deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy" }, diff --git a/public/postman/collection.json b/public/postman/collection.json new file mode 100644 index 0000000..53c36ff --- /dev/null +++ b/public/postman/collection.json @@ -0,0 +1,301 @@ +{ + "info": { + "name": "Whitepages API Reference", + "description": "Comprehensive API for accessing Whitepages data with enterprise-grade authentication, authorization, and usage tracking.\n\n

Features

\n\n- **Person Search**: Find individuals by name, phone number, address, and id\n- **Property Search**: Access property information and ownership details by address\n- **Enhanced Data Access**: API provides a more powerful way to search our dataset\n- **Usage Tracking**: Detailed usage accounting and reporting\n- **Rate Limiting**: Intelligent rate limiting to ensure optimal performance\n- **Data Validation**: Comprehensive input validation and sanitization\n\n

Authentication

\n\nAll requests require a valid API key provided in the X-Api-Key header. Contact our support team for API key provisioning and management.\n\n

Usage Accounting

\n\nAPI usage is tracked and billed based on successful requests:\n\n

Billing Rules

\n\n- **Billable Requests**: All 2xx (success) and 4xx (client error) responses from Person and Property endpoints are charged\n- **Non-Billable Requests**: 5xx (server error) and 429 (rate limit) responses are not charged\n- **Billing Period**: Usage is calculated from the start of the month in UTC time\n- **Usage Reports**: Detailed usage reports are available through the `/v1/account/usage` endpoint\n\n

Rate Limits

\n\nAPI usage is rate-limited to ensure system stability and fair usage across all customers. Rate limit violations return a 429 status code with retry information.\n\n

Getting Started

\n\nNew to the API? Check out our QuickStart Guide for step-by-step examples.\n\n

Support

\n\nFor API key management, rate limit increases, quota adjustments, or technical support, please contact our team at support@whitepages.com.\n", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Person", + "item": [ + { + "name": "Search for a person by name, phone number, and address", + "request": { + "method": "GET", + "header": [ + { + "key": "X-Api-Key", + "value": "{{apiKey}}", + "description": "Your Whitepages API key" + } + ], + "url": { + "raw": "https://api.whitepages.com/v1/person", + "protocol": "https", + "host": ["api", "whitepages", "com"], + "path": ["v1", "person"], + "query": [ + { + "key": "phone", + "value": "", + "description": "Phone number to search for a person by", + "disabled": true + }, + { + "key": "name", + "value": "", + "description": "Full name, first name, last name, or partial name match for the person", + "disabled": true + }, + { + "key": "first_name", + "value": "", + "description": "First name of the person. Cannot be used with 'name' parameter.", + "disabled": true + }, + { + "key": "middle_name", + "value": "", + "description": "Middle name of the person. Cannot be used with 'name' parameter.", + "disabled": true + }, + { + "key": "last_name", + "value": "", + "description": "Last name of the person. Cannot be used with 'name' parameter.", + "disabled": true + }, + { + "key": "street", + "value": "", + "description": "Full or partial street address including street number, name", + "disabled": true + }, + { + "key": "city", + "value": "", + "description": "Full city name", + "disabled": true + }, + { + "key": "state_code", + "value": "", + "description": "2-Letter code representing a state name", + "disabled": true + }, + { + "key": "zipcode", + "value": "", + "description": "Full 5-digit ZIP code", + "disabled": true + }, + { + "key": "include_historical_locations", + "value": "", + "description": "Include historical addresses in search. When false (default), only current addresses are included.", + "disabled": true + }, + { + "key": "min_age", + "value": "", + "description": "Minimum age of the person to search for, must be between 18 and 65", + "disabled": true + }, + { + "key": "max_age", + "value": "", + "description": "Maximum age of the person to search for, must be between 18 and 65", + "disabled": true + }, + { + "key": "radius", + "value": "", + "description": "Search radius in miles for distance-based filtering", + "disabled": true + } + ] + }, + "description": "Retrieve person information based on the provided query parameters.\n\nThis endpoint accepts a person query request and returns full details for\nup to the top 15 matching persons from Whitepages data." + } + }, + { + "name": "Gets person details by id", + "request": { + "method": "GET", + "header": [ + { + "key": "X-Api-Key", + "value": "{{apiKey}}", + "description": "Your Whitepages API key" + } + ], + "url": { + "raw": "https://api.whitepages.com/v1/person/:id", + "protocol": "https", + "host": ["api", "whitepages", "com"], + "path": ["v1", "person", ":id"] + }, + "description": "Retrieve detailed person information by Whitepages person ID.\n\nThis endpoint accepts a valid Whitepages person ID in the path parameter\nand returns the complete person record if the ID exists in our data." + } + } + ] + }, + { + "name": "Property", + "item": [ + { + "name": "Get property details by address", + "request": { + "method": "GET", + "header": [ + { + "key": "X-Api-Key", + "value": "{{apiKey}}", + "description": "Your Whitepages API key" + } + ], + "url": { + "raw": "https://api.whitepages.com/v1/property?street=", + "protocol": "https", + "host": ["api", "whitepages", "com"], + "path": ["v1", "property"], + "query": [ + { + "key": "street", + "value": "", + "description": "Full or partial street address including street number, name", + "disabled": false + }, + { + "key": "city", + "value": "", + "description": "Full city name", + "disabled": true + }, + { + "key": "state_code", + "value": "", + "description": "2-Letter code representing a state name", + "disabled": true + }, + { + "key": "zip", + "value": "", + "disabled": true + } + ] + }, + "description": "Retrieve property information based on the provided query parameters.\n\nThis endpoint accepts a property query request and returns the matching\nproperty details if found within Whitepages data." + } + } + ] + }, + { + "name": "Account", + "item": [ + { + "name": "Retrieve usage data for a specific time range", + "request": { + "method": "GET", + "header": [ + { + "key": "X-Api-Key", + "value": "{{apiKey}}", + "description": "Your Whitepages API key" + } + ], + "url": { + "raw": "https://api.whitepages.com/v1/account/usage?start_date=&end_date=", + "protocol": "https", + "host": ["api", "whitepages", "com"], + "path": ["v1", "account", "usage"], + "query": [ + { + "key": "start_date", + "value": "", + "description": "Start date of the duration to get usage data for. Format: YYYY-MM-DD", + "disabled": false + }, + { + "key": "end_date", + "value": "", + "description": "End date of the duration to get usage data for. Format: YYYY-MM-DD", + "disabled": false + } + ] + }, + "description": "Retrieve API usage data for a specified date range.\n\nReturns daily usage statistics including request counts for each day\nwithin the specified time period. The response includes both individual\ndaily usage data and total usage for the entire period.\n\n- The request count is total number of requests made including 2xx, 4xx\nand 5xx responses. It is not the same as billable requests which is not\navailable right now as part of API.\n- Maximum date range allowed: 90 days\n- Dates are in UTC format\n- Current day data when requested will be updated during the day\n- Usage data is updated approximately every 30 minutes\n- Usage data is returned only from the first date of actual usage in\nthe specified duration" + } + } + ] + }, + { + "name": "Property V2", + "item": [ + { + "name": "Get property by ID", + "request": { + "method": "GET", + "header": [ + { + "key": "X-Api-Key", + "value": "{{apiKey}}", + "description": "Your Whitepages API key" + } + ], + "url": { + "raw": "https://api.whitepages.com/v2/property/:property_id", + "protocol": "https", + "host": ["api", "whitepages", "com"], + "path": ["v2", "property", ":property_id"] + }, + "description": "Retrieve property information by property ID." + } + }, + { + "name": "Search for property by address", + "request": { + "method": "GET", + "header": [ + { + "key": "X-Api-Key", + "value": "{{apiKey}}", + "description": "Your Whitepages API key" + } + ], + "url": { + "raw": "https://api.whitepages.com/v2/property", + "protocol": "https", + "host": ["api", "whitepages", "com"], + "path": ["v2", "property"], + "query": [ + { + "key": "street", + "value": "", + "description": "Street address including number and name", + "disabled": true + }, + { + "key": "city", + "value": "", + "description": "City name", + "disabled": true + }, + { + "key": "state_code", + "value": "", + "description": "Two-letter state code", + "disabled": true + }, + { + "key": "zipcode", + "value": "", + "description": "5", + "disabled": true + } + ] + }, + "description": "Search for property by address parameters." + } + } + ] + } + ], + "variable": [ + { + "key": "apiKey", + "value": "YOUR_API_KEY", + "type": "string" + } + ] +} diff --git a/scripts/generators/postman/postman.generator.ts b/scripts/generators/postman/postman.generator.ts new file mode 100644 index 0000000..7e3daa9 --- /dev/null +++ b/scripts/generators/postman/postman.generator.ts @@ -0,0 +1,274 @@ +const OPENAPI_URL = "https://api.whitepages.com/openapi.json"; +const OUTPUT_PATH = "./public/postman/collection.json"; + +interface PostmanCollection { + info: { + name: string; + description?: string; + schema: string; + }; + item: PostmanItem[]; + variable?: PostmanVariable[]; +} + +interface PostmanItem { + name: string; + item?: PostmanItem[]; + request?: PostmanRequest; +} + +interface PostmanRequest { + method: string; + header: PostmanHeader[]; + url: PostmanUrl; + description?: string; +} + +interface PostmanHeader { + key: string; + value: string; + description?: string; +} + +interface PostmanUrl { + raw: string; + protocol: string; + host: string[]; + path: string[]; + query?: PostmanQuery[]; +} + +interface PostmanQuery { + key: string; + value: string; + description?: string; + disabled?: boolean; +} + +interface PostmanVariable { + key: string; + value: string; + type?: string; +} + +interface OpenAPISpec { + openapi: string; + info: { + title: string; + description?: string; + version: string; + }; + servers?: Array<{ url: string }>; + paths: Record>; + tags?: Array<{ name: string; description?: string }>; +} + +interface OpenAPIOperation { + summary?: string; + description?: string; + operationId?: string; + tags?: string[]; + parameters?: OpenAPIParameter[]; + requestBody?: OpenAPIRequestBody; +} + +interface OpenAPIParameter { + name: string; + in: string; + description?: string; + required?: boolean; + schema?: { type?: string; default?: unknown }; +} + +interface OpenAPIRequestBody { + description?: string; + content?: Record; +} + +async function fetchOpenAPISpec(): Promise { + const response = await fetch(OPENAPI_URL); + + if (!response.ok) { + throw new Error(`Failed to fetch OpenAPI spec: ${response.statusText}`); + } + + return response.json(); +} + +function parseServerUrl(servers?: Array<{ url: string }>): { + protocol: string; + host: string[]; +} { + const serverUrl = servers?.[0]?.url || "https://api.whitepages.com"; + const url = new URL(serverUrl); + return { + protocol: url.protocol.replace(":", ""), + host: url.host.split("."), + }; +} + +function convertPathToPostmanPath(path: string): string[] { + return path + .split("/") + .filter(Boolean) + .map((segment) => { + if (segment.startsWith("{") && segment.endsWith("}")) { + return `:${segment.slice(1, -1)}`; + } + return segment; + }); +} + +function buildQueryParameters( + parameters?: OpenAPIParameter[], +): PostmanQuery[] | undefined { + const queryParams = parameters?.filter((p) => p.in === "query"); + if (!queryParams?.length) return undefined; + + return queryParams.map((param) => ({ + key: param.name, + value: String(param.schema?.default ?? ""), + description: param.description, + disabled: !param.required, + })); +} + +function buildHeaders(parameters?: OpenAPIParameter[]): PostmanHeader[] { + const headers: PostmanHeader[] = [ + { + key: "X-Api-Key", + value: "{{apiKey}}", + description: "Your Whitepages API key", + }, + ]; + + const headerParams = parameters?.filter((p) => p.in === "header"); + headerParams?.forEach((param) => { + if (param.name.toLowerCase() !== "x-api-key") { + headers.push({ + key: param.name, + value: String(param.schema?.default ?? ""), + description: param.description, + }); + } + }); + + return headers; +} + +function convertOperationToPostmanRequest( + path: string, + method: string, + operation: OpenAPIOperation, + serverInfo: { protocol: string; host: string[] }, +): PostmanRequest { + const postmanPath = convertPathToPostmanPath(path); + const queryParams = buildQueryParameters(operation.parameters); + + let rawUrl = `${serverInfo.protocol}://${serverInfo.host.join(".")}/${postmanPath.join("/")}`; + if (queryParams?.length) { + const enabledParams = queryParams + .filter((q) => !q.disabled) + .map((q) => `${q.key}=${q.value}`) + .join("&"); + if (enabledParams) { + rawUrl += `?${enabledParams}`; + } + } + + return { + method: method.toUpperCase(), + header: buildHeaders(operation.parameters), + url: { + raw: rawUrl, + protocol: serverInfo.protocol, + host: serverInfo.host, + path: postmanPath, + query: queryParams, + }, + description: operation.description || operation.summary, + }; +} + +function groupOperationsByTag( + paths: OpenAPISpec["paths"], + serverInfo: { protocol: string; host: string[] }, +): Map { + const tagGroups = new Map(); + + for (const [path, methods] of Object.entries(paths)) { + for (const [method, operation] of Object.entries(methods)) { + if (typeof operation !== "object" || !operation) continue; + + const tags = operation.tags || ["Other"]; + const request = convertOperationToPostmanRequest( + path, + method, + operation, + serverInfo, + ); + + const itemName = + operation.summary || + operation.operationId || + `${method.toUpperCase()} ${path}`; + + for (const tag of tags) { + if (!tagGroups.has(tag)) { + tagGroups.set(tag, []); + } + tagGroups.get(tag)!.push({ + name: itemName, + request, + }); + } + } + } + + return tagGroups; +} + +function convertOpenAPIToPostman(spec: OpenAPISpec): PostmanCollection { + const serverInfo = parseServerUrl(spec.servers); + const tagGroups = groupOperationsByTag(spec.paths, serverInfo); + + const items: PostmanItem[] = []; + for (const [tagName, tagItems] of tagGroups) { + items.push({ + name: tagName, + item: tagItems, + }); + } + + return { + info: { + name: spec.info.title, + description: spec.info.description, + schema: + "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + }, + item: items, + variable: [ + { + key: "apiKey", + value: "YOUR_API_KEY", + type: "string", + }, + ], + }; +} + +async function main(): Promise { + console.log("Fetching OpenAPI spec..."); + const spec = await fetchOpenAPISpec(); + + console.log("Converting to Postman collection..."); + const collection = convertOpenAPIToPostman(spec); + + console.log(`Writing collection to ${OUTPUT_PATH}...`); + await Bun.write(OUTPUT_PATH, JSON.stringify(collection, null, 2)); + + console.log("Done!"); +} + +void main(); diff --git a/src/app/api/test-proxy/route.ts b/src/app/api/test-proxy/route.ts new file mode 100644 index 0000000..6dac787 --- /dev/null +++ b/src/app/api/test-proxy/route.ts @@ -0,0 +1,26 @@ +import { NextRequest, NextResponse } from "next/server"; + +export async function GET(request: NextRequest) { + const apiKey = request.headers.get("X-Api-Key"); + const { searchParams } = new URL(request.url); + + const queryString = searchParams.toString(); + const url = `https://api.whitepages.com/v1/person?${queryString}`; + + try { + const response = await fetch(url, { + headers: { + "X-Api-Key": apiKey || "", + }, + }); + + const data = await response.json().catch(() => null); + + return NextResponse.json(data, { status: response.status }); + } catch { + return NextResponse.json( + { error: "Failed to connect to API" }, + { status: 502 }, + ); + } +} diff --git a/src/components/activation/api-key-tester.tsx b/src/components/activation/api-key-tester.tsx new file mode 100644 index 0000000..7919e00 --- /dev/null +++ b/src/components/activation/api-key-tester.tsx @@ -0,0 +1,248 @@ +"use client"; + +import { useState } from "react"; +import { buttonVariants } from "@/components/ui/button"; + +interface ApiResponse { + success: boolean; + message: string; + details?: string; + data?: unknown; + status?: number; +} + +const MOCK_DATA = [ + { + id: "P1234567890", + name: "John Smith", + age_range: "35-44", + current_addresses: [ + { + id: "A9876543210", + address: "123 Main St, Seattle, WA 98101", + is_current: true, + }, + ], + phones: [ + { + number: "(206) 555-0198", + type: "mobile", + is_primary: true, + }, + ], + emails: [ + { + address: "john.smith@email.com", + is_primary: true, + }, + ], + }, +]; + +interface ApiKeyTesterProps { + mockMode?: boolean; +} + +export function ApiKeyTester({ mockMode = false }: ApiKeyTesterProps) { + const [apiKey, setApiKey] = useState(mockMode ? "demo-api-key-xxxxx" : ""); + const [isLoading, setIsLoading] = useState(false); + const [response, setResponse] = useState( + mockMode + ? { + success: true, + message: "Success! Your API key is working.", + data: MOCK_DATA, + status: 200, + } + : null, + ); + + const testApiKey = async () => { + if (!apiKey.trim()) { + setResponse({ + success: false, + message: "Please enter an API key", + }); + return; + } + + setIsLoading(true); + setResponse(null); + + try { + const result = await fetch( + "/docs/api/test-proxy?name=John%20Smith&address.city=Seattle&address.state_code=WA", + { + headers: { + "X-Api-Key": apiKey.trim(), + }, + }, + ); + + const data = await result.json().catch(() => null); + + if (result.ok) { + setResponse({ + success: true, + message: "Success! Your API key is working.", + data, + status: result.status, + }); + } else if (result.status === 403) { + setResponse({ + success: false, + message: "Invalid API key", + details: + "The API key you entered is not valid. Please check your email for the correct API key.", + status: result.status, + }); + } else if (result.status === 429) { + setResponse({ + success: false, + message: "Rate limit exceeded", + details: + "Your API key is valid but you've exceeded the rate limit. Wait a moment and try again.", + status: result.status, + }); + } else { + setResponse({ + success: false, + message: `Request failed (${result.status})`, + details: data?.message || "An unexpected error occurred.", + data, + status: result.status, + }); + } + } catch { + setResponse({ + success: false, + message: "Connection error", + details: + "Unable to connect to the API. Please check your internet connection and try again.", + }); + } finally { + setIsLoading(false); + } + }; + + const handleKeyDown = (event: React.KeyboardEvent) => { + if (event.key === "Enter" && !isLoading) { + testApiKey(); + } + }; + + return ( +
+

Try It Now

+

+ Enter your API key below to make a test request and see real results. +

+ +
+
+ + setApiKey(event.target.value)} + onKeyDown={handleKeyDown} + placeholder="Paste your API key here" + className="w-full px-3 py-2 border rounded-md bg-fd-background text-fd-foreground focus:outline-none focus:ring-2 focus:ring-fd-ring" + disabled={isLoading} + /> +
+ + + + {response && ( +
+
+
+ {response.success ? ( + + + + ) : ( + + + + )} +
+
+

+ {response.message} +

+ {response.details && ( +

+ {response.details} +

+ )} +
+
+
+ )} + + {response?.data !== undefined && ( +
+

Response Data

+
+              {JSON.stringify(response.data, null, 2)}
+            
+
+ )} +
+
+ ); +} diff --git a/src/components/activation/postman-button.tsx b/src/components/activation/postman-button.tsx new file mode 100644 index 0000000..e4f08d0 --- /dev/null +++ b/src/components/activation/postman-button.tsx @@ -0,0 +1,82 @@ +"use client"; + +import { buttonVariants } from "@/components/ui/button"; + +export function PostmanButton() { + const handleDownload = () => { + const link = document.createElement("a"); + link.href = "/docs/postman/whitepages-api-collection.json"; + link.download = "Whitepages-API-Collection.json"; + link.click(); + }; + + return ( +
+
+
+ + + +
+
+

Get Postman Collection

+

+ Download our pre-configured Postman collection with example requests + for Person Search and Property Search endpoints. Just import it into + Postman and add your API key. +

+
+ + + How to Import + + + + +
+
+
+
+ ); +} diff --git a/src/mdx-components.tsx b/src/mdx-components.tsx index cb71a5b..b18b1e4 100644 --- a/src/mdx-components.tsx +++ b/src/mdx-components.tsx @@ -1,6 +1,7 @@ import defaultMdxComponents from "fumadocs-ui/mdx"; import { Step, Steps } from "fumadocs-ui/components/steps"; import { APIPage } from "@/components/openapi/api-page"; +import { ApiKeyTester } from "@/components/activation/api-key-tester"; import type { MDXComponents } from "mdx/types"; export function getMDXComponents(components?: MDXComponents): MDXComponents { @@ -9,6 +10,7 @@ export function getMDXComponents(components?: MDXComponents): MDXComponents { Steps, Step, APIPage, + ApiKeyTester, ...components, }; }