Provider API

The Manifold Provider API is a set of HTTP endpoints a developer services provider implements as a part of their application stack in order to integrate with Manifold's identity, billing, and provisioning features.

By implementing resource and credential provisioning, deprovisioning, and single sign-on, a provider is capable of offering a complete end-to-end experience for developers to buy and integrate their products using a Manifold account, without having to implement billing or identity.

Fully detailed in its OpenAPI specification.

Grafton

What is Grafton?

Manifold provides a CLI tool called Grafton for locally testing a provider's implementation of resource and credential provisioning, resizing, deprovision, and sign in. We made it to make it easier for providers to validate whether or not their implementation works with our system. Grafton is open source with releases available on GitHub.

Using Grafton

To start using Grafton, you have to first generate a test master key which will be used for signing requests.

$ grafton generate
Generating Master Keypair
Writing master keypair to file: masterkey.json
Success.

The public master key contained in the generated file can be used to validate requests created by Grafton during testing. Once your service has been integrated onto Manifold, you’d need to use the Master Public Signing Key to validate requests coming from our servers.

You can use the following set of OAuth Credential to performs OAuth operations and any subsequent requests against our Connector API. These must be provided to Grafton and used by your server during local testing.

CLIENT_ID=21jtaatqj8y5t0kctb2ejr6jev5w8
CLIENT_SECRET=3yTKSiJ6f5V5Bq-kWF0hmdrEUep3m3HKPTcPX7CdBZw 

After you’ve finished configuring your server with the generated key and OAuth credentials, you’re ready to start testing with Grafton! Invoke the test command and supply the product, plan, region, OAuth credentials, and connector port as flags.

$ grafton test --product=bonnets --plan=small --region=aws::us-east-1 \
 	   --client-id=21jtaatqj8y5t0kctb2ejr6jev5w8 \
 	   --client-secret=3yTKSiJ6f5V5Bq-kWF0hmdrEUep3m3HKPTcPX7CdBZw \
  	  --connector-port=3000 \ # Port used by Grafton to serve Connector API
 	   --new-plan=large \ # Plan to upgrade to once the resource has been provisioned
    http://localhost:4567 # Full URL of the API implementation

For each passed test, a green check mark will be displayed. Once all the tests pass, you're ready to move onto the next phase of integration!

An example integration using Ruby and the Sinatra framework is available on GitHub. Trying this out would be a great way to start.

Grafton and this documentation is a work in progress, we appreciate and welcome all feedback!

Frequently Asked Questions

Why does the API separate Resource and Credential creation?

Over time, as Manifold evolves, we want to tackle the problem of credential management for both providers and developers. To achieve this, credentials must be a first class citizen of our platform which are addressable so they can added, removed, and updated independent of the Resource lifecycle.

Grafton already takes advantages of this in its credential rotation strategies.

What's the difference between the id in the path and body of a PUT request?

They are the same, the id is provided in the body for completeness and convenience.

Which identifier should be used for reference?

All Resource and Credential IDs are globally unique and can be used for tracking internal data structures against their Manifold counterparts.

A Credential ID should only be used in relation to a set of Credentials (username and password or connection url) belonging to a Resource (e.g. a database instance).

What if my product does not support regions?

For simplicity, a Resource will always have a region property. Products which do not support a region will receive the all::global region in the Provision request payload.

What are the single and multiple credential types?

The credential types allow Grafton to determine if your product supports multiple live credential simultaneously or not.

  • multiple credentials support means that your product can be accessed using different live credentials simultaneously.
  • single credentials support means that your product only supports one live credential simultaneously.

Grafton defaults to multiple since it offers the best experience to developers especially when performing a credential rotation.

What are the credential rotation tests?

The Grafton credential rotation tests ensure that the Credentials endpoints can be used to perform a rotation. Credential rotation is a process managed my Manifold that results in a new credential being used instead of the initial one.

Grafton will choose the best rotation strategy based on the type of credentials supported: single or multiple.

For single credentials, the initial credential is deleted first, and then a new one is created in its place. This is the replace strategy.

For multiple credentials, a new credential is created first, and then the old one gets deleted. This is the swap strategy.

Grafton also ensures that the initial and new credentials are different.

Authentication

