This document provides a comprehensive reference for the Obsidian API, which enables developers to create compatible add-ons.

The API has been enhanced and redesigned to support advanced features including constraint commands and notification-type messages, providing developers with greater control and flexibility when creating interactive experiences.

Backward compatibility

Obsidian maintains backward compatibility with a subset of the LEGACY Aeros API, ensuring that most existing furniture continues to function seamlessly by changing the Vertical and Horizontal angles and Stage of hardness. This compatibility has been enabled since version v107. It only works if the owner sits on the device that sends these commands.

Obsidian starting from v109 also responds to a "getInfo" that can be captured from a LEGACY script. This creates an opportunity for creators of other types of devices to query for Aeros product with just one command. The returning string is the same, but the id of the incoming message will reveal the actual device worn (through llKey2Name or similar).

API script

The API script is a communication layer that helps counter griefing, message sniffing, spoofing, and potential DoS attacks and script tampering.

The API script is a lightweight script that ensures messages conform to established rules and restrictions before passing them to the target. It enriches messages with a token for verification by the Device, receives notifications, verifies their origin, and passes them along to other scripts in the same link using Linked Messages.

There are two variants of the script:

  • */ aeros / API v[VERSION] — ‘Regular’ (included in the box). Communicates only with the current owner Obsidian.
  • */aeros / API v[VERSION] - <brand name> — ‘Customized’, provided by request. Enables communication with the other owner’s devices. To obtain such a script, follow the instruction.

Consider Express API scripts. This package targets specific use cases with simplified interfaces. See Express API Scripts for more information.

The version number incrementally grows with backward compatibility within the v1xx space.

Same-owner devices

  • Attachments or rezzed devices owned by the same user can communicate via the API without restrictions. A user can create a script to perform any desired function using the API, provided the API script is placed in the same link.
    • You will find a COPY|TRANS API script in the box. Use the following helper function to send an API command:
      OBS_send(key target, string payload) {
        llMessageLinked(LINK_THIS, -78000, payload, target);
      }
    • Sharing your add-on that utilizes the API requires including the API script. You may leave its permissions unchanged. The product will only be able to target the new owner’s Device.

Cross-owner devices

If you are a creator of add-ons or rezzed devices that require cross-owner communication, you must communicate with Obsidian using a ‘Customized’ API script issued to you for your brand. It should replace the Regular API script.

  • A cross-owner device is, for example, furniture that will send commands to an avatar who sits on it.

  • To combat abuse of the ‘Customized’ API script by implanting a griefing script into modifiable creator prims, you will use a custom proxy function instead of the generic OBS_send mentioned earlier. We will send you the function together with the script. The function contains a secret with which the message will be signed for verification by the API script. The ‘Customized’ script will reject regular messages that do not contain the signature and nonce.

    // It is crucial that the script containing the secret is not sent
    // with MOD permissions for the next user
    string CREATOR_SECRET = "<secret>";
    OBS_send(key target, string payload) {
      string nonce = (string)llFrand(1.0);
      string signature = llSHA1String(payload + ":" + nonce + ":" + CREATOR_SECRET);
      llMessageLinked(LINK_THIS, -78000, signature + "|" + nonce + "|" + payload, target);
    }

    For this security model to be successful, it is vital that the script containing CREATOR_SECRET is never sent with MOD permissions to the next owner. If you discovered that this has happened, please contact Aeros team so that we can invalidate the script.

  • Sharing your add-on or product that utilizes the API with cross-owner communication requires inclusion of the Customized API script. You may leave its permissions unchanged.

Errors

If any issues are discovered while sending a message via the API script, it will display errors in owner chat as outlined in Error Codes.

Commands format

Send ;-delimited key=value pairs that will modify the behavior of the target device via a proxy OBS_send function according to the tables below. A group of values is applied in one cycle. Constraints applied immediately for the intents that follow in one message.

Target key

Target Obsidian using the owner key. If the object key is detected through any means, it can also be used for narrow targeting.

