API -> users / bin

Hello All,

I was looking into the API. Is there a way to create / edit / delete users with the API.
And is there a way to add BIN locations?

Thanks!

At the moment, the only way to create users would be through the Generic API. But you would need to do some experimenting to see whether it would work for you. For example, I don’t know if/how you’d be able to deal with creating user roles since we don’t have an endpoint for that.

More generally, we are improving our approach to API design and development. This effort will eventually lead to a project in which we refactor our existing APIs (i.e. stock movements), as well as add APIs for some of the glaringly obvious holes (i.e. user management, basic transactions).

In addition, the User Management API will likely get a separate refactoring once we move to a more modern security framework like (Spring Security, Shiro, or another role-based framework).

Other related tickets

I decided to do the “experimenting” instead of delegating that to you.

Aside: I’m currently documenting the upgrade / migration process for 0.9.x and I have started to add some improvements to the API docs. If you decide to explore adding users and roles through the Generic API (Generic API - OpenBoxes), let me know of any issues you encounter and I’ll update the docs.

Here’s a general overview of how it could work.

Generic User API

You should be able to perform most operations on the User resource.

Method URL Action
GET /openboxes/api/generic/user Gets a list of user resources
GET /openboxes/api/generic/user/:id Get a single user resource
POST /openboxes/api/generic/user Create one or more user resources
POST /openboxes/api/generic/user/:id Update a single user resource
PUT /openboxes/api/generic/user/:id Update a single user resource
DELETE /openboxes/api/generic/user/:id Delete a single user resource

General Workflow

Login in as API User

Request

curl -i -c cookies.txt \
-X POST \
-H "Content-Type: application/json" \
-d '{"username":"jmiranda","password":"password","location":"1"}' \
https://openboxes.ngrok.io/openboxes/api/login

Response

HTTP/2 200 
content-type: text/html;charset=utf-8
date: Tue, 30 Apr 2024 19:29:47 GMT
set-cookie: JSESSIONID=5BF5BA395499C9D6E6AC978F63610744; Path=/openboxes; HttpOnly
x-application-context: application:development

Authentication was successful

Create a User

Request

curl -b cookies.txt -i \
-X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"firstName":"New", "lastName":"User","username":"newuser", "email":"newuser@openboxes.com", "password":"password"}' \
https://openboxes.ngrok.io/openboxes/api/generic/user

Response

{
  "data": {
    "id": "ff8081818f304ab7018f306709680002",
    "name": "New User",
    "firstName": "New",
    "lastName": "User",
    "email": "newuser@openboxes.com",
    "username": "newuser",
    "roles": null
  }
}

Get a User

Request

curl -b cookies.txt -i \
-X GET \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
https://openboxes.ngrok.io/openboxes/api/generic/user/ff8081818f304ab7018f306709680002

Response

{
  "data": {
    "id": "ff8081818f304ab7018f306709680002",
    "name": "New User",
    "firstName": "New",
    "lastName": "User",
    "email": "newuser@openboxes.com",
    "username": "newuser",
    "roles": null
  }
}

Change Password

Request

curl -b cookies.txt -i \
-X PUT \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"password":"s3cur3-p@55w0rd"}' \
https://openboxes.ngrok.io/openboxes/api/generic/user/ff8081818f304ab7018f306709680002

Response

{
  "data": {
    "id": "ff8081818f304ab7018f306709680002",
    "name": "New User",
    "firstName": "New",
    "lastName": "User",
    "email": "newuser@openboxes.com",
    "username": "newuser",
    "roles": null
  }
}

Get Roles

Request

curl -b cookies.txt -i \
-X GET \
-H "Accept: application/json" \
https://openboxes.ngrok.io/openboxes/api/generic/role

Response

HTTP/2 200 
content-type: application/json;charset=UTF-8
date: Tue, 30 Apr 2024 19:08:09 GMT
x-application-context: application:development