In order to provision, deprovision, or resize resources and credentials, our system performs HTTP calls against your API. All requests sent by us are signed, allowing you to validate that the requests were made by us.

The X-Signature header contains the signature of the request, the public key of the Ed25519 key-pair used to sign the request, and the signature of this public key signed by the Manifold master offline signing key.

This provides a complete chain of trust from the signature of the request all the way to the root signing key allowing you to verify the integrity and authenticity of the request from Manifold.

Included in the signature are the request method, path, query parameters, various headers (including the Date and Host headers), and the request body.

Requests to GET /v1/sso are not signed by us. Instead, a provider must validate that the user has access to the resource by requesting GET /v1/resources/:id from the Connector API using an authorization_code granted access token.

Authentication libraries

Manifold provides the following SDKs, making it easy for a provider to verify any incoming requests from Manifold:

Please reach out to Manifold if we're missing the language of your choice.

* Community Project

Request Signing Process

Manifold takes two steps to sign all outgoing requests to providers:

  1. Create the canonical form of the request.
  2. Sign the request.

Create the Canonical Form of the Request To begin, write the target of the signature, as follows.

lower(METHOD) < space > PATH <'?'> canonical(QUERY) <newline>

Here canonical(QUERY) will contain the query parameters, lexicographically sorted in ascending order (including parameter name, = sign, and value), and delimited by an &. If no query parameters are set, the ? after PATH is omitted. Example: put /v1/resources?foo=bar\n

Once we’ve defined the target, we then add all the headers in the X-Signed-Headers header, in the order they are listed, followed by the X-Signed-Headers header itself.

These headers would appear as follows.

lower(NAME) <colon> <space> VALUES <newline>

VALUES have all optional whitespace removed. If the header occurs multiple times on the request, the values are included delimited by , (comma space), in the order they appear on the request.

The X-Signed-Headers header includes the list of all headers included in the canonical request form, lowercased, and delimited by a space. Only one occurrence of X-Signed-Headers should exist on a request. If more than one exists, the first occurrence is used.

Manifold includes all headers that are explicitly set on any request we send, including the callback related headers.

Example: content-type: application/json\n

Finally, the contents of the request body are added to the canonical request form.

Sign the Request The request signature is created by creating an Ed25519 signature of the canonical request form, using a live signing key-pair. This key-pair's public key is signed by our offline master key. The master key signature is included with each signature.

The master key signature is included in the X-Signature header, which has the form:

X-Signature: <request_signature> <live_public_key> <master_key_signature>

Verifying a Request

Verifying a request follows similar steps to those listed above for signing a request.

  1. Verify the Request Age
  2. Create the canonical form of the request (Identical to the request signing step)
  3. Verify the signature

Verify the Request Age To prevent replay attacks, you must verify that value relayed in the Date (RFC3339 Format) header is within 5 minutes of the current time. If it is not, a 401 error should be returned to the caller.

Verify the Signature To verify a request signature, you must first verify that the signature of the public key included in the header is valid and signed by our offline signing key. Then, verify that the signature of the request is a valid signature on the canonical form of the request, and that it was signed by the public key included in the X-Signature header.

Public Master Key

The following is the public key used for generating endorsements encoded in base64. PtISNzqQmQPBxNlUw3CdxsWczXbIwyExxlkRqZ7E690 This key is included as the default key in our signature checking libraries.

The Manifold master key was generated using a high quality entropy source on an air-gapped system. Access to the private portion of the key is restricted; 3 of the 5 members of Manifold that have access to it must be present to decrypt the key as portions of the key are distributed to each member through Shamir's Secret Sharing and uniquely encrypted for each member. The master key is only used to sign live signing keys, which will in turn be used to sign the requests made to providers.

Repeatable Actions

In the case of network errors or other unexpected errors (such as a 500 Internal Server Error), we may issue a request more than once with the same payload. The result returned should always be the same, thereby requiring the APIs you implement to be idempotent.

We only use idempotent HTTP methods to perform calls that provision, modify, or deprovision resources and credentials, specifically.

PUT: To create a resource or credential, Manifold will issue a PUT request. The unique identifier provided in the URL must be used by the provider for identifying the created entity. If the entity does not exist in this case, but the properties match the entity stored by you, then the request is treated as a success, and a 201 Created or 204 No Content response must be returned to us. However, if the entity does exist, but the properties do not match, a 409 Conflict response must be returned by the provider.