Limits

API script will reject outgoing messages that:

  • Come from a different link number — ERR(lnk).
  • Target a cross-owner device (this limitation is lifted in the ‘Customized’ API script) — ERR(xow).
  • Contain the "!" character anywhere in the message — ERR(exc).
  • Are longer than 300 characters — ERR(len).
  • Include more than 40 commands — ERR(cnt).

An error message will be displayed in the local chat for the current owner to help diagnose the issue.

Float number rounding

The API internally rounds floats to a precision of 0.001. For example, 0.0015 and 0.002 are the same from the API’s standpoint. The majority of parameters, except fm and fp, support value snapping, so sending a close-enough value will be sufficient. Notifications will report back the exact values applied, rounded to the mentioned precision of 0.001. Values 0 and 1 always arrive as "0" and "1".

For constraints that are exact to distinct implemented values, use exact or higher precision. For example, 0.3333 can be used to represent 0.333, but 0.3 will not be “equal” to 0.333. Precision in ranges also matters; for example, the 0.3-1 range will include 0.333 but the 0-0.3 range will not. In other words, range formulas do not snap to distinct implemented values.

For example, this script sends exact or higher precision as intents:

float s = 2.0/3;
OBS_send(llGetOwner(), "vis=0.5;v=0.5;s="+(string)s);

For cross-owner device communication, a 'Customized' API script is needed along with a modified OBS_send function that includes signature and nonce as described earlier.

Intents, constraints, notifications

The majority of commands exist in 3 variants:

VariantExamplePurposeSourceReceiverAPI script — LM num (Send)API script — LM num (Receive)
(No prefix)f_mode=0Sends the Device an intent to attempt to set f_mode to 0.HUD, Add-onsDevice-78000-78002 (only same-owner other attachments)
% prefix%f_mode=0-0.5Commands the Device to constrain f_mode to the range of 0-0.5 (inclusive).Add-onsDevice-78000-78002 (only same-owner other attachments)
! prefix!f_mode=0The Devices notifies of a change of intent due to constraints. It represents values that have been accepted and applied. The add-ons might adjust in reaction to these.DeviceHUD (for HUD reactivity parameters), Add-ons— (cannot be sent)-78001 (attached and, if rezzed, initial reply or while subscribed(*))

(*) Sending any message by a non-attached product will result in an initial reply (may come split in several messages in response to fp* and tw). To continually receive notifications from the Device, a rezzed product has to subscribe for notifications by sending sub=1 every 15 seconds. To quickly unsubscribe from continuous notifications, send sub=0.

According to the table, use:

  • The OBS_send function which will send intents and constraints using Linked Messages via channel -78000.
    • Attempting to send a !-type setting will invalidate the whole message.
  • Listen (link_message):
    • -78001 — Listen to notifications from the Device regarding actually applied settings, according to constraints and value limitations (including reactions to intents and constraints sent by the listening add-on itself or a rezzed product).
      • This is the most commonly used type of message. The API script ensures it only comes from the Device.
      • Rezzed products will receive an initial reply (may come split in several messages in response to fp* and tw) and a continuous flow of notifications if they subscribe to them using sub=1 every 15 seconds.
    • -78002 — Intercept raw intents and raw constraints sent by the HUD and other add-ons to the Device. (Rarely used.)
      • Will only be heard by same-owner attached add-ons.
      • Use this channel only to learn what the HUD or add-ons send to the Device. Be aware that messages coming from add-ons may contain errors.

To summarize, a typical add-on listens to !-type commands (-78001) to sync its state with the Device. In order to initially sync with the Device’s and other add-ons’ states, it sends an ack=0 intent to receive a set of ! notifications. To control other settings, it can issue intent-type commands and, if necessary, %-prefixed commands to impose constraints. For rezzed and attached devices that need cross-owner notifications, a subscription is required with sub=1 every 15 seconds.

Commands overview

