A multi-byte character shifts the byte offset
"Check café https://example.com" — the link starts at byte 12, not character index 11, because "café " is 5 characters but 6 UTF-8 bytes (é takes 2 bytes).
Computes real AT Protocol "facets" for a Bluesky post: byte-accurate ranges (byteStart/byteEnd, real UTF-8 byte offsets, not character indexes) for every link, hashtag and @mention in your text, in the documented app.bsky.richtext.facet format. Link and tag facets are fully computed and ready to use; a mention facet’s "did" field needs a live handle lookup this client-side tool cannot perform, so it is marked null.
byteStart 38, byteEnd 56 — did: null (resolve via com.atproto.identity.resolveHandle)
byteStart 64, byteEnd 84
byteStart 85, byteEnd 96
Full record (text + facets)
{
"text": "Scheduling posts with SkedCast — cc @alice.bsky.social, check https://skedcast.com #scheduling",
"facets": [
{
"index": {
"byteStart": 38,
"byteEnd": 56
},
"features": [
{
"$type": "app.bsky.richtext.facet#mention",
"did": null
}
]
},
{
"index": {
"byteStart": 64,
"byteEnd": 84
},
"features": [
{
"$type": "app.bsky.richtext.facet#link",
"uri": "https://skedcast.com"
}
]
},
{
"index": {
"byteStart": 85,
"byteEnd": 96
},
"features": [
{
"$type": "app.bsky.richtext.facet#tag",
"tag": "scheduling"
}
]
}
]
}"Check café https://example.com" — the link starts at byte 12, not character index 11, because "café " is 5 characters but 6 UTF-8 bytes (é takes 2 bytes).
"See https://example.com/page#section here" produces exactly one link facet for the whole URL — the "#section" inside it is never reported as a separate tag facet.
Sign up free — no credit card. You land on the Free plan, and you can start a one-time 7-day Studio trial from your workspace whenever you are ready. Connect your first accounts, import a batch, and watch one post fan out across every platform.