While the line is somewhat blurry, for me a rule of thumb is: data that your business logic works on should be in the body, metadata can/should be put in headers.
Another way of looking at it is: data which appears only in specific kinds of requests should be in the body while data which is handled consistently across the whole application should go into headers.
Yet another point of view is: can you imagine that a piece of data is handled globally, e.g. by a router/firewall rather than by your application? If yes, it should probably go in the headers rather than in the body.
Some examples of applying these rules would be:
- Security credentials go into headers since most probably they will be handled the same in all places of an application; at implementation level there will probably be some request filter rejecting requests without valid credentials regardless of the actual endpoint handling the request in case it does get through the filter.
- If, on the other hand, you have an endpoint which allows an admin to add users to your system, the login of the user to be created should be in request body since: a) it is handled by your application's business logic, b) it appears in this particular endpoint but not in others.
- Options which control caching may well fit into headers (unless caching is a core part of your application's business logic).
Going back to your question about the unique device ID: if it is used in a consistent way everywhere, e.g. only for logging, it can be put in the headers. But if it is used to filter requests in different ways depending on the endpoint, it would better be in the body. Of course if you have both use cases, it is probably better to stick with just a single way of passing it (probably the headers) rather than forcing the API user to put the same data in two places, giving you the dilemma of either allowing inconsistent inputs or implementing some kind of validation.