File Drop Service
The complete reference for sending a file to Supply Lens through the File Drop Service, from getting an access token to the request forms, document type values, responses and error codes.
The File Drop Service lets you send a file to Supply Lens. Supply Lens keeps the file as source data for one of your Integrations, then converts it into a Document automatically, using that Integration's Configuration.
There's one endpoint with two request forms. Both use the same URL; the request's Content-Type
header picks the form.
| Form | Content type | Use it when |
|---|---|---|
| JSON form | application/json | The file is a JSON value. |
| Multipart form | multipart/form-data | The file is another format (CSV, XML, EDI, text), or you need to keep the file name. |
What you need
Your Supply Lens contact gives you these.
| Item | What it is |
|---|---|
| Client ID | The identifier of your API client. |
| Client secret | The password of your API client. Keep it secret, and don't put it in source control. |
| Organisation ID | The GUID of your Supply Lens organisation, for example 3fa85f64-5717-4562-b3fc-2c963f66afa6. |
| Integration type ID | The identifier of a type of Integration (not of one Integration), in lowercase, for example despatch-cloud. Supply Lens uses it to find the Integration of that type in your organisation. |
| Document type | The type of Document in the file, for example order. See the document type values below. |
Supply Lens must also:
- Enable the File Drop Service for the Integration type.
- Install one Integration of that type in your organisation.
- Enable that Integration.
- Configure that Integration to convert each document type you send.
Only one enabled Integration of the type
If your organisation has two or more enabled Integrations of the type, the file is rejected. A disabled or uninstalled Integration doesn't count.
Conversion isn't checked up front
The File Drop Service doesn't check step 4. If the Integration can't convert the document type, the file is accepted but the conversion fails.
Addresses
All addresses are for production.
| Service | Address |
|---|---|
| Authentication Service | https://id.supplylens.co.uk |
| File Drop Service | https://filedrop.supplylens.co.uk |
The Authentication Service publishes its configuration at
https://id.supplylens.co.uk/.well-known/openid-configuration.
Step 1: get an access token
The File Drop Service only accepts an access token from the Supply Lens Authentication Service. Get one with the OAuth 2.0 client credentials grant.
Request: POST https://id.supplylens.co.uk/connect/token with the header
Content-Type: application/x-www-form-urlencoded.
| Body value | Required | Value | Effect |
|---|---|---|---|
grant_type | Yes | client_credentials | Selects the client credentials grant. |
client_id | Yes | Your client ID. | Identifies your API client. |
client_secret | Yes | Your client secret. | Proves the request comes from your API client. |
- Don't send
scope. The token automatically gets every scope Supply Lens gave your client, includingfile_drop:write. - URL-encode each value. A secret can contain
+,/,=or&, and the request fails if these aren't encoded. - Read
access_tokenfrom the response.
curl -s https://id.supplylens.co.uk/connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "client_id=<client_id>" \
--data-urlencode "client_secret=<client_secret>"
You can send the client ID and secret with HTTP Basic authentication instead. If you do, leave
client_id and client_secret out of the body.
curl -s https://id.supplylens.co.uk/connect/token \
-u "<client_id>:<client_secret>" \
--data-urlencode "grant_type=client_credentials"
The token response
A correct request returns 200 OK:
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "file_drop:write organisations:read"
}
| Field | What it is |
|---|---|
access_token | The token. Send it in the Authorization header of each File Drop request. |
expires_in | Seconds until the token expires. Currently 3600 (one hour), but use the value in the response rather than assuming 3600. |
token_type | Always Bearer. |
scope | The scopes in the token. |
Reuse the token until it expires; don't get a new one for each file. Get a new token when the
File Drop Service returns 401 Unauthorized, or before expires_in runs out. A wrong client ID
or secret returns 400 Bad Request with "error": "invalid_client".
What the token must contain
The token is a JSON Web Token (JWT). You don't need to read it, but if a request fails, check these claims.
| Claim | Must contain |
|---|---|
aud | integrations |
scope | file_drop:write |
organisationId | Your organisation ID. There's one organisationId claim for each organisation your client can send files to. |
Step 2: send a file
Endpoint: POST https://filedrop.supplylens.co.uk/{integrationTypeId}/{organisationId}
with the header Authorization: Bearer <access_token>.
| Path value | Required | Effect |
|---|---|---|
integrationTypeId | Yes | The Integration type ID. Supply Lens finds the one enabled Integration of this type in the organisation, and that Integration receives and converts the file. Case-sensitive. |
organisationId | Yes | The organisation that receives the file. Must be a GUID, must match one of the organisationId claims in your token, and the organisation must have an active subscription. |
JSON form
Send the file as the request body, with Content-Type: application/json, and the other values
in the query string. The body can be any JSON value (an object, an array or a single value) and
is required: an empty body causes a validation error.
| Query value | Required | Effect |
|---|---|---|
type | Yes | The document type of the file. |
reference | Yes | The reference users see for the file in Supply Lens. Also used to make the file name if you don't send fileName. |
id | No | Your identifier for the file. Kept as the file's external ID and used to find duplicates. |
fileName | No | The file's name in Supply Lens. Without it, the name is <reference>.json. |
Supply Lens stores the JSON with indentation and the content type application/json. The
formatting changes; the data doesn't. URL-encode each query value: a value containing &, #,
+ or a space otherwise changes the query string.
curl -si "https://filedrop.supplylens.co.uk/<integration_type_id>/<organisation_id>?type=order&reference=PO-10001&id=PO-10001" \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
--data-binary @order.json
Multipart form
Send every value as a part of a multipart/form-data body. Let your HTTP client set the
Content-Type header, because it must include the boundary.
| Form part | Required | Effect |
|---|---|---|
file | Yes | The file. The part must have a file name (filename in its Content-Disposition). Supply Lens keeps the file name and this part's content type. |
type | Yes | The document type of the file. |
reference | No | The reference users see. Without it, the file name is used. |
id | No | Your identifier for the file. Kept as the file's external ID and used to find duplicates. |
Send one file per request. Values in the query string are ignored: the multipart form only reads form parts. Set the file part's content type correctly, because Supply Lens keeps it with the file.
curl -si "https://filedrop.supplylens.co.uk/<integration_type_id>/<organisation_id>" \
-H "Authorization: Bearer <access_token>" \
-F "type=order" \
-F "reference=PO-10001" \
-F "id=PO-10001" \
-F "file=@PO-10001.csv;type=text/csv"
How id, reference and the file name are used
| Value | If you send it | If you don't |
|---|---|---|
id | Kept as the file's external ID and used to find duplicates. | No duplicate check. The external ID is the file name. |
reference | Users see it in Supply Lens. | Users see the file name. (The JSON form always requires reference.) |
| File name | The file's name in Supply Lens. In the JSON form it comes from fileName, or else from reference; in the multipart form, from the file part. | Not applicable: the file name is always set. |
Duplicate rules:
- If you send
id, it's compared with the external IDs of files the same Integration received before, for the same document type. - The comparison covers all the Integration's files, including those received another way and
those sent without
id(whose external ID is their file name). - If the same external ID is found, the file is rejected with
400 Bad Requestand isn't kept. - If you don't send
id, there's no duplicate check: the file is kept and converted every time you send it.
Always send a stable id
Send a unique, stable id for each file, such as your order number. Then if you resend after a
timeout or network error, you won't get a second Document.
Size limit
A request can be at most 1 MB (1,048,576 bytes). The limit covers the whole request body; for
the multipart form that includes the boundaries and every part. Larger requests get
413 Request Entity Too Large.
Document type values
Send one of these in type, and only a type Supply Lens has configured your Integration for.
Supply Lens tells you which types your Integration accepts. Values are case-sensitive and
lowercase.
| Value | Document |
|---|---|
order | Sales order |
order-change | Change to a sales order |
order-acknowledgement | Order acknowledgement |
purchase-order | Purchase order |
warehouse-order | Warehouse order |
shipment | Shipment or despatch advice |
warehouse-shipment | Warehouse shipment |
goods-inbound | Advice of goods that will arrive |
goods-inbound-receipt | Receipt of goods that arrived |
proof-of-delivery | Proof of delivery |
invoice | Sales invoice |
credit | Sales credit note |
debit-note | Debit note |
purchase-invoice | Purchase invoice |
purchase-credit | Purchase credit note |
payment-request | Payment request |
payment-confirmation | Payment confirmation |
remittance | Remittance advice |
statement | Statement |
bank-transaction | Bank transaction |
journal | Journal |
quote | Quote |
inventory | Inventory levels |
inventory-report | Inventory report |
stock-adjustment | Stock adjustment |
warehouse-transfer | Warehouse transfer |
pick | Pick |
work-order | Work order |
product-update | Product update |
customer-update | Customer update |
sales-report | Sales report |
functional-acknowledgement | Functional acknowledgement |
custom | Custom file |
Responses
Success
An accepted file returns 201 Created, with a Location header giving the file's address in the
Supply Lens app (https://app.supplylens.co.uk/org/<organisation_id>/data/source/<id>) and this
body:
{
"id": "6710c2f4a1b2c3d4e5f60789"
}
id is the Supply Lens identifier of the source data. Keep it, and give it to Supply Lens
support if there's a problem with the file.
201 Created means Supply Lens has kept the file. It doesn't mean the conversion is complete or
correct: users see the result of the conversion in the Supply Lens app.
Errors
A rejected request returns a problem body (application/problem+json), with the cause in
detail:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"detail": "The integration type does not support file drop"
}
| Status | Detail or cause | What to do |
|---|---|---|
400 | Incorrect integration type "<integrationTypeId>" | The organisation has no enabled, installed Integration of this type, or the value is wrong (it's case-sensitive). Check integrationTypeId, then speak to Supply Lens. |
400 | The integration type does not support file drop | The File Drop Service isn't enabled for this Integration type. Speak to Supply Lens. |
400 | Too many integrations of type "<integrationTypeId>", please contact support | The organisation has two or more enabled Integrations of this type. Speak to Supply Lens. |
400 | The file <id> is a duplicate | The Integration has already received a file with this external ID. Don't send it again. |
400 | There was a problem saving the file, if this continues please contact support | Supply Lens didn't keep the file. Send it again; if the error repeats, speak to Supply Lens. |
400 | A validation problem with an errors object | A required value is missing or invalid. The errors object names it. |
401 | No body | The token is missing, expired or invalid. Get a new token. |
403 | No body | The token lacks the file_drop:write scope, wasn't issued for organisationId, or the organisation has no active subscription. Check the token claims. |
404 | No body | The URL is wrong. Check it has both path values and that organisationId is a GUID. |
413 | An HTML page | The request is over the 1 MB size limit. |
415 | None | The Content-Type header isn't application/json or multipart/form-data. |
500 | Invalid document type <type> | The type value isn't a known document type. It's case-sensitive; use a value from the list above. |
An unknown type returns 500
At the moment an unknown type causes 500, not 400. Don't resend the request unchanged:
correct the type first.
Checklist for a failed request
- You're using the production addresses.
- The
Authorizationheader isBearer <access_token>, with one space. - The token hasn't expired (it's valid for
expires_inseconds). - The token has the
file_drop:writescope and yourorganisationIdclaim. - The
organisationIdin the URL matches the claim in the token. integrationTypeIdis the value Supply Lens gave you, in lowercase.typeis a lowercase value from the document type list.- JSON form:
typeandreferenceare in the query string, and the body is JSON. - Multipart form:
typeandfileare form parts, and thefilepart has a file name. - The request is no larger than 1 MB.
- You haven't sent the same
idbefore.