PATCH: To update a resource or credential, Manifold will issue a PATCH request against the targeted entity. For this, a 200 Success or 204 No Content response must be returned if the entity exists and the update was successful. However, if the entity does not exist, a 404 Not Found error must be returned.

Example: If a PUT /v1/resources/:id is received twice with the same id value, the provider should ensure that the plan, product, and region (if applicable) values are the exact same. If they are the same, a 201 Created or 204 No Content response should be returned. Otherwise, a 409 Conflict must be returned.

Callback

For requests that may not be able to be completed by you within 60 seconds, we allow you to return a 202 Accepted response, along with a message that will be shown to the user. The default integration does not require support for callbacks. However, you must decide if long-running provisioning must be supported through your implementation of the API.

Once the provider has completed the provision, plan change, or deprovision operation they must send the outcome to the callback URL provided on the initial request to the Connector API.

If the Connector API can't be reached or an unexpected response is returned (for instance, 500 Internal Server Error), you should attempt to invoke the callback in the future until you get an expected response.

The Connector API Callback route matches the "Repeatable Actions" specification. If the callback has already been received and the payload matches the previous request a 204 No Content response will be returned. However, if the payloads do not match a 409 Conflict error response will be returned.

If we do not receive the callback within 24 hours, the request will be performed again. As a result of supporting repeatable actions, you should return whether or not the operation was successful to us. Expected behaviour, in this scenario, is documented on a route-by-route basis.

The callback URL and a unique identifier for the callback are sent along in the request as defined below.

X-Callback-ID: A unique 18 byte base32 encoded unique identifier sent to the provider for the purposes of logging and debugging. X-Callback-URL: The URL to call to complete the operation and report the outcome to us. The callback request is authenticated using an access token granted using the client_credentials OAuth 2.0 flow. Please see the Connector API documentation for more information.

Errors

We require providers to return a relevant HTTP Status Code with an appropriate, human-readable error message, in the format below, if an error occurs, that will be shown to the user inside our dashboard.

Example Response

{ "message": "Cannot upgrade to the cookies plan from chocolate-bars." } Depending on the route, the response status code and the corresponding message may be interpreted differently, and will be documented on a per-route basis.

Resource

For a more detailed API reference, click here.

Provision

Our API will call this endpoint to request the provisioning of a resource using the provided identifier. This route must support being called more than once with the same payload.

The id property is the unique identifier that we will map to this resource. You should use this value for mapping Manifold Resources to data inside your systems.

The product, plan, and region properties are machine-readable names for the type of product, its plan, and the region in which the requested resource is to be provisioned. These values map to configuration stored in our catalog database.

A response should only be returned once an error has occurred or the provisioned resource is ready for a user to use. If a requested action takes longer than 60 seconds to complete, a callback must be used.

Request Timeout

If the request takes longer than 60 seconds, then it is assumed to have failed. We will retry the request again.

Callback Timeout

If a 202 Accepted response is returned, we expect the provider to complete the provision flow by calling the callback URL within 24 hours. If the callback is not invoked, Manifold will retry the request.

If the resource has been provisioned successfully with properties that match the request, then the provider should return a 201 Created or 204 No Content response. However, if the resource provisioning failed, a corresponding error should be returned.

Change Plan

Our system will call this endpoint to request a change in plan of a resource, for example, an upgrade or downgrade. This route must support being called more than once with the same payload.

The plan property is the machine-readable name of the new plan that the resource is being resized to. The list of possible values are provided by you and stored in our catalog.

A response should only be returned when the change is completed, or if an error occurs. If a requested action takes longer than 60 seconds to complete, a callback must be used.

Request Timeout

If the request takes longer than 60 seconds, then it is assumed to have failed, and will be retried in the future.

Callback Timeout

If a 202 Accepted response is returned, we expect the provider to complete the plan change flow by calling the callback URL within 24 hours. If the callback is not invoked, we will retry the request again.

If the resource's plan matches the given plan, then a 200 Success or 204 No Content response must be returned.

Deprovision

Manifold calls this endpoint to request a resource be deprovisioned. When a resource is deprovisioned, all attached credentials are assumed to be deprovisioned as well.

