# `Movoa`
[🔗](https://github.com/hanshs474/movoa-ex/blob/main/lib/movoa.ex#L1)

Official Elixir SDK for [Movoa](https://movoa.ai), the AI video generator
with sound.

    {:ok, url} = Movoa.generate_video("a lighthouse in a storm, thunder")
    {:ok, url} = Movoa.generate_video("the statue turns its head", image_url: "https://example.com/statue.jpg")
    {:ok, left} = Movoa.credits()

Pass `api_key:` or set `MOVOA_API_KEY`; create a key at
https://movoa.ai/settings/apikeys. Runs cost credits on your Movoa account,
at the prices on https://movoa.ai/pricing.

Failures are `{:error, %Movoa.Error{reason: reason}}`; see `Movoa.Error`.
Uses only `:httpc` from OTP and the standard `JSON` module; no dependencies.

# `check`

```elixir
@spec check(integer(), term()) :: {:ok, term()} | {:error, Movoa.Error.t()}
```

Maps one answer of movoa.ai to `{:ok, data}` or `{:error, %Movoa.Error{}}`.

movoa.ai answers almost every refusal as HTTP 200 with a non-zero `code` and
a `data.reason`, so the status code alone reads a refusal as success.

# `credits`

```elixir
@spec credits(keyword()) :: {:ok, integer()} | {:error, Movoa.Error.t()}
```

Credits left on the account.

# `default_model`

The default model.

# `generate_video`

```elixir
@spec generate_video(String.t(), keyword()) ::
  {:ok, String.t()} | {:error, Movoa.Error.t()}
```

Generates one clip with sound and returns the URL of the finished MP4.

Options:

  * `:model` - `"minimax-h3"` (default), `"veo-3-1-fast"`, `"seedance-2-5"`,
    `"seedance-2"`, `"seedance-2-fast"` or `"seedance-2-mini"`
  * `:image_url` - a public https image; makes it photo to video, with the
    image as the first frame
  * any other keyword, such as `duration: 8`, `resolution: "768p"` or
    `aspect_ratio: "9:16"`, goes to the model as-is (so does an `:options` map)
  * `:api_key`, `:base_url` (default `MOVOA_BASE_URL`, then https://movoa.ai)
  * `:poll_ms` (10 seconds), `:timeout_ms` (30 minutes)

# `generate_video!`

```elixir
@spec generate_video!(String.t(), keyword()) :: String.t()
```

Like `generate_video/2`, but returns the URL or raises `Movoa.Error`.

# `status`

```elixir
@spec status(String.t(), keyword()) :: {:ok, map()} | {:error, Movoa.Error.t()}
```

Returns the run's record: a map with `"id"`, `"status"` (`"pending"`,
`"processing"`, `"success"`, `"failed"` or `"canceled"`), `"videos"` once
it succeeded, `"errorMessage"` / `"failureKind"` when it failed, and
`"queued"` / `"queueEta"` (seconds) while it waits in the queue.

# `submit`

```elixir
@spec submit(String.t(), keyword()) :: {:ok, String.t()} | {:error, Movoa.Error.t()}
```

Starts a run and returns its id without waiting. Same options as `generate_video/2`.

# `wait`

```elixir
@spec wait(String.t(), keyword()) :: {:ok, map()} | {:error, Movoa.Error.t()}
```

Polls a submitted run until it succeeds (returns its record), fails or is
canceled (`:failed`), or `:timeout_ms` passes (`:timeout`, with `task_id`).

---

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