> ## Documentation Index > Fetch the complete documentation index at: https://docs.lobstersoftware.com/llms.txt > Use this file to discover all available pages before exploring further. # Create partner > Creates a partner and returns it with its server-assigned `id`. ## OpenAPI ````json POST /platform/api/partners/preview { "openapi": "3.1.0", "info": { "title": "Lobster Platform API", "description": "**PREVIEW:** This API is in preview and under active development. Endpoints and schemas are subject to breaking changes, and backward compatibility is not guaranteed until the API is promoted to a stable version.\n\nREST API of the Lobster Platform for headless clients. Every operation, schema and member of this document carries a description, and the create operations carry complete examples. Each tag links the page of the Lobster documentation that explains its resources.\n\n## Authentication\nFour authentication flows are available. All of them produce a bearer token that must be sent as `Authorization: Bearer ` on every request.\n\n1. **Authorization code with PKCE** (acting as a user; works with Microsoft and other SSO): the steps are in the `OAuth2AuthorizationCode` security scheme.\n2. **OAuth2 Client Credentials** (machine-to-machine): exchange a client id/secret provisioned in the Lobster administration UI at `POST /dw/register/oauth/token` with `grant_type=client_credentials&client_id=&client_secret=`.\n3. **UI Session Bearer Token**: log in via the Lobster web UI, then exchange the session cookie for a token at `GET /auth/sessionBearerToken`.\n4. **Guest User Login Token**: append a GuestUser `loginToken` as a URL query parameter (`?loginToken=`) to establish a session without a password, then retrieve the bearer token the same way.", "version": "preview" }, "servers": [ { "url": "https://platform-beta-test.lobster-cloud.com" } ], "security": [ { "OAuth2": [] }, { "OAuth2AuthorizationCode": [] }, { "SessionBearer": [] } ], "tags": [ { "name": "Channels", "description": "The channels of a partner, the concrete connections a profile uses to receive or send.", "externalDocs": { "description": "Lobster documentation", "url": "https://docs.lobstersoftware.com/docs/en/manage-partners-channels" } }, { "name": "Functions", "description": "The catalogue of mapping functions a profile's mapping can call.", "externalDocs": { "description": "Lobster documentation", "url": "https://docs.lobstersoftware.com/docs/en/functions" } }, { "name": "Partners", "description": "Integration partners, the business parties profiles exchange data with.", "externalDocs": { "description": "Lobster documentation", "url": "https://docs.lobstersoftware.com/docs/en/manage-partners" } }, { "name": "Profiles", "description": "Integration profiles: what data comes in, how it is mapped and where the result goes.", "externalDocs": { "description": "Lobster documentation", "url": "https://docs.lobstersoftware.com/docs/en/profiles" } } ], "externalDocs": { "description": "Lobster documentation", "url": "https://docs.lobstersoftware.com/docs/en/profiles" }, "paths": { "/platform/api/partners/preview": { "post": { "tags": [ "Partners" ], "summary": "Create partner", "description": "Creates a partner and returns it with its server-assigned `id`.", "operationId": "createPartner", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/core.IntegrationPartner", "title": "IntegrationPartner" }, "examples": { "partner": { "summary": "A partner", "description": "partner", "value": { "name": "ACME Logistics", "status": "ACTIVE", "policy": "Global" } } } } } }, "responses": { "201": { "description": "Partner created.", "headers": { "Location": { "description": "URI of the created partner.", "style": "simple", "schema": { "format": "uri" } } }, "content": { "application/json": { "schema": { "type": "object", "description": "Standard Platform API success envelope. Every response carries exactly one of `data` (success) or `error` (failure) at top level.", "properties": { "data": { "$ref": "#/components/schemas/core.IntegrationPartner", "title": "IntegrationPartner" } }, "required": [ "data" ] } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "409": { "$ref": "#/components/responses/Conflict" }, "422": { "$ref": "#/components/responses/UnprocessableEntity" }, "500": { "$ref": "#/components/responses/ServerError" } } } } }, "components": { "schemas": { "core.IntegrationPartner": { "type": "object", "description": "A business party the platform exchanges data with. The partner itself holds only its name, status and rate-limit policy; its connections (logins, addresses, identifiers) are channels, managed under `/platform/api/partners/preview/{partnerId}/channels`, which profiles reference instead of the partner. `{\"name\": \"ACME Logistics\"}` is enough to create one; deleting a partner also deletes all its channels.", "properties": { "class": { "type": "string", "const": "core.IntegrationPartner", "description": "The type of this object, always `core.IntegrationPartner`. Required where the member it is sent in accepts several types." }, "contacts": { "type": "array", "description": "Contacts of the partner (people, not logins). `null` or left out keeps the stored contacts; a list replaces them: contacts not in the list are deleted, entries with a known `id` are updated, entries without one are created. Not carried on the partner embedded in a channel body.", "items": { "$ref": "#/components/schemas/core.IntegrationPartnerContact", "title": "IntegrationPartnerContact" } }, "id": { "type": "integer", "format": "int64", "description": "Numeric id of the partner, assigned by the server on create; it is the `{partnerId}` in the partner's URLs and its channels' URLs. A value sent in a body is ignored.", "readOnly": true }, "isManagedByTpm": { "type": "boolean", "description": "`true` when the partner is managed by the connected Trading Partner Manager (TPM). Such a partner and its channels cannot be deleted through this API. Set only by the TPM synchronisation; a value sent here is ignored.", "readOnly": true }, "name": { "type": "string", "description": "Display name of the partner. Must be unique among all partners that are not deleted.", "examples": [ "ACME Logistics" ], "maxLength": 250, "minLength": 1 }, "policy": { "type": "string", "description": "Name of the rate-limit policy for requests this partner sends to the platform's REST API and MCP endpoints through its HTTP channels; it applies when the HTTP channel itself names no policy. `Global` is built in; another name must reference an existing policy. Left out on create or blank, the partner has no policy; an explicit `null` keeps the stored value.", "examples": [ "Global" ] }, "status": { "$ref": "#/components/schemas/core.PartnerStatus", "default": "ACTIVE", "description": "Whether the partner is in use. An explicit `null` keeps the stored value. `DELETED` cannot be written - delete the partner with DELETE instead.", "title": "PartnerStatus" } }, "required": [ "name" ], "title": "IntegrationPartner" }, "core.IntegrationPartnerContact": { "type": "object", "description": "A contact person (a person to reach, not a login): a partner's contacts are held in the partner's `contacts` member, the contact of a single channel in that channel's `contact` member.", "properties": { "class": { "type": "string", "const": "core.IntegrationPartnerContact", "description": "The type of this object, always `core.IntegrationPartnerContact`. Required where the member it is sent in accepts several types." }, "emailAddresses": { "type": "array", "description": "E-mail addresses of the contact, one entry per address; in the stored form (comma-separated, display names quoted) they must fit 250 characters.", "examples": [ "[\"erika.mustermann@acme.example\"]" ], "items": { "type": "string" } }, "fax": { "type": "string", "description": "Fax number of the contact.", "examples": [ "+49 30 1234568" ], "maxLength": 20 }, "id": { "type": "integer", "format": "int64", "description": "Id of the contact, assigned by the server. Ignored on create. On a partner update an id must be one of this partner's stored contact ids and updates that contact, left out creates a new one; on a channel update it must be the id of the channel's own contact, left out updates that contact." }, "language": { "type": "string", "description": "Language of the contact, as stored by the platform; left out or blank, none is set.", "examples": [ "de" ], "maxLength": 10 }, "name": { "type": "string", "description": "Name of the contact person.", "examples": [ "Erika Mustermann" ], "maxLength": 250 }, "note": { "type": "string", "description": "Free-text note about the contact." }, "phone": { "type": "string", "description": "Phone number of the contact.", "examples": [ "+49 30 1234567" ], "maxLength": 20 } }, "title": "IntegrationPartnerContact" }, "core.PartnerStatus": { "type": "string", "description": "Lifecycle state of a partner (its `status`). It switches all of the partner's channels on or off at once, on top of each channel's own `active` flag.\n\n- `ACTIVE`: The partner is in use; each of its channels works when its own `active` is true. The default.\n- `INACTIVE`: The partner is switched off: the platform treats all its channels as inactive and the channel list shows them with `active: false`, while their stored flags stay unchanged and return with `ACTIVE`.\n- `DELETED`: Soft-deleted by DELETE. Cannot be written, and a read normally never shows a deleted partner.", "enum": [ "ACTIVE", "INACTIVE", "DELETED" ], "title": "PartnerStatus" }, "ErrorResponse": { "type": "object", "description": "Standard Platform API error envelope: every failed request carries a single `error` object. Every response carries exactly one of `data` (success) or `error` (failure) at top level.", "properties": { "error": { "$ref": "#/components/schemas/Error", "description": "What went wrong: branch on its `code`, show its `message`, and read `details` for one entry per failed member." } }, "required": [ "error" ] }, "Error": { "type": "object", "description": "The error that made the request fail.", "properties": { "code": { "type": "string", "description": "Stable machine-readable `UPPER_SNAKE` error code for programmatic handling, e.g. `PARTNER_NOT_FOUND`, `VALIDATION_FAILED` or `FORBIDDEN`. New codes may be added over time; treat an unknown code according to the HTTP status class. The HTTP status itself is carried by the status line only, never duplicated here." }, "message": { "type": "string", "description": "Human-readable, non-localized explanation. Branch on `code`, never on this text." }, "details": { "type": "array", "description": "Individual failures within the request, e.g. one entry per violated field.", "items": { "$ref": "#/components/schemas/ErrorDetail" } } }, "required": [ "code", "message" ] }, "ErrorDetail": { "type": "object", "description": "A single failure within the request.", "properties": { "code": { "type": "string", "description": "Stable machine-readable `UPPER_SNAKE` code of this individual failure, e.g. `FIELD_REQUIRED`, `FIELD_INVALID` or `VALUE_OUT_OF_RANGE`." }, "message": { "type": "string", "description": "Human-readable, non-localized explanation of this individual failure." }, "target": { "type": "string", "description": "What this failure refers to, e.g. the violated field." } }, "required": [ "code", "message" ] } }, "responses": { "BadRequest": { "description": "The request could not be understood. `BAD_REQUEST` for a body that does not decode — its `message` names the property at fault — and `VALIDATION_FAILED` with one `details` entry per offending parameter for a malformed request target (an unknown query parameter or sort key). Also `BAD_REQUEST` for a response unit naming a channel this client cannot reference.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "BAD_REQUEST", "message": "The request is invalid." } } } } }, "Unauthorized": { "description": "Authentication is required or the supplied credentials are invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "UNAUTHORIZED", "message": "Authentication is required to access this resource." } } } } }, "Forbidden": { "description": "The caller lacks the permission for this operation on this resource (`FORBIDDEN`).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "FORBIDDEN", "message": "Access to this resource is denied." } } } } }, "NotFound": { "description": "The requested resource does not exist. The code is resource-specific, e.g. `PARTNER_NOT_FOUND`, `PROFILE_NOT_FOUND` or `CHANNEL_NOT_FOUND`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "PARTNER_NOT_FOUND", "message": "Partner not found: 42" } } } } }, "Conflict": { "description": "The request collides with the current state of the resource: a name already taken by another record, a `lastModified` that no longer matches because the record changed since it was read, or a delete the record's state refuses (it is open in the Lobster UI, still referenced by other objects, or managed by the Transport Manager). A field-level collision carries one `details` entry naming it.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "CONFLICT", "message": "The request conflicts with the current state of the resource.", "details": [ { "code": "FIELD_INVALID", "message": "A partner with the name 'ACME' already exists.", "target": "name" } ] } } } } }, "UnprocessableEntity": { "description": "The body decoded but breaks a rule of the resource: a required member is missing, a value is not one of the accepted values or violates its pattern, length or range, a referenced id (partner, certificate, profile) does not exist, or two members contradict each other. One `details` entry per violation: `FIELD_REQUIRED` or `FIELD_INVALID`, with `target` naming the member path and `message` the rule, including the accepted values where there is a fixed set.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "VALIDATION_FAILED", "message": "Request validation failed", "details": [ { "code": "FIELD_REQUIRED", "message": "Field 'name' must not be empty.", "target": "name" } ] } } } } }, "ServerError": { "description": "An unexpected server error occurred.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": { "code": "INTERNAL_ERROR", "message": "An unexpected error occurred." } } } } } }, "securitySchemes": { "OAuth2": { "type": "oauth2", "description": "OAuth2 client-credentials flow for machine-to-machine access.\nExchange a client id/secret at `/dw/register/oauth/token` for a bearer token, then send it\nas `Authorization: Bearer `. Client credentials are provisioned in the\nLobster administration UI.\n\nThis is the client-credentials token endpoint of the Platform API. `/oauth/token`, the\n`token_endpoint` advertised at `/.well-known/oauth-authorization-server`, serves the authorization\ncode flow of the `OAuth2AuthorizationCode` scheme.", "flows": { "clientCredentials": { "tokenUrl": "/dw/register/oauth/token", "refreshUrl": "/dw/register/oauth/token", "scopes": {} } } }, "OAuth2AuthorizationCode": { "type": "oauth2", "description": "OAuth 2.1 authorization code flow with PKCE, for acting as a signed-in user. It works with Microsoft and other single sign-on providers: the provider only confirms the user's identity to the platform, which then issues its own token.\n\n1. `POST /oauth/register` (no authentication) with the JSON body `{\"redirect_uris\": [\"\"]}` returns a `client_id`. The user's browser has to reach the redirect URI.\n2. Open `/oauth/authorize` in the user's browser with `response_type=code`, `client_id`, `redirect_uri`, `state` and a `code_challenge` with `code_challenge_method=S256`. `state` is a fresh unguessable value per request, and `code_challenge` is the S256 hash of a `code_verifier` freshly generated per request from a cryptographically secure random source (43 to 128 characters, RFC 7636). The user signs in on the platform's login page, and the browser is redirected to the redirect URI with `code` and `state`; a callback whose `state` differs from the one sent is to be rejected.\n3. The code is valid for 30 seconds. Exchange it at `POST /oauth/token`, form-encoded, with `grant_type=authorization_code`, `code`, `redirect_uri`, `code_verifier` and `client_id`.\n4. The answer carries `access_token` (valid 30 minutes), `refresh_token` (valid 48 hours) and `expires_in` **in milliseconds**. Refresh with `grant_type=refresh_token`, `refresh_token` and `client_id`.", "flows": { "authorizationCode": { "authorizationUrl": "/oauth/authorize", "tokenUrl": "/oauth/token", "refreshUrl": "/oauth/token", "scopes": {} } } }, "SessionBearer": { "type": "http", "description": "Bearer token obtained from an authenticated browser or guest-user session.\n\n**Flow 1 — UI login:** Log in via the Lobster web UI at `/`, then call\n`GET /auth/sessionBearerToken` to exchange the session cookie for a bearer token.\n\n**Flow 2 — Guest user login token:** Append a GuestUser `loginToken` as a URL\nquery parameter to the base URL to establish a password-free session:\n```\nGET ?loginToken=\n```\nThen call `GET /auth/sessionBearerToken` to obtain the bearer token.", "scheme": "bearer" } } } } ````