API Extensions
Partners may provide an OpenAPI 3 specificication that defines a set of API endpoints they wish to make available inside a platform.
An extension field x-marketplace-integration-handler is required at the top
level, paths, or operations of the OpenAPI 3 specification. This field should
point to a JavaScript module that exports a handler function. This handler will
receive a partner integratoin object as its first argument and a request object
as its second. The handler should resolve with a response object.
See src/api/typescript/
for the interfaces that define the partner integration object and the request
and response objects.
Note: this specification may support additional languages in the future.
Manifest Example
The api field in the manifest can be used to specify an OpenAPI v3 document.
Generally that's too large to embed directly into a JSON blob, so put it in an
openapi.yaml file along with the API extension handlers in a separate directory
and include that in your manifest:
{
"api": "file:api/openapi.yaml"
}
That file itself would look something like this:
openapi: 3.1.0
info:
title: Exampe
version: v1
# catchall handler, will used if no handler is defined for a path
x-partner-marketplace-handler: api/catchall.js
paths:
# this route uses the handler defined in the `info` section above
/catchall/{example}:
get:
description: yell a greeting at a user
operationId:
parameters:
- in: path
name: example
schema:
type: string
required: true
responses:
"200":
content:
application/json:
schema:
type: object
properties:
greeting:
type: string
description: OK
/greeting:
# handler for this path as a whole, this will be invoked for _all_
# accepted methods on this operation
x-partner-marketplace-handler: api/greeting.js
get:
description: send a greeting to the userk
operationId: greeing
parameters:
- in: query
name: name
schema:
type: string
required: true
description: the name of the user to greet
responses:
"200":
content:
application/json:
schema:
type: object
properties:
greeting:
type: string
description: OK
/yell:
post:
# handler for a specific method on an operation, this would
# only be invoked with a `POST` request is made to `/yell`
x-partner-marketplace-handler: api/yell.js
description: yell a greeting at a user
operationId: yell
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
responses:
"200":
content:
application/json:
schema:
type: object
properties:
greeting:
type: string
description: OK
Handler Examples
The x-partner-marketplace-handler field should point to a JavaScript file that
exports a single function that looks something like this:
export default async function ({ usage, tenant, platform }, request) {
await usage.track('skuhere', 123);
console.log(platform.id); // the identifier of the platform, `alli` in this case
// if secrets were set up in the manifest, they'll be at platform.secrets
// as an object with secret name as the key.
console.log(platform.secrets);
console.log(requst.method)
console.log(request.url);
console.log(request.body);
console.log(request.parameters);
return {
status: 200,
body: {
greeting: `Hello, ${request.parameters.name}!`
}
headers: {
'Content-Type': ['application/json'],
}
};
}
Platform Object
The first argument passed to the handler is a platform object with tenant,
usage, and secrets properties.
Tenant
This is the tenant with the marketplace platform (eg an Alli Client).
Tenants have a unique id (identifier) property that identifies them with Alli,
as well as an extension_version that identifies the app version the tenant has
installed, and configuration which is the user supplied configuration defined
in the app's manifest. Configuration will be a JSON object with the keys
corresponding to the configuration fields defined in the manifest.
Usage
This is an object with a single method track. Usage tracked here will be
recorded by alli marketplace for consumption based billing.
The SKU name here should be from the app's manifest.
usage.track('skuNameFromManifestHere', 123);
Secrets
These are the secrets configured in the manifest. They'll be in secretName: value
pairs. Be sure to coordinate with your Alli rep to get the real secret values
to the Alli team!
Platform
This is extra metadata about Alli itself. Generally this will have an id field
with the value alli and that's it.
Requests and Responses
Requests have method, url, and body properties. If the content type is
JSON Alli will parse the body for you.
Additionally any parameters defined in the OpenAPI spec will be parsed out and
made available at request.parameters. This is a list of object, each with
in, name, and value properties. in will be one of path, query, or
header.
Responses should include a status, body, and headers. If body is
anything other than a string, it will be JSON encoded.
Alli Does Not Validate Request Bodies for Apps
Defining the schema in the OpenAPI document is recommended, but Alli will not validate that schema for an app.
Building Apps with API Extensions
Alli will not do any additional build processes for an app when it provisions
API endpoints. This means that the app should do any build processes before
pushing a version to its OCI repository. Do not send us typescript files, only
plain JavaScript files that can be run on Node v22. Include a node_modules
directory as well if applicable.
Bundling the API code and configuration into the OCI repository can be done via oras.
Assuming a file system layout like this:
manifest.json
api/
openapi.yaml
handler.js
oras push https://example.com/your-repository \
--config manifest.json:application/vnd.alli-marketplace.manifest.v1+json \
./api/:application/vnd.alli-marketplace.api+tar