{
  "data": [
    {
      "id": "09f5e1167751da090177634f04a42b66",
      "name": "Purchase approver",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_PURCHASE_APPROVER"
      },
      "description": null
    },
    {
      "id": "1",
      "name": "Admin",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_ADMIN"
      },
      "description": null
    },
    {
      "id": "2",
      "name": "Manager",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_MANAGER"
      },
      "description": null
    },
    {
      "id": "3",
      "name": "Browser",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_BROWSER"
      },
      "description": null
    },
    {
      "id": "4",
      "name": "Assistant",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_ASSISTANT"
      },
      "description": null
    },
    {
      "id": "5",
      "name": "Superuser",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_SUPERUSER"
      },
      "description": null
    },
    {
      "id": "ff8080818662c5070186a5b964fe7476",
      "name": "Requestor",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_REQUESTOR"
      },
      "description": "Allows users to request stock from depot locations"
    },
    {
      "id": "ff80818162d9f9cc0162d9fbbeb40001",
      "name": "User Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_USER_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about users."
    },
    {
      "id": "ff80818162d9f9cc0162d9fbf09e0002",
      "name": "Product Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_PRODUCT_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about products."
    },
    {
      "id": "ff80818162d9f9cc0162d9fc1c220003",
      "name": "All Shipment Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_SHIPMENT_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about shipments."
    },
    {
      "id": "ff80818162d9f9cc0162d9fc48990004",
      "name": "Error Notification",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_ERROR_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about errors."
    },
    {
      "id": "ff80818162d9f9cc0162d9fc74fa0005",
      "name": "Feedback Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_FEEDBACK_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about feedback."
    },
    {
      "id": "ff80818162debf330162dec0548e0001",
      "name": "Order Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_ORDER_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about orders."
    },
    {
      "id": "ff808181681c757c01681c89c4960001",
      "name": "Finance",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_FINANCE"
      },
      "description": "Allow user to view and modify financial information"
    },
    {
      "id": "ff808181681c757c01681c89c4960002",
      "name": "Invoice",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_INVOICE"
      },
      "description": "Allow user to view and modify invoice information"
    },
    {
      "id": "ff8081816cdefc9d016cdf09a9ee0001",
      "name": "All Stock Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_ITEM_ALL_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about orders."
    },
    {
      "id": "ff8081816cfeeaf0016cff174ca70001",
      "name": "Expiry Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_ITEM_EXPIRY_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about expiring/expired items."
    },
    {
      "id": "ff8081816cfeeaf0016cff18057e0002",
      "name": "Overstock Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_ITEM_OVERSTOCK_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about overstocked items."
    },
    {
      "id": "ff8081816cfeeaf0016cff1834080003",
      "name": "Reorder Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_ITEM_REORDER_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about reorder items."
    },
    {
      "id": "ff8081816cfeeaf0016cff18617d0004",
      "name": "Low Stock Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_ITEM_LOW_STOCK_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about low stock items."
    },
    {
      "id": "ff8081816cfeeaf0016cff1b6e780005",
      "name": "Out of Stock Notifications",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_ITEM_OUT_OF_STOCK_NOTIFICATION"
      },
      "description": "Role that represents users who should receive notifications about stockouts."
    },
    {
      "id": "ROLE_PRODUCT_MANAGER",
      "name": "Product Manager",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_PRODUCT_MANAGER"
      },
      "description": "Role that represents users who have the permission to manage products"
    },
    {
      "id": "ROLE_REQUISITION_APPROVER",
      "name": "Request approver",
      "roleType": {
        "enumType": "org.pih.warehouse.core.RoleType",
        "name": "ROLE_REQUISITION_APPROVER"
      },
      "description": null
    }
  ]
}

Create a User Role

Unfortunately, this is where things take a turn. We currently don’t have a domain to represent the User Role database table, so there’s no generic endpoint that would allow us to assign roles to users. Therefore, we need to get creative.

The first option would be to try to assign the role via the User’s roles association. However, that does not seem to work.

Request