Here is an overview of all parameters and supported constraint and notification variants; which parameters receive immediate updates in the HUD (HUD reactivity) and which ones support Value snapping.

Key(no-prefix)-intent%-constraint!-notificationHUD reactivityValue snapping
vis
f_mode
s
v
h
c (since v110)
v_b
z_b
th
ao
m_int
m_kin
m_vibe
thr
fm
fp
fp_int
fp_dir
fp_thr— (no UI)
fp_vis— (no UI)— (cannot be set)

Message control commands

Key(no-prefix)-intent%-constraint!-notificationHUD reactivityValue snapping
id✅ (once in reply to initial command)
ack✅ (if not equal to 0)
sub
  • !id (only notification) is an Object Key of the original message sender which caused the reply from the Device.
    • The Device will keep sending further notifications without the !id.
  • ack serves two main purposes:
    • ack=0 can be included with any message to receive the current state of the Device (except fp* and tw messages). In reply to ack=0, the notification set will have no !ack=0.
    • ack=<random value> (avoid using =, ! and ; characters and surrounding spaces) can be used, in combination with the id, to help the add-on recognize that “this notification is in reply to my message” outcome. It is the add-on’s responsibility to keep track of the sent ack and take into consideration that notifications may asynchronously arrive after several sent commands. In practice, saving the last sent ack should suffice, since every notification contains the full set of settings, except for the fp* and tw. Any constraints will be immediately seen in a notification that arrives back with matching !ack, so if an add-on needs to react accordingly, do not ignore this message simply assuming it has the same set of properties the add-on has sent.
  • sub subscribes to and unsubscribes from continuous notifications. To subscribe to notification-type messages, send the sub=1 command every 15 seconds. To quickly unsubscribe, send sub=0. If a script sending these commands is attached, the command will be ignored.

    For a single immediate notification to a sent message, there is no need to subscribe.

Play HUD-only commands

The following commands are accepted only when they come from the Play HUD:

Key(no-prefix)-intent%-constraint!-notificationHUD reactivityValue snapping
pos✅ (Play HUD only)
f_auto(*)✅ (Play HUD only)
th_eq(*)✅ (Play HUD only)

(*) Since v108.

  • The Play HUD sends f_auto=1 if foreskin auto mode is selected. This is transferred to the Device, and if API messages change the s (stage of hardness) and no f_mode is sent, the Device will set f_mode to one of 1,0.5,0.5,0 depending on the s value.
  • The Play HUD sends th_eq=0,0.333,0.667,1 (default) or user modified list of values when Girth EQ is adjusted. This is used in case an API message changes s (stage of hardness) and no th is sent to adhere to the user’s preference.

HUD reactivity

HUD reactivity refers to the action of showing the Device’s current parameters in the HUD.

Currently this is limited to just three parameters:

  • Stage of hardness (s)
  • Vertical angle (v)
  • Horizontal angle (h)

This ensures the user of the HUD feels they are always in control, expecting only two UI elements to ever change through the API: Main Arrow (responsible for s and v) and Horizontal Angle Arc (h parameter).

In practice, this means that as soon as the user touches anything on the HUD, the HUD will revert any changes made by the API according to the HUD’s current view. The HUD always sends a full package, not just the parameter the user clicked. All accumulated constraints will be adhered to, if at all possible, and the HUD will only flip the UI in these two elements as explained above.

HUD reactivity for non-reactive parameters means that, for example, the HUD will not show the alpha-cut button lit up, nor the on/off button shown as toggled “on” when constraints of %vis force the Device to flip to 0.5 “alpha-cut” although an intent was to set it to 0 (“invisible”). This is because vis is not among the parameters supporting HUD reactivity. As soon as the add-on is detached or internally changes constraints, the Device will be able to flip vis to the originally intended 0. This happens automatically with a maximum delay of 10 seconds.

The HUD sends its full state only when it is clicked or when new add-ons made by Aeros are attached.

Internally there is a capability to perform reactivity for all parameters. In a future update, we might add a button that performs a manual synchronization.

