https://www.ably.io/documentation/rest/messages#idempotent explains how to specify the id of a Message in order to make the publish idempotent. This will mean that any attempt to publish a Message with the same id will result in Ably not sending the presumably duplicated message.
You can include multiple messages in a single publish request (in either REST or realtime). For example, by calling publish()with an array of Messages, rather than a single Message or name/data. When you do this, those messages are published together by Ably atomically. They are bundled together, and the messages will either all succeed or all fail. From the point of view of idempotency, there is only a single idempotency 'key' for the entire array of Messages. As such, Ably prevents you from including a different, unrelated, id in each Message, in order to avoid the mistaken impression that the messages will be individually idempotent. For example, if Ably permitted a call of publish([{ id: 'foo', data: 'first' },{ id: 'bar', data: 'second' }]), it might seem like you could later call publish([{ id: 'bar', data: 'second' }]) and the second published would be ineffective due to idempotency; but that is not the case.
As such, we require that for messages published atomically, all the messages must have an id derived from a single, base id. Each message must to contain an id, which must be of the form <base id>:<idx> where idx is a zero-based index into the array of messages. So if, for example, you want to publish 3 messages with a base id of foo, then the messages must have ids foo:0, foo:1, foo:2. This emphasises that the messages share a single 'idempotency key' (which will be 'foo').
If you want the messages to be separately and individually idempotent, you should publish them in a way that the publish is not done atomically. For example, instead of a single publish() call with an array of Messages, you can issue multiple publish() calls serially. If you are publishing over REST and want the publishes to all happen with a single HTTP request (e.g. because you are using a client library where publishes are blocking, and the publish rate is high enough that serial blocking requests would be too slow), you can use the batch publishing API, where each message is contained in its own 'batchspec object'. All messages in a single batchspec are published atomically (for each channel in that batchspec), so by specifying multiple batchspecs, you can publish the messages nonatomically, so allowing you to specify a different idempotency id for each
Please get in touch for any further help or questions you may have on this.