A struct representing the response from a push notification request.
Fields
:provider- The provider used (:apnsor:fcm):status- The result status (see below):id- Provider-specific message ID (if available):reason- Error reason string (if failed):raw- Raw response body (for debugging)
Status Values
:sent- Message was successfully sent:invalid_token- Device token is invalid or expired:expired_token- Device token has expired:unregistered- Device is no longer registered:payload_too_large- Payload exceeds size limit:rate_limited- Too many requests, try again later:server_error- Provider server error:connection_error- Network/connection failure:circuit_open- Circuit breaker is open, provider temporarily blocked:invalid_request- Missing or invalid request parameters (e.g., no:topicfor APNS):auth_error- Authentication failure (e.g., invalid private key, JWT generation failed):unknown_error- Unrecognized error
Summary
Functions
Maps an APNS error reason to a status atom.
Creates an error response.
Creates an error response with raw data.
Creates an error response with raw data and retry_after value.
Extracts the FCM-specific error code from a decoded error response body.
Maps an FCM error code to a status atom.
Returns true if the error is retryable.
Returns true if the token should be removed from the database.
Creates a successful response.
Returns true if the response indicates success.
Types
@type status() ::
:sent
| :invalid_token
| :expired_token
| :unregistered
| :payload_too_large
| :rate_limited
| :server_error
| :connection_error
| :circuit_open
| :provider_disabled
| :invalid_request
| :auth_error
| :unknown_error
@type t() :: %PushX.Response{ id: String.t() | nil, provider: :apns | :fcm | :unknown, raw: any(), reason: String.t() | nil, retry_after: non_neg_integer() | nil, status: status() }
Functions
Maps an APNS error reason to a status atom.
@spec error( provider :: :apns | :fcm | :unknown, status :: status(), reason :: String.t() | nil ) :: t()
Creates an error response.
@spec error( provider :: :apns | :fcm | :unknown, status :: status(), reason :: String.t() | nil, raw :: any() ) :: t()
Creates an error response with raw data.
@spec error( provider :: :apns | :fcm | :unknown, status :: status(), reason :: String.t() | nil, raw :: any(), retry_after :: non_neg_integer() | nil ) :: t()
Creates an error response with raw data and retry_after value.
Extracts the FCM-specific error code from a decoded error response body.
The FCM v1 API wraps the real error code (e.g., "UNREGISTERED") inside the
details array with @type "type.googleapis.com/google.firebase.fcm.v1.FcmError",
while the top-level "status" contains a generic gRPC code (e.g., "NOT_FOUND").
Returns nil if no FCM-specific error code is found in the details.
Examples
iex> body = %{"error" => %{"status" => "NOT_FOUND", "details" => [%{"@type" => "type.googleapis.com/google.firebase.fcm.v1.FcmError", "errorCode" => "UNREGISTERED"}]}}
iex> PushX.Response.extract_fcm_error_code(body)
"UNREGISTERED"
iex> PushX.Response.extract_fcm_error_code(%{"error" => %{"status" => "INTERNAL"}})
nil
Maps an FCM error code to a status atom.
See: https://firebase.google.com/docs/reference/fcm/rest/v1/ErrorCode
Returns true if the error is retryable.
Retryable errors:
:connection_error- Network/connection failure:rate_limited- Too many requests (with backoff):server_error- Provider server error (5xx)
Examples
iex> PushX.Response.error(:fcm, :server_error, "Internal") |> PushX.Response.retryable?()
true
iex> PushX.Response.error(:apns, :invalid_token, "bad") |> PushX.Response.retryable?()
false
Returns true if the token should be removed from the database.
Examples
iex> PushX.Response.error(:apns, :invalid_token, "BadDeviceToken") |> PushX.Response.should_remove_token?()
true
iex> PushX.Response.error(:fcm, :server_error, "Internal") |> PushX.Response.should_remove_token?()
false
Creates a successful response.
Returns true if the response indicates success.
Examples
iex> PushX.Response.success(:apns, "id-123") |> PushX.Response.success?()
true
iex> PushX.Response.error(:fcm, :invalid_token, "bad") |> PushX.Response.success?()
false