# Workers Use `Oban.Pro.Worker` with `args_schema` for type-safe, validated job arguments: ```elixir defmodule MyApp.SomeWorker do use Oban.Pro.Worker, queue: :default, max_attempts: 3 args_schema do field :user_id, :integer, required: true field :status, :enum, values: ~w(pending active)a, default: :pending field :metadata, :map end @impl Oban.Pro.Worker def process(%Oban.Job{args: %__MODULE__{} = args}) do :ok end end ``` ## Structured Args Validates data when calling `new/1`, before insertion. Notable types: - `:enum` - Validates and casts to atoms: `field :status, :enum, values: ~w(pending completed)a` - `:term` - Safely encodes any Elixir term - `:uuid` - Alias for `:binary_id` - `{:array, *}` - Lists of typed values Nested structures with `embeds_one` and `embeds_many`: ```elixir args_schema do field :order_id, :integer, required: true embeds_one :customer, required: true do field :customer_id, :integer, required: true end embeds_many :line_items do field :product_id, :integer, required: true field :quantity, :integer, required: true end end ``` ## Encrypted jobs Protect sensitive args with transparent encryption: ```elixir use Oban.Pro.Worker, queue: :default, encrypted: [key: {Application, :fetch_env!, [:my_app, :oban_key]}] ``` **Important:** Only `args` are encrypted, never put sensitive data in `meta`. ## Hooks **Execution hooks:** - `before_new/2` — modify args/opts before job creation - `before_process/1` — runs before `process/1` - `after_process/3` — runs after `process/1` with state (`:complete`, `:cancel`, `:discard`, `:error`, `:snooze`), job, and result **External state hooks:** - `on_cancelled/2` — job cancelled externally (manual, deadline, dependency) - `on_discarded/2` — job discarded by DynamicLifeline ```elixir @impl Oban.Pro.Worker def after_process(state, job, result) do if state in [:error, :discard], do: notify_error(job) :ok end ``` Attach hooks explicitly or globally: ```elixir use Oban.Pro.Worker, hooks: [MyApp.ErrorHook] # Global hooks (in application startup) Oban.Pro.Worker.attach_hook(:errors, MyApp.ErrorHook) ``` ## Deadlines Cancel jobs that don't run within a time limit: ```elixir use Oban.Pro.Worker, deadline: {1, :hour} # Override at runtime MyWorker.new(%{}, deadline: {30, :minutes}) # Force cancel already-running jobs use Oban.Pro.Worker, deadline: [in: {10, :minutes}, force: true] ```