If the resource has already been deprovisioned, then the provider should return a 404 response.

A response should only be returned once an error has occurred or when the resource is no longer accessible by the user. If a requested action takes longer than 60 seconds to complete, a callback must be used.

Request Timeout

If the request takes longer than 60 seconds, then it is assumed to have failed, and will be retried in the future.

Callback Timeout

If a 202 Accepted response is returned, we expect the provider to complete the plan change flow by calling the callback URL within 24 hours. If the callback is not invoked, we will retry the request again.

If the deprovision was successful, then a 204 Not Found response should be returned to Manifold.

Resource and Feature Usage

We call this endpoint daily to get usage information about a resource and its features. You only need to hold information about the time left in the current month and the previous month. Months are defined by the Manifold billing period which starts at the first of each month in UTC-0 and ends at the start of the next.

Credential

For a more detailed API reference, click here.

Provision

Manifold calls this endpoint to request the provisioning of a set of credentials for the specified resource. This route must support being called more than once with the same payload.

The id property is the unique identifier that we will map to this set of credentials. You should use this value for mapping Manifold Credentials to data inside your systems.

The resource_id property is the unique identifier for the resource that the requested set of credentials grant access to.

The provider can return multiple key-value pairs that represent this set of credentials. However, if a URL form exists (e.g. postgres://user:pw@host:5432/db), please provide the credentials in that form.

Request Timeout

If the request takes longer than 60 seconds, then it is assumed to have failed, and will be retried in the future.

Callback Timeout

If a 202 Accepted response is returned, we expect the provider to complete the provision flow by calling the callback URL within 24 hours. If the callback is not invoked, we will retry the request again.

If the credentials have been provisioned successfully with properties that match the request, then a 201 Created response must be returned. If provisioning has failed, a corresponding error must be returned.

Deprovision

Manifold calls this endpoint to request the deprovisioning the specified set of credentials.

When a resource is deprovisioned, we assume that all associated credentials are deprovisioned by the provider as well.

If the credentials have already been deprovisioned, then the provider must return a 404 response.

Request Timeout

If the request takes longer than 60 seconds, then it is assumed to have failed, and will be retried in the future.

Callback Timeout

If a 202 Accepted response is returned, we expect the provider to complete the plan change flow by calling the callback URL within 24 hours. If the callback is not invoked, we will retry the request again. If the deprovision was successful, then a 204 Not Found response should be returned to Manifold.

Single Sign-On

For a more detailed API reference, click here.

Users can sign in to your product’s dashboard for a resource they’ve provisioned straight from our dashboard. The session on your product’s dashboard that the user sees is restricted to the resource they used to log into it. Having access to the dashboard lets users use features that are specific to your product.

The single sign-on (SSO) flow is built on top of OAuth 2.0 using the authorization_code grant type flow for granting you an access token that communicates the user and the selected resource.

We are responsible for creating a scoped authorization_code and forwarding the user to your implementation of the GET /v1/sso route. The code to grant an access token and the selected resource_id are included as query parameters to the request.

An access token can be created by the provider by issuing a POST request to /v1/oauth/tokens which is a part of our Connector API.

Once an access token has been granted, you can issue requests on behalf of the user to the Connector API to retrieve information about the current user and the resource in question.

You must create your own login session with the user for further authentication for accessing the resources dashboard. The granted access token should be stored securely and only used by your server to request data from us to render or implement any dashboard functionality.

The granted access token will only be valid for 24 hours. After the token has expired, the user must re-do the SSO from our dashboard to grant you a new token.

For the SSO, a code and resource_id are sent as query parameters allowing the provider to request an access token. The code, which communicates the initiating user and resource, is only valid for five minutes and cannot be used more than once. Once an access token has been acquired for this user's session by your server, the user should be forwarded to the resource's dashboard.

The access token should be associated with the user for the duration of their session. The access token is sensitive data that grants access to a user's data within Manifold. It should be treated with care.

Requests to this route will not be signed by us as they are performed by the user's browser.

IMPORTANT: A malicious user could tamper with the resource_id query parameter. To prevent this, the provider must validate that the user has access to the targeted resource by requesting information about it from the Connector API using an authorization_code granted access token.