Protocol specification for https.onCall

An https.onCall trigger for Cloud Functions is an HTTPS trigger with a specific format for the request and response. This section provides a specification for the HTTPS request and response formats used by the client SDKs to implement the API. This information may be useful to you if your requirements can't be met using the Android, Apple platforms, or web SDKs.

Request format: headers

The HTTP request to a callable trigger endpoint must be a POST with the following headers:

  • Required: Content-Type: application/json
    • An optional ; charset=utf-8 is allowed.
  • Optional: Authorization: Bearer <token>
    • A Firebase Authentication user ID token for the logged-in user making the request. The backend automatically verifies this token and makes it available in the handler's context. If the token is not valid, the request is rejected.
  • Optional: Firebase-Instance-ID-Token: <iid>
    • The FCM registration token from the Firebase client SDK. This must be a string. This is available in the handler's context. It is used for targeting push notifications.
  • Optional: X-Firebase-AppCheck: <token>
    • The Firebase App Check token provided by the client app making the request. The backend automatically verifies this token and decodes it, injecting the appId in the handler's context. If the token cannot be verified, the request is rejected. (Available for SDK >=3.14.0)

If any other headers are included, the request is rejected, as described in the response documentation below.

Note: In JavaScript clients, these requests trigger a CORS OPTIONS preflight, because:

  • application/json is not allowed. It must be text/plain or application/x-www-form-urlencoded.
  • The Authorization header is not a CORS-safelisted request-header.
  • Other headers are similarly not allowed.

The callable trigger automatically handles these OPTIONS requests.

Request body

The body of the HTTP request should be a JSON object with any of the following fields:

  • Required: data - The argument passed to the function. This can be any valid JSON value. This is automatically decoded into native JavaScript types according to the serialization format described below.

If there are any other fields present in the request, the backend considers the request malformed, and it is rejected.

Response format: status codes

There are several cases that could result in different HTTP status codes and string status codes for errors in the response.

  1. In the case of an HTTP error before the client trigger is invoked, the response is not handled as a client function. For example, if a client tries to invoke a non-existent function, it receives a 404 Not Found response.

  2. If the client trigger is invoked, but the request is in the wrong format, such as not being JSON, having invalid fields, or missing the data field, the request is rejected with 400 Bad Request, with an error code of INVALID_ARGUMENT.

  3. If the auth token supplied in the request is invalid, the request is rejected with 401 Unauthorized, with an error code of UNAUTHENTICATED.

  4. If the FCM registration token supplied in the request is invalid, the behavior is undefined. The token is not checked on every request, except when it is used to send a push notification with FCM.

  5. If the callable trigger is invoked, but fails with an unhandled exception or returns a failed promise, the request is rejected with 500 Internal Server Error, with an error code of INTERNAL. This prevents coding errors from accidentally being exposed to end users.

  6. If the callable is invoked and returns an explicit error condition using the API provided for callable functions, then the request fails. The HTTP status code returned is based on the official mapping of error status to HTTP status, as defined in code.proto. The specific error code, message, and details returned are encoded in the response body as detailed below. This means that if the function returns an explicit error with status OK, then the response has status 200 OK, but the error field is set in the response.

  7. If the client trigger is successful, the response status is 200 OK.

Response format: headers

The response has the following headers:

  • Content-Type: application/json
  • An optional ; charset=utf-8 is allowed.

Response body

The response from a client endpoint is always a JSON object. At a minimum it contains either result or error, along with any optional fields. If the response is not a JSON object, or does not contain data or error, the client SDK should treat the request as failed with Google error code INTERNAL (13).

  • error - If this field is present, then the request is considered failed, regardless of the HTTP status code or whether data is also present. The value of this field should be a JSON object in the standard Google Cloud HTTP Mapping format for errors, with fields for status, message, and (optionally) details. The code field shall not be included. If the status field is unset, or is an invalid value, the client should treat the status as INTERNAL, in accordance with code.proto. If details is present, it is included in any user info attached to the error in the client SDK, if applicable.
    Note: The details field here is a user-supplied value. It is not necessarily a list of values keyed by proto type as in the Google Status format.
  • result - The value returned by the function. This can be any valid JSON value. The firebase-functions SDK automatically encodes the value returned by the user into this JSON format. The client SDKs automatically decode these params into native types according to the serialization format described below.

If other fields are present, they should be ignored.

Serialization

The serialization format for arbitrary data payloads is the same for both the request and the response.

For platform consistency, these are encoded in JSON as though they are the value of an Any field in a proto3 protocol buffer, using the standard JSON mapping. Values of simple types such as null, int, double, or string are encoded directly, and do not include their explicit type. So, a float and double are encoded the same way, and you may not know which is received on the other end of the call. For types that are not native to JSON, the typed proto3 encoding for the value is used. For more information, see the documentation for Any JSON encoding.

The following types are allowed:

  • null - null
  • int (signed or unsigned, up to 32 bit) - e.g. 3 or -30.
  • float - e.g. 3.14
  • double - e.g. 3.14
  • boolean - true or false
  • string - e.g. "hello world"
  • map - e.g. {"x": 3}
  • list - e.g. [1, 2, 3]
  • long (signed or unsigned, up to 64 bits) - [see below for details]

NaN and Infinity values for float and double are not supported.

Note that long is a special type not normally allowed in JSON, but is covered by the proto3 specification. For example, these are encoded as:

long

{
    '@type': 'type.googleapis.com/google.protobuf.Int64Value',
    'value': '-123456789123456'
}

unsigned long

{
    '@type': 'type.googleapis.com/google.protobuf.UInt64Value',
    'value': '123456789123456'
}

In general, the @type key should be considered reserved, and not used for maps passed in.

Because the type is not specified for simple types, some values will change type after passing over the wire. A float passed in becomes a double. A short becomes an int, and so on. In Android, both List and JSONArray are supported for list values. In those cases, passing in a JSONArray will yield a List.

If a map with an unknown @type field is deserialized, it is left as a map. This allows developers to add fields with new types to their return values without breaking older clients.

Code samples

The samples in this section illustrate how to encode the following:

  • A callable.call example in Swift
  • A success response for the call
  • A failure response for the call

Callable.call example in Swift to encode

callable.call([
    "aString": "some string",
    "anInt": 57,
    "aFloat": 1.23,
    "aLong": -123456789123456 as Int64
])

Request header:

Method: POST
Content-Type: application/json; charset=utf-8
Authorization: Bearer some-auth-token
Firebase-Instance-ID-Token: some-iid-token

Request body:

{
    "data": {
        "aString": "some string",
        "anInt": 57,
        "aFloat": 1.23,
        "aLong": {
            "@type": "type.googleapis.com/google.protobuf.Int64Value",
            "value": "-123456789123456"
        }
    }
}

Response to encode

return {
    "aString": "some string",
    "anInt": 57,
    "aFloat": 1.23
};

Successful response header:

200 OK
Content-Type: application/json; charset=utf-8

Successful response body:

{
    "response": {
        "aString": "some string",
        "anInt": 57,
        "aFloat": 1.23
    }
}

Failure response to encode

throw new HttpsError("unauthenticated", "Request had invalid credentials.", {
  "some-key": "some-value"
});

Failed response header:

401 UNAUTHENTICATED
Content-Type: application/json; charset=utf-8

Failed response body:

{
    "error": {
        "message": "Request had invalid credentials.",
        "status": "UNAUTHENTICATED",
        "details": {
            "some-key": "some-value"
        }
    }
}