The way in which I've done this in the past follows a "ticket" or "receipt" pattern. The REST service accepts requests for a resource (a report name, a znode, etc.) and returns a ticket. This ticket (usually a UUID or something similar) can be used to represent a session. Subsequent requests use this ticket to check on the status of their requests. To ensure proper expirey of tickets, one of two cases occurs; you can time out tickets or, upon receiving a result, the client must provide an ACK (acknowledgment) back to the service.
ex.
Request: GET /zookeeper/znode/ephemeral/foo
Response: 1234-1234-1234-1234
Request: GET /zookeeper/status/1234-1234-1234-1234
Response: WORKING (or UNAVAILABLE or BLOCKED or NOTREADY or FAILED...)
Request: GET /zookeeper/status/1234-1234-1234-1234
Response: ACQUIRED (or AVAILABLE or OK or SUCCESS or some value(s)...)
Request: GET /zookeeper/acknowledge/1234-1234-1234-1234
Response: OK (or UNKNOWN TICKET, etc.)
Interesting manageability messages:
Request: GET /zookeeper/sessions (or /tickets)
Response: [ 1234, 5668, ... ]
Request: GET /zookeeper/kill/
Response: OK (or UNKNOWN or FAILED...)
This has worked very, very well. This does mean, however the REST service is stateful which makes things like load balancing trickier. I've used a protocol that ensures a server ID is returned with each response and if the client receives a different server ID and an UNKNOWN ticket, you assume the service you were talking to has died and start over. This implies sticky load balancing (i.e. round-robin wouldn't work here). The REST service needs to be multi-threaded to support performing these requests in parallel and provide access to a ticket database (usually in memory, sync'd hashtable data structure) as well as a session / ticket timeout thread.
Hope this helps.