Value snapping

Value snapping refers to the action of value snapping to the edges of constraints when an intent is not within accumulated constraints.

Two examples of how that works:

  • If vis, after all intersection calculations, is constrained to a range 0.5-1 and an intent is sent to set it to 0 (invisible), the Device will snap the vis parameter to the closest value to the intended one, which is 0.5 (alpha-cut).
  • If vis, after all intersection calculations, is constrained to 0,1 (two possible exact values) and an intent is sent to set it to 0.5 (alpha-cut), the Device will snap the vis parameter to the closest value to the intended one, which is 0 (invisible). This makes the alpha-cut intent still possible to choose from the HUD, but the effect will be as if switching the visibility off.

The fm and fp parameters do not support snapping due to their nature: if a fluid of one value is requested, it shouldn't attempt to find the closest within allowed values and therefore shift from its original purpose. The 0 (stop) will not be able to be performed if it is not within the allowed constraints.

Main commands

The following values are expected to be float numbers from 0 to 1, inclusive. For some parameters, the values are clamped and rounded to Implemented Values.

KeyImplemented ValuesDescriptionDefault
ack(*)0 or any string value up to 36 characters long(Intent and Notification only). Request current state without changing anything. Value should not contain leading or trailing spaces, nor =, ! or ; symbols.
vis0, 0.5, 1Visibility 0 — invisible, 1 — visible. 0.5 — the only implemented alpha cut thus far.1
f_mode0, 0.5, 1Foreskin mode: 0 — open, 0.5 — semi-closed, 1 — closed. The HUD chooses the value depending on the current foreskin selection. The “auto” selection modifies the mode sent depending on s.1 (result of “auto” mode in soft stage)
s0, 0.333, 0.667, 1Stage of hardness: 0 — soft, 1 — hard. Values in-between represent the range from soft to hard.0
v0.25-0.75 (range)Vertical angle for the shaft. 0.25 — down, 0.5 — horizontal, 0.75 — up.0.32
h0.25-0.75 (range)Horizontal angle for the shaft. When looking from top down: 0.25 — right, 0.5 — middle, 0.75 — left.0.5
c0, 1since v110Curve toggle for horizontal angle core animations. 0 - disabled, 1 - enabled.0
v_b0.3-1 (range) — since v110 (0.3-0.7 before v110)Vertical angle for the balls. 0.3 — back, 0.5 — middle, 0.7 — forward, 1 — maximum forward since v1100.5
z_b0-1 (range)Z-axis adjustment for the balls. 0 — low point, 0.5 — middle point, 1 — high point.0.5
thBefore v107: 0, 1; From v107: 0-1 (range)Thickness (girth) control. 0 — minimum girth, 1 — maximum girth.(§)
ao0, 1Animation Overrider. 0 — “off”, 1 — “on”. When “on”, the Device activates special Motion animations when avatar walks or runs.1
m_int0, 0.333, 0.667, 1Motion intensity. (Called “physics intensity” in the HUD help.) 0 — motion turned off, 1 — max intensity.0
m_kin0, 1Motion kinematics mode (Called “physics mode” in the HUD help). 0 — both the shaft and balls are animated, 1 — only the balls are animated. Represented by arrow down and arrow up respectively.1 (arrow up)
m_vibe0, 1Internal Core (‘vibe’) animations. 0 — “off”, 1 — “on”. When “on”, the Device plays animations that define the curvature and movements of the Device. The API is available for scripts to set or constrain m_vibe to 0.1
(Sent Separately:)
thr(♦)0-1 (range)Throbbing intent. 0 — stop, 1 — max intensity.0

(*)ack with value 0 is a “special” variant. By convention, it requests the current state like any other ack=<value>, but the reply will not contain a !ack=0 notification. Use it to grab the state without changing any settings. For example, an add-on can send ack=0 or ack=<value> upon attachment or, perhaps, when all runtime permissions are received, in order to get the initial state and react accordingly to maintain itself visually in sync with the Device and the rest of the add-ons. The Device will automatically re-broadcast its current state as notifications upon region crossing and teleporting, so it is rarely necessary to issue ack=0 in these cases.

