# `Otel.Trace.SpanId`
[🔗](https://github.com/yangbancode/otel/blob/main/lib/otel/trace/span_id.ex#L1)

Opaque 64-bit Span identifier (W3C `parent-id` / OTel
`trace/api.md` §SpanContext SpanId, L234-L235).

The OTel spec defines a valid `SpanId` as an 8-byte array with
at least one non-zero byte (L234-L235). On the wire, W3C Trace
Context encodes it as a 16-character lowercase hex string
(`parent-id = 16HEXDIGLC`, §parent-id). Internally we store it
as a non-negative 64-bit integer and expose it through
`@opaque` so Dialyzer distinguishes it from unrelated integers
— and from `Otel.Trace.TraceId`.

Per spec L266 *"The API SHOULD NOT expose details about how
they are internally stored"* — callers go through `to_hex/1`
/ `to_bytes/1` rather than the raw integer.

## Public API

| Function | Role |
|---|---|
| `valid?/1` | **Application** (OTel API MUST) — IsValid for SpanId (L234-L235, L268-L271) |
| `to_hex/1` | **Application** (OTel API MUST) — Hex retrieval (L258-L262) |
| `to_bytes/1` | **Application** (OTel API MUST) — Binary retrieval (L263-L264) |
| `new/1` | **SDK** (SDK helper) — wrap a 64-bit integer from an ID generator |

## References

- OTel Trace API §SpanContext SpanId: `opentelemetry-specification/specification/trace/api.md` L234-L235, L256-L266
- W3C Trace Context Level 2 §parent-id: `w3c-trace-context/spec/20-http_request_header_format.md` §parent-id

# `t`

```elixir
@opaque t()
```

A 64-bit Span identifier (W3C `parent-id`).

Stored as a `0..2^64 - 1` integer but declared `@opaque` so
callers cannot construct one with an arbitrary integer literal
from outside the module. Use `new/1` at construction
boundaries (e.g. random generation in the SDK id generator)
and `to_hex/1` / `to_bytes/1` for serialisation.

The all-zero value is reserved as the invalid sentinel meaning
"no span"; `valid?/1` returns `false` for it (spec L234-L235 +
W3C §parent-id L113-L117).

# `new`

```elixir
@spec new(integer :: 0..18_446_744_073_709_551_615) :: t()
```

**SDK** (SDK helper) — wrap a 64-bit unsigned integer as a
`t()`.

The opaque-boundary-respecting way to turn a raw integer (e.g.
from an ID generator) into a `SpanId.t()`. The `@spec` input
range is the type gate — Dialyzer flags literal out-of-range
callers; runtime-origin values are the caller's
responsibility.

# `to_bytes`

```elixir
@spec to_bytes(span_id :: t()) :: &lt;&lt;_::64&gt;&gt;
```

**Application** (OTel API MUST) — "Binary Retrieval"
(`trace/api.md` L263-L264).

Returns the SpanId as an 8-byte big-endian binary.

# `to_hex`

```elixir
@spec to_hex(span_id :: t()) :: &lt;&lt;_::128&gt;&gt;
```

**Application** (OTel API MUST) — "Hex Retrieval"
(`trace/api.md` L258-L262).

Returns the SpanId as a **16-character lowercase** hex string
(zero-padded). Matches the W3C `parent-id` wire format
(§parent-id: `16HEXDIGLC`).

# `valid?`

```elixir
@spec valid?(span_id :: term()) :: boolean()
```

**Application** (OTel API MUST) — "IsValid for SpanId"
(`trace/api.md` L234-L235, L268-L271).

Returns `true` iff the SpanId has at least one non-zero byte.
Per spec the all-zero value (`0000000000000000`) is explicitly
invalid (W3C §parent-id L113-L117).

Accepts any term as a robust predicate — returns `false` for
`0`, negatives, out-of-range integers, and non-integers.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
