nips/105.md

230 lines
7.6 KiB
Markdown
Raw Normal View History

2023-09-13 09:34:27 -04:00
NIP-105
======
API Service Marketplace
-------------------------------
`draft` `optional` `author:coachchuckff` `author:unclejim21` `author:cmdruid`
This NIP defines `kind:31402` (a _parameterized replaceable event_) for broadcasting API services, endpoints and their costs in mSats. API services are offered ala carte to be paid via lightning invoices.
API service providers will issue a `kind:31402` event as an API service offering. Clients can fetch `s` (service) tag offerings, call their endpoints, and pay per use. Creating an API service marketplace.
## Protocol flow
1. API Service provider creates and hosts API service offerings, they then issue a `kind:31402` event for each `s` ( service ) tag.
2. Client fetches API offerings by `s` tag.
3. Client chooses a provider(s) and POSTs to their `endpoint`. The POST body should match exactly what is required by the underlying API less the API key. ( Or following the note's `schema` )
4. The service provider should validate their request and return a lightning invoice with a `successAction` url. The `successAction` url should be formatted as such: `<endpoint>/<invoice_hash>/get_result`
5. The client pays.
6. The client will poll the `successAction` until the service provider has returned the result(s) matching exactly what the underlying API will produce ( Or following the note's `outputSchema` ).
## Server Functions
### Create Invoice
Initally, the client will make a POST request to the `endpoint`. The POST body should be exactly what the underlying API is expecting less the API key. This information should also be represented in the `schema` portion of the `content` field.
The service provider will then do the following:
1. Validate their API request
2. Issue a lightning invoice
The issued lightning invoice will contain a `successAction` url formatted as so:
```typescript
{
tag: "url",
url: `https://example.api/chat/${paymentHash}/get_result`,
description: "Open to get the confirmation code for your purchase."
}
```
From here the service provider can either listen and execute their API request "on-payment" or wait until the client calls `get_results`.
### Get Results
Once the client has gotten the `successAction` url, they can issue a GET request. The service provider should respond in one of three ways:
1. 200 ( DONE ) - the service provider returns the results of the API as if it was coming from the product itself ( Errors included )
2. 202 ( WORKING ) - the service provider returns a status that indicates the results are not ready yet.
3. 402 ( NOT PAID YET ) - the service provider should respond with a 402 error if the client has not paid yet.
It is up to the Client to poll the `successAction` until a terminal result is reached.
## Event Reference and Examples
### Offering Event
`kind:31402`
`.content` should be a JSON stringified version of the following JSON:
```typescript
enum OfferingStatus {
up = 'UP',
down = 'DOWN',
closed = 'CLOSED'
}
interface OfferingContent = {
endpoint: string, // The POST endpoint you call to pay/fetch
status: OfferingStatus, // UP/DOWN/CLOSED
cost: number, // The cost per call in mSats
schema?: Object, // Reccomended - JSON schema for the POST body of the endpoint
outputSchema?: Object, // Reccomended - JSON schema for the response of the call
description?: string // Optional - Description for the end user
}
```
`.tag` MUST include the following:
- `s`, the service tag should simply be the underlying API endpoint of the service provided. For example if you are offering a ChatGPT service, you would set `s` = `https://api.openai.com/v1/chat/completions`. This way the service they are buying is implicit.
- `d`, following **parameterized replaceable events** in [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md), this tag allows this event to be replaceable. Since there should only be one service per pubkey, the `d` tag should match the `s` tag.
### Example Service Event
```typescript
const content = {
endpoint: "https://example.api/chat/",
status: "UP",
cost: 5000,
schema: {...},
outputSchema: {...},
description: "This will call the ChatGPT endpoint"
}
const event = {
"kind": 31402,
"created_at": 1675642635,
"content": JSON.stringify(content),
"tags": [
["s", "https://api.openai.com/v1/chat/completions"]
["d", "https://api.openai.com/v1/chat/completions"],
],
"pubkey": "<pubkey>",
"id": "<id>"
}
```
### Example Schema
The following is for the `gpt-3.5-turbo` input schema:
```json
{
"type": "object",
"properties": {
"model": {
"type": "string",
"enum": ["gpt-3.5-turbo"]
},
"messages": {
"type": "array",
"items": {
"type": "object",
"properties": {
"role": {
"type": "string",
"enum": ["system", "user"]
},
"content": {
"type": "string"
}
},
"required": ["role", "content"]
},
"minItems": 1
}
},
"required": ["model", "messages"]
}
```
The following is for the `gpt-3.5-turbo` output schema:
```json
{
"type": "object",
"required": ["id", "object", "created", "model", "choices", "usage"],
"properties": {
"id": {
"type": "string",
"pattern": "^chatcmpl-[a-zA-Z0-9-]+$"
},
"object": {
"type": "string",
"enum": ["chat.completion"]
},
"created": {
"type": "integer"
},
"model": {
"type": "string"
},
"choices": {
"type": "array",
"items": {
"type": "object",
"required": ["index", "message"],
"properties": {
"index": {
"type": "integer"
},
"message": {
"type": "object",
"required": ["role", "content"],
"properties": {
"role": {
"type": "string",
"enum": ["assistant"]
},
"content": {
"type": "string"
}
}
},
"finish_reason": {
"type": "string",
"enum": ["stop"]
}
}
}
},
"usage": {
"type": "object",
"required": ["prompt_tokens", "completion_tokens", "total_tokens"],
"properties": {
"prompt_tokens": {
"type": "integer"
},
"completion_tokens": {
"type": "integer"
},
"total_tokens": {
"type": "integer"
}
}
}
}
}
```
### Safey
It is not mandatory, but to raise the barrier to entry, clients should screen service provider's NIP-05 identifier. The domain used in their NIP-05, should be the same domain used for their endpoint.
Clients may wish to create a whitelist of trusted service providers once tested.
### Problems
- No data integrity - service providers can store/redistribute any data passed to them
- Service providers could take payment and never return the product
- Service providers are not gaurenteed to call the endpoint specified in the `s` tag
- No recourse for errored API fetches
- The `cost` field may not match the actual final price
- No proof of purchase
### Example Implementations
- [Server](https://github.com/Team-Pleb-TabConf-2023/nip-105-server)
- [Client](https://github.com/Team-Pleb-TabConf-2023/nip-105-client)