(♦)thr:

  • Before v107: The HUD sends thr=0.5 separately as a one-time action when the owner clicks the “throbbing” button.
  • From v107: When the “throbbing” button is toggled on, the HUD sends thr=0.5 at random intervals every 2-5 seconds.

(§)th:

  • Before v107: The HUD chooses the value depending on the Settings > Girth area.slider (“always thin”—“auto”—“always thick”) and current stage (s).
  • From v107: The HUD chooses the value based on the girth EQ sliders in the Settings.

Particles fluids

The HUD sends fluids commands separately from the other commands. Mesh and particles fluids can co-exist on the Device. Particles fluids can only be of one type at a time.

Commands vis=0 and fp_dir=(any value) implicitly stop particles fluids. Notification from the Device will include !fp=0;fp_int=0.

The Device postpones issuing particles, and !fp* notifications, for about one second after each tap to accumulate multi-tap clicks before starting the particles (fp_int). If the HUD quickly sends fp=0 within this period, the Device interprets this as a cancellation.

The following commands are sent by the HUD from the buttons in the fluids section. They can also be issued by scripts.

KeyImplemented ValuesDescription
fp0 — “stop”, 0.25 — “pee”, 0.5 — “splash”, 0.75 — “precum”, 1 — “cum”Particles fluids action intent. 0 — intent to stop the action.
fp_int0-1 (range)Particles fluids intensity intent. The HUD progressively issues bigger numbers when the user quickly clicks the same fluids button (up to 10 times).
fp_dir0, 1Particles fluids direction intent. 0 — forward, 1 — down. Affects both the visible source and direction of particles.
fp_thr0-1 (range)Throbbing intent to accompany particles fluids. 0 — stop, 1 — max intensity. The HUD sends it with “cum” and value equal to fp_int.

Commands without Intent variant:

KeyImplemented ValuesDescription
%fp_vis(*)(same as fp; 0 (stop) is always allowed)(constraint) %fp_vis constrains visibility of requested fp.

(*)%fp_vis constrains visual aspect of particles. Some add-ons, like condoms, can limit %fp_vis to “suppress” visual ejaculation.

Mesh fluids

Mesh fluids are decorations, not intended to be recognized as ejaculation. !fm will be included in notifications for add-ons to react accordingly if needed. %fm can also be sent to constrain fm if an add-on you develop conflicts with the mesh fluids presence.

Mesh and particles fluids can co-exist on the Device. Mesh fluids can only be of one type at a time.

KeyImplemented ValuesDescriptionDefault
fmMesh fluids: 0 — “stop”, 0.75 — “precum”, 1 — “cum”Mesh fluids type. 0 hides them.0

Notifications

Notifications will report back the exact values applied according to all constraints. Values are rounded to 0.001 precision. Values 0 and 1 always arrive as "0" and "1".

Notifications can only originate from the Device. Attempting to send a !-type setting through the API will invalidate the whole message — ERR(exc).

Commands supporting notifications are sent by the Device in groups, separated by ;. !fp*-type and !thr notifications will arrive separately.

This incomplete table of notifications shows how to interpret !fp*-type notifications:

KeyImplemented ValuesDescription
!fp0 — “stop”, 0.250 — “pee”, 0.500 — “splash”, 0.750 — “precum”, 1 — “cum”Particles fluids action issued. 0 when stopped.
!fp_int0-1 (range)Particles fluids intensity issued.
!fp_dir0, 1Particles fluids direction issued. 0 — forward, 1 — down.
!fp_thr0-1 (range)Throbbing effect accompanying particles fluids issued. 0 — stop, 1 — max intensity.
!fp_vis(*)Particles fluids intent visually issued. 0 — “stop”, 0.250 — “pee”, 0.500 — “splash”, 0.750 — “precum”, 1 — “cum”.Particles fluids visibly issued. 0 (“stop”) means they were stopped.
(others)(The rest of the parameters have similar interpretation)

