API Developer Documentation
💡This page is intended as an in-depth overview of the essential endpoints available on the expoze.io API. If you haven't already, we recomment first take a looking at getting started with the expoze.io API to get an idea of the typical user flow.
First up, you can log in to the developer console at expoze.app and get your Postman collection to get up and running fast! The developer console can also be used to manage your account, billing details and invoices.
Authorizing user
Get the auth token
POST /auth/token
Key | Type | Description |
---|---|---|
username | string | The email of the user |
password | string | The user password. |
200 - Response success:
{ "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2MzQ1NTk5NDAsImV4cCI6MTYzNDYyMDQyMCwicm9sZXMiOlsiUk9MRV9VU0VSIiwiUk9MRV9BRE1JTiJdLCJ1c2VybmFtZSI6ImpvaG5AYWxwaGEub25lIiwidXNlcklkIjoxLCJvcmdhbml6YXRpb24iOjEsInN1YnNjcmlwdGlvbiI6eyJwcm8iOnRydWUsImNvcmVwbHVzIjpmYWxzZSwiY29yZSI6ZmFsc2V9fQ.0wGb8ih8l6Ph1DWjMpPLtk1W5Q6a2w0KxawrTzVTdhX9OZw7e7CpILFoph75ikGX6j1ZwIZHEmMUOein5n2cLcKEUXwWH-b3l97Qrgm-K00Bupyv2J-uZLjM--3XCKMgI8qvgxzU_R9iYNsYIjz06eEs6QdRUgWnt49KSQ1_qoyvbvITI3UY51uY_TzxaUxcWbF7KJylW_ilLTCuehFrNIT_V0K6pHQv-b_mlbau0mXCoef0sgDmUye1Z0cLLUvsMf3XpQiXy9b7DEzKK1jqkg928miO__6zEQkgcAPeH-pCZwpgaT3XqMQD-20aQWPkFSBp4e7uuVTQKHYNQCA8Uw", "refresh_token": "32506d6669a4b229b04292e1186a7252648a67bb35982de9c3d44613629028103a1acda9a92cbb4414acbf8aaf0391d9c2dd4ba5418e91388e38ef28fd06cf58" }
401 - Unauthorized
{ "code": 401, "message": "Invalid credentials." }
Get the refresh token
POST /auth/token/refresh
Key | Type | Description |
---|---|---|
username | string | The email of the user |
password | string | The user password. |
200 - Unauthorised
{ "code": 401, "message": "An authentication exception occurred." }
401 - Response success:
{ "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2ODI0MTc0NDIsImV4cCI6MTY4MjQ3NzkyMiwicm9sZXMiOlsiUk9MRV9VU0VSIiwiUk9MRV9BRE1JTiIsIlJPTEVfQVBJX1VTRVIiXSwidXNlcm5hbWUiOiJkYWFuK2FwaUBleHBvemUuaW8iLCJ1c2VySWQiOjQ1OTA5LCJvcmdhbml6YXRpb24iOjQ0NjI3LCJzdWJzY3JpcHRpb24iOnsicHJvIjpmYWxzZSwiY29yZXBsdXMiOmZhbHNlLCJjb3JlIjpmYWxzZX19.tVodc5sBgvIJrZ_ogtHG6WNxYqHDQHJobFCoa1qWa4fEQukhIkgZ3cDhI_HBYfTyfwGEMUck2AoucZaUgE5VLLGJufoQpvcqvq8GPhL3D6kVz7FliXYvl4XeTvZIfvrtELpPdFD8A5RLdntAn_Hq4iRAg-N7Cixk56D7N-_ap_Wly_IVfFX06Mv5QYMNHbtHg5eEMrbm15oMBZXNJTJOwU_1xL5jTLVDLJeU_lt_2-igg6T-hk650Wxhn6604x-jMtdoYkNJI9EhfoJHJK3Rjep_outWIEYMrKxVyHds27U_0riYkExFfMXuIMa8EqlHE-kn1xB13XrPBNOrCHyJa1YuxjEWCNY6_rEKbd1vkJuC1sSsBmUMA_DfJ4OM_W1cdtVdCpLsQNZYMVpHr9sK3eLS9gbSKn_301GAGlk2wKs1WsVCMkN9MvLzf9jfmRRRXEkv1gGXoHvh2L0RbA2a6nuEDCRx0kHJSsQeBlyPjUWb6gIrlefbLqCsJ-MZKnA-ymCbIc3YdgJWdWEi9To8F7iyBGN2eZ6E4qNQscY9DiBiRcrLfehSYx83HFAn8vUzFrX2lvWYuG-_qGynkK4OYADAgFe2-HKOaQntDFlh3DWJxlOXgGA3EEK_ETnevBRp_ePTwn2TDpn0xbLP2pAzC-58UAfFZceJ0prfy3O5zu8", "refresh_token": "c36d31774b1eddfa210a60bac31d5e0cebcdaab02cde4ff855e83661e6868c5e5de6f761ecd17f454ccbb1771144738dc72d73f2c1cd50c6de0df492030269b5" }
Get the current user
POST /auth/me
200 - Response success:
{ "firstName": "John", "lastName": "Doe", "company": "Company Name", "username": "johndoe@company.com", "roles": [ "ROLE_USER", "ROLE_ADMIN" ], "organization": { "uuid": "037faedf-eade-4f25-8def-e3f30346758e", "stripePlanId": "plan_GTmggbYeNnxrSw", "id": 1 }, "organizationId": 1, "status": "active", "avatar": null, "lastLogin": "2021-10-18T12:31:24+00:00", "department": null, "jobTitle": null, "id": 1 }
Workspaces
💡 Previously, it was required to create a workspace before you could create any jobs. However, this is no longer required.
Create a new workspace
POST /spaces
Body:
{ "organization" : <organization_id>, "name": "Figma plugin" "description" : "<optional>" }
Response:
{ "organization": { "id": 1 }, "name": "Test 2", "description": null, "cover": null, "coverGrid": [], "mediaobject": [], "parent": null, "parentId": null, "children": [], "id": 7 }
List spaces
GET /spaces.json
Images have a specific prefix to reference a specific version (see using image paths).
Response:
[ { "organization": { "id": 1 }, "name": "Figma", "description": "Images processed from your Figma.", "cover": "1\\/1\\/25\\/jobs\\/eab3a441-fb15-4df7-a4b1-e7a47dd4dce1.jpg", "coverGrid": [ "1\\/1\\/14bd2ff8-1cfc-4e6f-a24e-bd6a3fd03a2a.jpeg", "1\\/1\\/23\\/jobs\\/ba374b17-f5b9-4da4-bac6-b455446b65fd.jpg", "1\\/1\\/29\\/jobs\\/bbd2d436-b841-419b-8c90-c5c0ce38e91d.jpg", "1\\/1\\/4d179860-758d-4e8e-bb72-356c601fcd88.jpeg" ], "mediaobject": [ { "id": 27 }, { "id": 23 } ], "parent": null, "parentId": null, "children": [], "id": 1 }, ... ]
Uploading a new image
For uploading an image a multipart form needs to be posted to an endpoint from that specific space.
Using multipart form blob
If using workspace - POST /spaces/{spaceId}/upload
If not using workspace - POST /developer/upload
Custom headers:
Name | Value |
---|---|
Content-Type | multipart/form-data |
Form parameters:
Name | Value |
---|---|
file | <blob>https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects |
200 - Response success:
{ "message": "Upload accepted.", "mediaId": 34018 }
Upload image using base_64
POST /developer/upload
Custom headers:
Name | Value |
---|---|
Content-Type | text/plain |
Form parameters:
Name | Value |
---|---|
file | Base64ImageCode |
200 - Response success:
{ "message": "Upload accepted.", "mediaId": 34018 }
Add a new job
To create a new heatmap prediction, you need to create a new job. The data you provide in the body of the request will define what kind of heatmap output you will receive, for additional details take a look at the heatmap presets article or custom heatmaps article
POST /jobs.json
Images have a specific prefix to reference a specific version (see using image paths).
for the settings of the job please see Heatmap presets for the examples.
body:
{ "media": "<media_id>", "colormap": "jet", "alpha": 0.85, "enableWatermark": false, "boost": 2, "inverse": false, "reveal": false, "preset": "normal" }
200 - response:
{ "position": 1, "status": "queued", "media": { "id": 1337 }, "colormap": "jet", "spyneId": null, "mediaId": 2364, "priority": 1, "alpha": 1, "modulus": 3, "interval": 1, "progress": 0, "boost": 2, "description": null, "outputFile": null, "outputPath": null, "outputPdfFile": null, "reveal": false, "inverse": false, "watermark": [], "expectedOutputUri": null, "poster": null, "spyneTask": null, "preset": "normal", "enableWatermark": null, "progressStatus": { "progress": null, "frames_done": null, "total_frames": null, "time_elapsed": null, "time_remaining": null }, "salience": { "mode": null, "sum": null, "min": null, "max": null, "mean": null, "median": null, "stddev": null, "variance": null }, "properties": { "area": null, "derotation": null, "dim_height": null, "dim_width": null, "suffix_out": null }, "id": 10025 }
422 - response:
{ "type": "https:\\/\\/tools.ietf.org\\/html\\/rfc2616#section-10", "title": "An error occurred", "detail": "tokens: There are not enough tokens left in your account to add this job.", "violations": [ { "propertyPath": "tokens", "message": "There are not enough tokens left in your account to add this job.", "code": null } ] }
Using image paths
The images can be prefixed with the following url prefix:
<https://api.expoze.app/core/media/cache/resolve/><preset>/userfiles/
The <preset> placeholder refers to specific sizes of the available image.
Preset | Size | Example |
---|---|---|
thumb_small | 300 x 200 | Link |
thumb_medium | 900 x 600 | Link |
thumb_large | 1800 x 1200 | Link |
media_max | 2048 x 1536 | Link |
original | source | Link |
Using Webhooks
If you do not want to create a job to download the final output, you can use webhook - the system will automatically send the result to the webhook when it is ready.
First, you will need to ensure to have a unique URL from https://webhook.site/
Then, get your org ID from /organizations.json, then go to "Set up webhook" endpoint and update the request URL with the organization ID you got from /organizations.json. Send your request and after that, every time you upload your job and job is ready - the system will send json with a response to your webhook.
Creating and editing AOIs
Important to note: You cannot create an AOI without first creating a heatmap! See creating a job section for more info on how to create a heatmap.
AOIs are an important part of getting value out of expoze.io. They allow users to quantify a proportion of attention that is given to any specific area in an image, for example a brand or product. See the below screenshot for an example of how this is visualised in the expoze.io web app:
To create rectangle, circle, and triangle AOIs - POST /a_o_is.json
body:
{ "type": "rectangle", "media": 108, "mediaId": 108, "media_id": 108 }
200 - response
{ "id": 108, "position": 3, "type": "triangle", "media": "/images/50", "mediaId": 50, "points": [], "name": null, "score": null, "visible": true, "colors": [] }
An area of interest always needs coordinated within the image to quantify the amount of attention on the given area. You can edit your created AOI to adjust the point and name using the varies edit endpoints:
PUT /a_o_is.json/{aoi-id}
{ "$id": "21473", "id": 21473, "type": "rectangle", "loading": false, "editing": false, "name": "test", "editName": false, "visible": true, "selected": true, "media": "/images/34266", "mediaId": 34266, "position": 0, "points": [ { "type": "rect", "version": "3.6.3", "originX": "left", "originY": "top", "left": 12.55, "top": 55.55, "width": 120, "height": 100, "fill": "rgba(18, 109, 88, 0.40)", "stroke": "#005094", "strokeWidth": 1, "strokeDashArray": null, "strokeLineCap": "butt", "strokeDashOffset": 0, "strokeLineJoin": "miter", "strokeMiterLimit": 4, "scaleX": 4.03, "scaleY": 4.03, "angle": 0, "flipX": false, "flipY": false, "opacity": 1, "shadow": null, "visible": true, "clipTo": null, "backgroundColor": "", "fillRule": "nonzero", "paintFirst": "fill", "globalCompositeOperation": "source-over", "transformMatrix": null, "skewX": 0, "skewY": 0, "rx": 0, "ry": 0 } ] }
200 - Response success
{ "id": 21473, "position": 0, "type": "rectangle", "media": "/images/34266", "mediaId": 34266, "points": [ { "type": "rect", "version": "3.6.3", "originX": "left", "originY": "top", "left": 12.55, "top": 55.55, "width": 120, "height": 100, "fill": "rgba(18, 109, 88, 0.40)", "stroke": "#005094", "strokeWidth": 1, "strokeDashArray": null, "strokeLineCap": "butt", "strokeDashOffset": 0, "strokeLineJoin": "miter", "strokeMiterLimit": 4, "scaleX": 4.03, "scaleY": 4.03, "angle": 0, "flipX": false, "flipY": false, "opacity": 1, "shadow": null, "visible": true, "clipTo": null, "backgroundColor": "", "fillRule": "nonzero", "paintFirst": "fill", "globalCompositeOperation": "source-over", "transformMatrix": null, "skewX": 0, "skewY": 0, "rx": 0, "ry": 0 } ], "name": "test", "score": 5.1, "visible": true, "colors": [] }