mirror of
https://github.com/nostr-protocol/nips.git
synced 2024-12-22 08:25:53 -05:00
working
This commit is contained in:
parent
70ede5e67d
commit
b6f3e249cd
229
105.md
Normal file
229
105.md
Normal file
|
@ -0,0 +1,229 @@
|
|||
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)
|
Loading…
Reference in New Issue
Block a user