Writing API Tasks

API Tasks verify and provide a transcript for the execution of web services' API calls.

During the Early Access Program, all API Task activities are performed by Truebit. In the initial version of the API Task service, a transcript is provided but no secondary verification by decentralized nodes occurs.

With API Tasks, the Truebit Verify Hub proxies requests to an external API endpoint. API Tasks are dispatched and executed only by the Truebit Verify Hub, which secures sensitive data such as API credentials and allows for execution of a single, audited API call. In a role reversal, Truebit Verify Nodes then audit the network transaction originated by the hub, providing full transparency for each call.

API Tasks are accessed via a built-in web services interface provided by the Truebit Verify Hub, and can be called by any application using HTTP. Each API Task execution is recorded in a transcript.

Prerequisites

There are several important considerations when defining an API Task:

  • OpenAPI Schemas: Truebit Verify uses the OpenAPI schema specification as the basis for the API Manifest that defines the interaction with an external HTTP-based API. The OpenAPI schema describes the servers, data formats, validation rules, etc. for a given API endpoint. The Truebit Verify Hub will validate all API Task calls using the OpenAPI schema before executing a network request. Many APIs already provide an OpenAPI schema as part of their documentation β€” and this can serve as a good starting point for defining your API Manifest. Truebit supports OpenAPI Schema v3 and later.

  • Credentials: Many APIs require authentication credentials to be provided as part of the network request. We currently support basic auth, AWS key, and custom HTTP Header credentials. The Truebit Verify CLI allows you to upload encrypted credentials to the Truebit Verify Hub, where they will be securely stored in their encrypted state. When possible, we recommend obtaining credentials specifically scoped to the authorization requirements of your API Task, and rotating credentials in accordance with your team's security practices.

  • No Determinsim Requirement: Truebit Verify uses a different verification strategy for APIs which does not require deterministic outputs to be provided by the API.

Building an API Task

1. Define the API Manifest

The task developer provides an OpenAPI schema as an API Manifest for the relevant APIs that will be proxied by Truebit Verify. We recommend using a tool like Swagger Editor to help develop the OpenAPI schema used in your manifest. If using a pre-existing OpenAPI schema provided by an API publisher, we recommend reviewing the contents of the schema to remove any unneeded endpoints and/or add any additional validation rules as needed by your API Task.

Using OpenAPI for the Manifest

OpenAPI Specification (formerly Swagger Specification) is an API description format for REST APIs. An OpenAPI file allows you to describe an entire API, including:

  • Available endpoints (/users) and operations on each endpoint (GET /users, POST /users)

  • Operation parameters Input and output for each operation

  • Authentication methods

  • Contact information, license, terms of use and other information.

Advantages

  • It’s a standard widely used

  • Most of the APIs will have an OpenAPI specification ready to be used

  • Native support of validations

  • Can be customized to include/exclude fields

  • Can include credentials or other definitions (using extensibility mechanisms)

Example

PET SHOP example manifest
{
  "openapi": "3.0.3",
  "info": {
    "title": "Swagger Petstore - OpenAPI 3.0",
    "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification.",
    "contact": {
      "email": "[email protected]"
    },
    "license": {
      "name": "Apache 2.0",
      "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
    },
    "version": "1.0.11"
  },
  "externalDocs": {
    "description": "Find out more about Swagger",
    "url": "http://swagger.io"
  },
  "servers": [
    {
      "url": "https://petstore3.swagger.io/api/v3"
    }
  ],
  "paths": {
    "/store/order": {
      "post": {
        "tags": [
          "store"
        ],
        "summary": "Place an order for a pet",
        "description": "Place a new order in the store",
        "operationId": "placeOrder",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Order"
              }
            },
            "application/xml": {
              "schema": {
                "$ref": "#/components/schemas/Order"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/Order"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "405": {
            "description": "Invalid input"
          }
        }
      }
    },
    "/store/order/{orderId}": {
      "get": {
        "tags": [
          "store"
        ],
        "summary": "Find purchase order by ID",
        "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.",
        "operationId": "getOrderById",
        "parameters": [
          {
            "name": "orderId",
            "in": "path",
            "description": "ID of order that needs to be fetched",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              },
              "application/xml": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID supplied"
          },
          "404": {
            "description": "Order not found"
          }
        }
      },
      "delete": {
        "tags": [
          "store"
        ],
        "summary": "Delete purchase order by ID",
        "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",
        "operationId": "deleteOrder",
        "parameters": [
          {
            "name": "orderId",
            "in": "path",
            "description": "ID of the order that needs to be deleted",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "400": {
            "description": "Invalid ID supplied"
          },
          "404": {
            "description": "Order not found"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Order": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "example": 10
          },
          "petId": {
            "type": "integer",
            "format": "int64",
            "example": 198772
          },
          "quantity": {
            "type": "integer",
            "format": "int32",
            "example": 7
          },
          "shipDate": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "description": "Order Status",
            "example": "approved",
            "enum": [
              "placed",
              "approved",
              "delivered"
            ]
          },
          "complete": {
            "type": "boolean"
          }
        },
        "xml": {
          "name": "order"
        }
      },
      "ApiResponse": {
        "type": "object",
        "properties": {
          "code": {
            "type": "integer",
            "format": "int32"
          },
          "type": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        },
        "xml": {
          "name": "##default"
        }
      }
    }
  }
}

2. Develop the API Task

The task developer creates a new API task by calling the create-api command from the Truebit CLI using the defined manifest as the input parameter. As a result, a new taskId will be returned.

3. Test the API Task

The task developer tests the API task using the start-api command from the Truebit CLI.

What if the endpoint requires authentication?

If the API endpoint you want to call is authenticated, you'll need to provide the necessary credentials when running the start-api command. Currently, we support three authentication methods:

  • custom-header

  • basic-auth

  • aws-signature

How to pass credentials

Custom Header

Use this method when the API expects authentication via custom headers.

truebit start-api [options] <manifestPath> <input> custom-header "username:<username>,password:<password>"

Basic Auth

Use this for standard HTTP Basic Authentication.

truebit start-api [options] <manifestPath> <input> basic-auth "username:<username>,password:<password>"

AWS Signature

Use this for endpoints protected with AWS Signature Version 4.

truebit start-api [options] <manifestPath> <input> aws-signature "accessKey:<accesskey>,secretKey:<secretkey>"

4. Deploy the API Task

When the API Task is ready, the Task Developer deploys the task using the deploy command from the Truebit CLI.

Deploying Credentials for Authenticated endpoints

If the endpoints defined in your manifest require authentication (custom-header, basic-auth, or aws-signature), you must also deploy the corresponding credentials to ensure the Task Requester can execute the task successfully.

To do this, use the api-auth command along with the appropriate authentication method and credentials:

truebit api-auth <namespace> <taskname> <taskId> <authType> <authString>

Authorize the Task execution

The Task Authorization process is an optional step, but it becomes mandatory when the Task Requester has registered the task. This step allows the Task Developer to define who has permission to execute the deployed task.

Click Here to see more information about the Task Authorization process.

5. Execute the API Task

The Task Requester executes the task using the Task Execution Endpoint from the Truebit API.

Click Here to view the list of pre-developed Truebit Tasks created for you to start getting familiar with the task development

Last updated

Was this helpful?