(*)!fp_vis will match !fp when both are within allowed constraints. When a match is impossible, particles are suppressed and !fp_vis=0 arrives.

If your goal is to recognize unsuppressed ejaculation, consider listening to the !fp_vis notification. You may consider !fp for devices that care about successful action, rather than visibility of the particles. These include condoms, facial animation activators, etc.

If you are designing an add-on that needs to constrain particles, such as a condom, consider a constraint of %fp_vis=0.5,0.75 along with any other necessary constraints (like %fm=0 if the condom does not look right with the mesh liquids). This will specify that only “splash” and “precum” are allowed to be used as particles fluids, which specifically excludes 0.25 (“pee”) and 1 (“cum”). As a result, only 0 (always allowed), 0.500 and 0.750 will arrive as !fp_vis notifications.

For an add-on that needs to be aware of ejaculation, listening to !fp to be equal to 1 will be the right approach.

Constraints

Example: An add-on is limited to a particular f_mode due to the way it is built. The add-on notifies the Device of its allowed ranges for f_mode.

Add-ons need to repeat their constraints every 15 seconds (not more often; the Device gives an allowance of 20 seconds to retain constraints internally and also checks for availability of the add-on in the region every 10 seconds).

What if a constraint depends on the current state of Obsidian? For example, an add-on may need to change the vertical angle constraint (v) due to a change in the stage of hardness (s). This use case requires sending live constraints. New constraints overwrite previous ones per add-on’s objectKey, each setting individually. It is advisable to pass all constraints in one message. If a particular setting disappears from the list of constraints, instead of changing its value, it retains the previous one until a 20 second period passes.

The resulting constraint introduced by several add-ons on the same setting is the one that will satisfy them all.

There could be situations when the merged constraints are too strict, leaving no solution for a parameter to resolve to. In such cases the Device will not be able to change according to new commands, and has to inform the owner of the names of potentially incompatible add-ons.

Constraining commands may be used together with intent commands. They are applied in the sequence they follow each other. So if a constraining command is followed by an intent-type command, the intent-type command will be constrained by the previous command right away.

Format

To send a constraint, prefix a regular command with % and pass a single number or a range of allowed (--delimited) values. A list of individual numbers and ranges can also be sent, using , for separation. All values need to be in the 0-1 range. When providing single values as constraints for parameters that only implement certain discrete values (like f_mode), use either exact values from the table or any higher than 3 digits precision (0.3333 will be trimmed to 0.333, 0.6667 to 0.667 etc.).

Please remember that internally API rounds up all floats to a precision of 0.001 and that all ranges are inclusive from both ends.

Non-overlapping ranges will differ by 0.001, e.g. 0-0.449 and 0.500-1, but these two ranges are also adjacent from the API’s standpoint without any gap in-between. Single values like 0.333 and 0.667 should be specified with either exact or higher precision. Ranges that use such values can extend beyond their edges; for example, 0-0.666 will not include 0.667, but 0-0.6666 will.

Constraint examples

  • %m_vibe=0 — limit internal Core (‘vibe’) animations to 0 (do not play). This will silence animations defining the shape and motion of Obsidian, but not the Control animations that are used for angles and adjustments.
  • %f_mode=0-0.5 — limit f_mode to the range of 0-0.5.
  • %f_mode=0,1 — limit f_mode to either 0 or 1.
  • %th=0 — limit th to 0.
  • %s=0-0.333 — limit s to the range of 0-0.333. (If you specify 0.3 or 0.33, this will not include 0.333)
  • %s=0-1 — immediately remove previously set constraints on s.
  • %vis=0 — limit visibility to 0 (hidden).
  • %fm=0 — only allow “stop” (invisibility) for mesh fluids.

Scripts examples

Please navigate to Examples page to find examples of scripts.

EOF