curl -b cookies.txt -i \
-X PUT \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"roles": [{"user.id":"ff8081818f304ab7018f306709680002", "role.id":"5"}]}' \
https://openboxes.ngrok.io/openboxes/api/generic/user/ff8081818f304ab7018f306709680002

Response

Caused by: java.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`obgm`.`user_role`, CONSTRAINT `FK143BF46AFF37FDB5` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`))
        at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117)
        at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
        at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
        at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953)
        at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1092)
        at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1040)
        at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1347)
        at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1025)
        at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175)
        ... 34 common frames omitted

My second idea would be to create a Location Role for each separate location the user should have access to. Thankfully, we have a generic endpoint for that, but

Request

curl -b cookies.txt -i \
-X PUT \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"user.id":"ff8081818f304ab7018f306709680002", "location.id":"1", "role.id":"5"}' \
https://openboxes.ngrok.io/openboxes/api/generic/locationRole

Response

curl -b cookies.txt -i -X POST -H "Accept: application/json" -H "Content-Type: application/json" -d '{"user":{"id":"ff8081818f304ab7018f306709680002"}, "location":{"id":"1"}, "role":{"id":"5"}}' https://openboxes.ngrok.io/openboxes/api/generic/locationRole
HTTP/2 201 
content-type: application/json;charset=UTF-8
date: Tue, 30 Apr 2024 19:27:14 GMT
x-application-context: application:development

{
  "data": {
    "id": "ff8081818f304ab7018f3079b6600008",
    "role": {
      "id": "5"
    },
    "location": {
      "id": "1"
    },
    "user": {
      "id": "ff8081818f304ab7018f306709680002"
    }
  }
}

Get User Details (including roles)

Unfortunately, the response does not include default roles and location-based roles, so we’ll need to check the UI to see if the location role was created.

Request

curl -b cookies.txt -i \
-X GET \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
https://openboxes.ngrok.io/openboxes/api/generic/user/ff8081818f304ab7018f306709680002

Response

HTTP/2 200 
content-type: application/json;charset=UTF-8
date: Tue, 30 Apr 2024 19:31:23 GMT
x-application-context: application:development

{
  "data": {
    "id": "ff8081818f304ab7018f306709680002",
    "name": "New User",
    "firstName": "New",
    "lastName": "User",
    "email": "newuser@openboxes.com",
    "username": "newuser",
    "roles": []
  }
}

Validate Location Role

Check the user interface to see if location-based role was added for the user. :white_check_mark:

Delete User

This worked for me (before I added a location role), but probably won’t work now that I have foreign key relationships in the location role table.

Request

curl -b cookies.txt -i \
-X DELETE \
https://openboxes.ngrok.io/openboxes/api/generic/user/ff8081818f304ab7018f306709680002

Response

HTTP/2 204 
date: Tue, 30 Apr 2024 19:05:35 GMT
x-application-context: application:development

Error Handling

General note about error handling. If the server error returns an error, it should be in the following format, including HTTP status code (40x, 50x) and an error response body.

HTTP/2 400 
content-type: application/json;charset=UTF-8
date: Tue, 30 Apr 2024 19:24:08 GMT
x-application-context: application:development
{
  "errorCode": 400,
  "errorMessage": "...",
  "errorMessages": [
    "Property [role] of class [class org.pih.warehouse.core.LocationRole] cannot be null",
    "Property [location] of class [class org.pih.warehouse.core.LocationRole] cannot be null",
    "Property [user] of class [class org.pih.warehouse.core.LocationRole] cannot be null"
  ]
}

1 Like

Creating the bin locations can be achieved in one of the following ways. I’m not sure which of these works so I’m just going to try the first one and call it a day.

  • POST /openboxes/api/generic/location
  • POST /openboxes/api/internalLocations
  • PUT /openboxes/api/location/:id (include in the parent location’s locations association)

Get Location Types

Request

curl -b cookies.txt -i -X GET -H "Accept: application/json" -H "Content-Type: application/json" https://openboxes.ngrok.io/openboxes/api/generic/locationType

Response

HTTP/2 200 
content-type: application/json;charset=UTF-8
date: Tue, 30 Apr 2024 19:46:17 GMT
x-application-context: application:development

{
  "data": [    {
      "id": "2",
      "name": "Depot",
      "description": "Depot",
      "locationTypeCode": "DEPOT"
    },
    {
      "id": "3",
      "name": "Dispensary",
      "description": "Dispensary",
      "locationTypeCode": "DISPENSARY"
    },
    {
      "id": "4",
      "name": "Supplier",
      "description": "Supplier",
      "locationTypeCode": "SUPPLIER"
    },
    {
      "id": "5",
      "name": "Ward",
      "description": "Ward",
      "locationTypeCode": "WARD"
    },
    {
      "id": "6",
      "name": "Pharmacy",
      "description": "Pharmacy",
      "locationTypeCode": "DISPENSARY"
    },
    {
      "id": "cab2b48e649c71940164a13750f40001",
      "name": "Cross-docking",
      "description": "Default cross-docking location type",
      "locationTypeCode": "INTERNAL"
    },
    {
      "id": "cab2b4f35ba2d867015ba2e17e390001",
      "name": "Bin Location",
      "description": "Default bin location type",
      "locationTypeCode": "BIN_LOCATION"
    },
    {
      "id": "ff8081816482352b01648249e8cc0001",
      "name": "Receiving",
      "description": "Default receiving location type",
      "locationTypeCode": "INTERNAL"
    },
    {
      "id": "HOLD_LOCATION",
      "name": "Hold",
      "description": "Default hold location type",
      "locationTypeCode": "INTERNAL"
    },
    {
      "id": "ZONE",
      "name": "Zone",
      "description": "Default zone location type",
      "locationTypeCode": "ZONE"
    }
  ]
}

Bulk create internal locations

Request

As mentioned previously, the Generic API allows you to create multiple objects at once.
To demonstrate this, I’m going to post data sourced from a file.

curl -b cookies.txt -i -X POST \
-H "Accept: application/json" -H "Content-Type: application/json" \
-d @internal-locations.json \
https://openboxes.ngrok.io/openboxes/api/generic/location

internal-locations.json

[
{"name": "New Bin 1","locationNumber":"NEWBIN1","locationType":{"id":"cab2b4f35ba2d867015ba2e17e390001"}, "parentLocation":{"id":"1"}},
{"name": "New Bin 2","locationNumber":"NEWBIN2","locationType":{"id":"cab2b4f35ba2d867015ba2e17e390001"}, "parentLocation":{"id":"1"}},
]

Response

The JSON response for the Generic Location API is a little verbose due to the parent-child relationship between locations. In addition, the Bulk Create endpoint doesn’t return the correct HTTP status code, indicating that multiple objects were created. So while it’s fine to use for one-off bulk creation, I would be careful if you need to perform error handling and other basic user interfactions within an application.

HTTP/2 201 
content-type: application/json;charset=UTF-8
date: Tue, 30 Apr 2024 20:05:44 GMT
x-application-context: application:development

{
  "data": [
    {
      "id": "ff8081818f304ab7018f309cf85b0009",
      "name": "New Bin 1",
      "description": null,
      "locationNumber": "NEWBIN1",
      "locationGroup": null,
      "parentLocation": {
        "id": "1",
        "name": "Boston: Haiti Stock",
        "description": null,
        "locationNumber": "BOS:Haiti",
        "locationGroup": {
          "id": "8a8a9e9666194c8901661d2e06340167",
          "name": "Boston Office"
        },
        "parentLocation": null,
        "locationType": {
          "id": "2",
          "name": "Depot|fr:D",
          "description": "Depot",
          "locationTypeCode": "DEPOT"
        },
        "sortOrder": 0,
        "hasBinLocationSupport": true,
        "hasPackingSupport": true,
        "hasPartialReceivingSupport": false,
        "hasCentralPurchasingEnabled": false,
        "organizationName": "PIH Boston",
        "organizationCode": "INT",
        "backgroundColor": null,
        "zoneName": null,
        "zoneId": null,
        "active": true,
        "organization": {
          "id": "ff808081727fd8ce0172a4aacad560bb",
          "name": "PIH Boston",
          "description": "Organization encompassing all PIH USA locations, and any buying activities out of the Boston office.",
          "code": "INT",
          "dateCreated": "2020-06-11T18:35:56Z",
          "lastUpdated": "2023-04-06T18:02:45Z",
          "defaultLocation": null,
          "partyType": {
            "id": "ORG",
            "name": "Organization",
            "code": "ORG",
            "partyTypeCode": "ORGANIZATION"
          },
          "roles": [
            {
              "id": "ff808081727fd8ce0172a4abfd1960bc",
              "roleType": "ROLE_BUYER",
              "startDate": "2020-06-11T18:36:00Z",
              "endDate": "2020-06-11T18:36:00Z"
            }
          ],
          "sequences": {
            "PURCHASE_ORDER_NUMBER": "9712"
          }
        },
        "manager": {
          "id": "2",
          "name": "Mister Miranda",
          "firstName": "Mister",
          "lastName": "Miranda",
          "email": "manager@pih.org",
          "username": "manager",
          "roles": [
            "ROLE_FINANCE",
            "ROLE_MANAGER"
          ]
        },
        "address": {
          "address": "Partners In Health",
          "address2": "Attn: Supply Chain Haiti",
          "city": "800 Boylston Street, Suite 300",
          "country": "",
          "description": null,
          "postalCode": "02199",
          "stateOrProvince": "Boston, MA"
        },
        "supportedActivities": [
          "PACK_SHIPMENT",
          "ENABLE_REQUESTOR_APPROVAL_NOTIFICATIONS",
          "PLACE_REQUEST",
          "EXTERNAL",
          "SEND_STOCK",
          "ENABLE_FULFILLER_APPROVAL_NOTIFICATIONS",
          "RECEIVE_STOCK",
          "PICK_STOCK",
          "PUTAWAY_STOCK",
          "FULFILL_REQUEST",
          "REQUIRE_ACCOUNTING",
          "MANAGE_INVENTORY"
        ]
      },
      "locationType": {
        "id": "cab2b4f35ba2d867015ba2e17e390001",
        "name": "Bin Location",
        "description": "Default bin location type",
        "locationTypeCode": "BIN_LOCATION"
      },
      "sortOrder": null,
      "hasBinLocationSupport": true,
      "hasPackingSupport": false,
      "hasPartialReceivingSupport": false,
      "hasCentralPurchasingEnabled": false,
      "organizationName": null,
      "organizationCode": null,
      "backgroundColor": "FFFFFF",
      "zoneName": null,
      "zoneId": null,
      "active": true,
      "organization": null,
      "manager": null,
      "address": null,
      "supportedActivities": [
        "PICK_STOCK",
        "PUTAWAY_STOCK"
      ]
    },
    {
      "id": "ff8081818f304ab7018f309cf871000a",
      "name": "New Bin 2",
      "description": null,
      "locationNumber": "NEWBIN2",
      "locationGroup": null,
      "parentLocation": {
        "id": "1",
        "name": "Boston: Haiti Stock",
        "description": null,
        "locationNumber": "BOS:Haiti",
        "locationGroup": {
          "id": "8a8a9e9666194c8901661d2e06340167",
          "name": "Boston Office"
        },
        "parentLocation": null,
        "locationType": {
          "id": "2",
          "name": "Depot|fr:D",
          "description": "Depot",
          "locationTypeCode": "DEPOT"
        },
        "sortOrder": 0,
        "hasBinLocationSupport": true,
        "hasPackingSupport": true,
        "hasPartialReceivingSupport": false,
        "hasCentralPurchasingEnabled": false,
        "organizationName": "PIH Boston",
        "organizationCode": "INT",
        "backgroundColor": null,
        "zoneName": null,
        "zoneId": null,
        "active": true,
        "organization": {
          "id": "ff808081727fd8ce0172a4aacad560bb",
          "name": "PIH Boston",
          "description": "Organization encompassing all PIH USA locations, and any buying activities out of the Boston office.",
          "code": "INT",
          "dateCreated": "2020-06-11T18:35:56Z",
          "lastUpdated": "2023-04-06T18:02:45Z",
          "defaultLocation": null,
          "partyType": {
            "id": "ORG",
            "name": "Organization",
            "code": "ORG",
            "partyTypeCode": "ORGANIZATION"
          },
          "roles": [
            {
              "id": "ff808081727fd8ce0172a4abfd1960bc",
              "roleType": "ROLE_BUYER",
              "startDate": "2020-06-11T18:36:00Z",
              "endDate": "2020-06-11T18:36:00Z"
            }
          ],
          "sequences": {
            "PURCHASE_ORDER_NUMBER": "9712"
          }
        },
        "manager": {
          "id": "2",
          "name": "Mister Miranda",
          "firstName": "Mister",
          "lastName": "Miranda",
          "email": "manager@pih.org",
          "username": "manager",
          "roles": [
            "ROLE_FINANCE",
            "ROLE_MANAGER"
          ]
        },
        "address": {
          "address": "Partners In Health",
          "address2": "Attn: Supply Chain Haiti",
          "city": "800 Boylston Street, Suite 300",
          "country": "",
          "description": null,
          "postalCode": "02199",
          "stateOrProvince": "Boston, MA"
        },
        "supportedActivities": [
          "PACK_SHIPMENT",
          "ENABLE_REQUESTOR_APPROVAL_NOTIFICATIONS",
          "PLACE_REQUEST",
          "EXTERNAL",
          "SEND_STOCK",
          "ENABLE_FULFILLER_APPROVAL_NOTIFICATIONS",
          "RECEIVE_STOCK",
          "PICK_STOCK",
          "PUTAWAY_STOCK",
          "FULFILL_REQUEST",
          "REQUIRE_ACCOUNTING",
          "MANAGE_INVENTORY"
        ]
      },
      "locationType": {
        "id": "cab2b4f35ba2d867015ba2e17e390001",
        "name": "Bin Location",
        "description": "Default bin location type",
        "locationTypeCode": "BIN_LOCATION"
      },
      "sortOrder": null,
      "hasBinLocationSupport": true,
      "hasPackingSupport": false,
      "hasPartialReceivingSupport": false,
      "hasCentralPurchasingEnabled": false,
      "organizationName": null,
      "organizationCode": null,
      "backgroundColor": "FFFFFF",
      "zoneName": null,
      "zoneId": null,
      "active": true,
      "organization": null,
      "manager": null,
      "address": null,
      "supportedActivities": [
        "PICK_STOCK",
        "PUTAWAY_STOCK"
      ]
    }
  ]
}

Internal Location API

Once the new internal locations are created, I would recommend switching to the Internal Locations API in order to manage them. With that said, it only seems to be a little less verbose.

HTTP/2 200 
content-type: application/json;charset=UTF-8
date: Tue, 30 Apr 2024 20:16:56 GMT
x-application-context: application:development

{
  "data": {
    "id": "ff8081818f304ab7018f309cf85b0009",
    "name": "New Bin 1",
    "description": null,
    "locationNumber": "NEWBIN1",
    "locationGroup": null,
    "parentLocation": {
      "id": "1",
      "name": "Boston: Haiti Stock",
      "description": null,
      "locationNumber": "BOS:Haiti",
      "locationGroup": {
        "id": "8a8a9e9666194c8901661d2e06340167",
        "name": "Boston Office"
      },
      "parentLocation": null,
      "locationType": {
        "id": "2",
        "name": "Depot|fr:D",
        "description": "Depot",
        "locationTypeCode": "DEPOT"
      },
      "sortOrder": 0,
      "hasBinLocationSupport": true,
      "hasPackingSupport": true,
      "hasPartialReceivingSupport": false,
      "hasCentralPurchasingEnabled": false,
      "organizationName": "PIH Boston",
      "organizationCode": "INT",
      "backgroundColor": null,
      "zoneName": null,
      "zoneId": null,
      "active": true,
      "organization": {
        "id": "ff808081727fd8ce0172a4aacad560bb",
        "name": "PIH Boston",
        "description": "Organization encompassing all PIH USA locations, and any buying activities out of the Boston office.",
        "code": "INT",
        "dateCreated": "2020-06-11T18:35:56Z",
        "lastUpdated": "2023-04-06T18:02:45Z",
        "defaultLocation": null,
        "partyType": {
          "id": "ORG",
          "name": "Organization",
          "code": "ORG",
          "partyTypeCode": "ORGANIZATION"
        },
        "roles": [
          {
            "id": "ff808081727fd8ce0172a4abfd1960bc",
            "roleType": "ROLE_BUYER",
            "startDate": "2020-06-11T18:36:00Z",
            "endDate": "2020-06-11T18:36:00Z"
          }
        ],
        "sequences": {
          "PURCHASE_ORDER_NUMBER": "9712"
        }
      },
      "manager": {
        "id": "2",
        "name": "Mister Miranda",
        "firstName": "Mister",
        "lastName": "Miranda",
        "email": "manager@pih.org",
        "username": "manager",
        "roles": [
          "ROLE_MANAGER",
          "ROLE_FINANCE"
        ]
      },
      "address": {
        "address": "Partners In Health",
        "address2": "Attn: Supply Chain Haiti",
        "city": "800 Boylston Street, Suite 300",
        "country": "",
        "description": null,
        "postalCode": "02199",
        "stateOrProvince": "Boston, MA"
      },
      "supportedActivities": [
        "PACK_SHIPMENT",
        "ENABLE_REQUESTOR_APPROVAL_NOTIFICATIONS",
        "PLACE_REQUEST",
        "EXTERNAL",
        "SEND_STOCK",
        "ENABLE_FULFILLER_APPROVAL_NOTIFICATIONS",
        "RECEIVE_STOCK",
        "PICK_STOCK",
        "PUTAWAY_STOCK",
        "FULFILL_REQUEST",
        "REQUIRE_ACCOUNTING",
        "MANAGE_INVENTORY"
      ]
    },
    "locationType": {
      "id": "cab2b4f35ba2d867015ba2e17e390001",
      "name": "Bin Location",
      "description": "Default bin location type",
      "locationTypeCode": "BIN_LOCATION"
    },
    "sortOrder": null,
    "hasBinLocationSupport": true,
    "hasPackingSupport": false,
    "hasPartialReceivingSupport": false,
    "hasCentralPurchasingEnabled": false,
    "organizationName": null,
    "organizationCode": null,
    "backgroundColor": "FFFFFF",
    "zoneName": null,
    "zoneId": null,
    "active": true,
    "organization": null,
    "manager": null,
    "address": null,
    "supportedActivities": [
      "PICK_STOCK",
      "PUTAWAY_STOCK"
    ]
  }
}

And the Locations API seems to be about the same level of verbosity as the Internal Location API.

curl -b cookies.txt -i -X GET -H "Accept: application/json" https://openboxes.ngrok.io/openboxes/api/locations/ff8081818f304ab7018f309cf85b0009
HTTP/2 200 
content-type: application/json;charset=UTF-8
date: Tue, 30 Apr 2024 20:19:40 GMT
x-application-context: application:development

{
  "data": {
    "id": "ff8081818f304ab7018f309cf85b0009",
    "name": "New Bin 1",
    "description": null,
    "locationNumber": "NEWBIN1",
    "locationGroup": null,
    "parentLocation": {
      "id": "1",
      "name": "Boston: Haiti Stock",
      "description": null,
      "locationNumber": "BOS:Haiti",
      "locationGroup": {
        "id": "8a8a9e9666194c8901661d2e06340167",
        "name": "Boston Office"
      },
      "parentLocation": null,
      "locationType": {
        "id": "2",
        "name": "Depot|fr:D",
        "description": "Depot",
        "locationTypeCode": "DEPOT"
      },
      "sortOrder": 0,
      "hasBinLocationSupport": true,
      "hasPackingSupport": true,
      "hasPartialReceivingSupport": false,
      "hasCentralPurchasingEnabled": false,
      "organizationName": "PIH Boston",
      "organizationCode": "INT",
      "backgroundColor": null,
      "zoneName": null,
      "zoneId": null,
      "active": true,
      "organization": {
        "id": "ff808081727fd8ce0172a4aacad560bb",
        "name": "PIH Boston",
        "description": "Organization encompassing all PIH USA locations, and any buying activities out of the Boston office.",
        "code": "INT",
        "dateCreated": "2020-06-11T18:35:56Z",
        "lastUpdated": "2023-04-06T18:02:45Z",
        "defaultLocation": null,
        "partyType": {
          "id": "ORG",
          "name": "Organization",
          "code": "ORG",
          "partyTypeCode": "ORGANIZATION"
        },
        "roles": [
          {
            "id": "ff808081727fd8ce0172a4abfd1960bc",
            "roleType": "ROLE_BUYER",
            "startDate": "2020-06-11T18:36:00Z",
            "endDate": "2020-06-11T18:36:00Z"
          }
        ],
        "sequences": {
          "PURCHASE_ORDER_NUMBER": "9712"
        }
      },
      "manager": {
        "id": "2",
        "name": "Mister Miranda",
        "firstName": "Mister",
        "lastName": "Miranda",
        "email": "manager@pih.org",
        "username": "manager",
        "roles": [
          "ROLE_MANAGER",
          "ROLE_FINANCE"
        ]
      },
      "address": {
        "address": "Partners In Health",
        "address2": "Attn: Supply Chain Haiti",
        "city": "800 Boylston Street, Suite 300",
        "country": "",
        "description": null,
        "postalCode": "02199",
        "stateOrProvince": "Boston, MA"
      },
      "supportedActivities": [
        "PACK_SHIPMENT",
        "ENABLE_REQUESTOR_APPROVAL_NOTIFICATIONS",
        "PLACE_REQUEST",
        "EXTERNAL",
        "SEND_STOCK",
        "ENABLE_FULFILLER_APPROVAL_NOTIFICATIONS",
        "RECEIVE_STOCK",
        "PICK_STOCK",
        "PUTAWAY_STOCK",
        "FULFILL_REQUEST",
        "REQUIRE_ACCOUNTING",
        "MANAGE_INVENTORY"
      ]
    },
    "locationType": {
      "id": "cab2b4f35ba2d867015ba2e17e390001",
      "name": "Bin Location",
      "description": "Default bin location type",
      "locationTypeCode": "BIN_LOCATION"
    },
    "sortOrder": null,
    "hasBinLocationSupport": true,
    "hasPackingSupport": false,
    "hasPartialReceivingSupport": false,
    "hasCentralPurchasingEnabled": false,
    "organizationName": null,
    "organizationCode": null,
    "backgroundColor": "FFFFFF",
    "zoneName": null,
    "zoneId": null,
    "active": true,
    "organization": null,
    "manager": null,
    "address": null,
    "supportedActivities": [
      "PICK_STOCK",
      "PUTAWAY_STOCK"
    ]
  }
}

Note: All of these curl commands were executed against v0.9.x of the API. There might be differences between 0.8.x and 0.9.x so let me know if you encounter any issues if you’re executing against v0.8.x.