The usual approach would be to use atoms inside the system, e.g. :source_lvl1_api
and convert to/from integer encoding on the system boundary, if needed. This makes it easy for debugging and introspection since at runtime you have readable atoms, instead of opaque integer values flying around. You can generate the conversion functions easily with a sprinkle of macros:
values = [source_lvl1_api: 301023, ...]
for {key, value} <- values do
def encode(unquote(key)), do: unquote(value)
def decode(unquote(value)), do: unquote(key)
end