Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
dist/
*.tsbuildinfo
.env*
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,24 @@
# x402-directory
A curated, agent-maintained directory of x402 payment-enabled applications and endpoints. Browse, search, and discover services that accept x402 micropayments. Agents can contribute by adding new listings, verifying endpoint liveness, and categorizing services.

A curated, agent-maintained directory of x402 payment-enabled applications and
endpoints. Browse, search, and discover services that accept x402 micropayments.
Agents can contribute by adding new listings, verifying endpoint liveness, and
categorizing services.

## Listing Data Model

This repository starts with a simple JSON-backed data model for v1 listings.

- `schema/listing.schema.json` defines the JSON Schema contract.
- `src/schema.ts` exports TypeScript types and category/status constants.
- `data/listings.json` contains seed listings using the schema.
- `src/validate.ts` validates the seed data without external runtime dependencies.

## Scripts

```bash
npm install
npm run typecheck
npm run build
npm run validate
```
36 changes: 36 additions & 0 deletions data/listings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[
{
"id": "x402_weather_quote",
"name": "Weather Quote API",
"url": "https://weather-quote.example/x402",
"description": "Returns a paid current-weather summary for a requested city through an x402-protected HTTP endpoint.",
"category": "data-feeds",
"pricing": {
"amount": "0.001",
"currency": "USDC",
"unit": "request",
"network": "base",
"details": "Flat per-request price for JSON weather summaries."
},
"status": "unverified",
"createdAt": "2026-05-11T00:00:00.000Z",
"updatedAt": "2026-05-11T00:00:00.000Z"
},
{
"id": "x402_agent_icon",
"name": "Agent Icon Renderer",
"url": "https://agent-icon.example/render",
"description": "Generates a small paid PNG icon from a text prompt and returns the image URL after x402 payment.",
"category": "ai-ml-apis",
"pricing": {
"amount": "0.02",
"currency": "USDC",
"unit": "render",
"network": "base",
"details": "Per successful image render."
},
"status": "unverified",
"createdAt": "2026-05-11T00:00:00.000Z",
"updatedAt": "2026-05-11T00:00:00.000Z"
}
]
47 changes: 47 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "x402-directory",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit",
"validate": "npm run build && node dist/src/validate.js"
},
"devDependencies": {
"@types/node": "22.15.17",
"typescript": "5.6.3"
}
}
97 changes: 97 additions & 0 deletions schema/listing.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/shipyard-projects/x402-directory/schema/listing.schema.json",
"title": "x402 Directory Listing",
"type": "object",
"additionalProperties": false,
"required": [
"id",
"name",
"url",
"description",
"category",
"pricing",
"status",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"pattern": "^x402_[a-z0-9_]+$"
},
"name": {
"type": "string",
"minLength": 2,
"maxLength": 80
},
"url": {
"type": "string",
"format": "uri"
},
"description": {
"type": "string",
"minLength": 20,
"maxLength": 280
},
"category": {
"type": "string",
"enum": [
"ai-ml-apis",
"data-feeds",
"compute",
"storage",
"content",
"developer-tools",
"identity",
"other"
]
},
"pricing": {
"type": "object",
"additionalProperties": false,
"required": ["amount", "currency", "unit", "network"],
"properties": {
"amount": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?$"
},
"currency": {
"type": "string",
"minLength": 2,
"maxLength": 16
},
"unit": {
"type": "string",
"minLength": 3,
"maxLength": 64
},
"network": {
"type": "string",
"minLength": 2,
"maxLength": 32
},
"details": {
"type": "string",
"maxLength": 180
}
}
},
"status": {
"type": "string",
"enum": ["verified", "unverified"]
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"updatedAt": {
"type": "string",
"format": "date-time"
},
"lastCheckedAt": {
"type": "string",
"format": "date-time"
}
}
}
44 changes: 44 additions & 0 deletions src/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
export const listingCategories = [
"ai-ml-apis",
"data-feeds",
"compute",
"storage",
"content",
"developer-tools",
"identity",
"other"
] as const;

export const listingStatuses = ["verified", "unverified"] as const;

export type ListingCategory = (typeof listingCategories)[number];
export type ListingStatus = (typeof listingStatuses)[number];

export type PricingInfo = {
amount: string;
currency: string;
unit: string;
network: string;
details?: string;
};

export type X402Listing = {
id: `x402_${string}`;
name: string;
url: string;
description: string;
category: ListingCategory;
pricing: PricingInfo;
status: ListingStatus;
createdAt: string;
updatedAt: string;
lastCheckedAt?: string;
};

export function isListingCategory(value: string): value is ListingCategory {
return listingCategories.includes(value as ListingCategory);
}

export function isListingStatus(value: string): value is ListingStatus {
return listingStatuses.includes(value as ListingStatus);
}
Loading