What's the best way of handling error?

It is not a common pattern to use try and catch clause… and Yes, it’s a scoping problem.

But before solving the scope, You might want to refactor a little bit.

If You think something can go wrong, You could use the “soft” Poison.encode, instead of Poison.encode!, which raise an error.

You just have to deal with {:ok, result}, or {:error, reason}.

And because You have multiple conditions, I would be tempted to use with.

with {:ok, attrs} <- Poison.encode(params),
  {:ok, response} <- HTTPoison.post(url, attrs, @content_type),
  {:ok, body} <- Poison.decode(response.body) do
  render("show.json", body)
else
  ... # Manage errors here!
end

I would always try to avoid try and catch