Documentation

Request Formats

New
Browse documentation

Published 2026-08-13. V2 is a prerelease; the web core is release-gated and other previews are named separately. What beta means.

Read this page as HTML, Markdown, or structured JSON—or open the published Markdown with an AI assistant. Gemini receives the prompt through your clipboard because its signed-out page does not reliably prefill URL text; paste when the new tab opens. External assistants need the public site URL.

1.4.1
Unavailable in this version
1.5
Unavailable in this version

Request formats

Released in 2.0.0-beta.5: bounded CBOR and bidirectional authenticated COSE are available as opt-in request and response formats.

Declare every request representation an action actually accepts. Amber checks the incoming Content-Type before parsing and returns 415 when the media type is outside the contract.

Where the examples go

  • Put content_type and field declarations inside a contract under src/schemas/.
  • Put the COSE provider in config/wire_format.cr and require it from config/application.cr.
  • The JSON, form, and XML documents shown here are HTTP request bodies sent to the bound action; they are not files to add to the application.
  • Run key-generation and request commands from the application root beside shard.yml.

Supported request media types

Media type Parser
application/json or text/json JSON object
application/xml, text/xml, or application/xhtml+xml XML document
application/x-www-form-urlencoded Form fields, including bracket notation
multipart/form-data Form fields and uploaded-file metadata
application/cbor Bounded deterministic CBOR object
application/cose COSE Encrypt0 containing the CBOR object

CSV, Protocol Buffers, and MessagePack are not built-in Amber V2 schema formats. Applications may integrate them separately, but public contracts should not claim framework support that is not present.

Declare one or more formats

File: src/schemas/create_pet_schema.cr.

Crystal
class CreatePetSchema < Amber::Schema::Definition
  content_type "application/json"

  field :name, String, required: true
  field :species, String, required: true
end

To support the same JSON-compatible object through JSON, CBOR, and encrypted COSE:

Crystal
content_type "application/json", "application/cbor", "application/cose"

The controller binding is unchanged. Amber chooses the request parser from Content-Type and the schema-aware response format from Accept.

JSON

File: the request body sent to the bound action — not a Crystal source file.

JSON
{
  "name": "Mochi",
  "species": "cat",
  "age": 3,
  "tags": ["indoor", "friendly"]
}

The top-level document must be an object. Malformed JSON and non-finite numbers fail before field validation.

URL-encoded forms

File: src/schemas/registration_schema.cr.

Crystal
class RegistrationSchema < Amber::Schema::Definition
  content_type "application/x-www-form-urlencoded"

  field :name, String, required: true
  field :email, String, required: true, format: "email"
  field :age, Int32, min: 13
  field :tags, Array(String)
end

Example HTTP body:

Output
name=Alex&email=alex%40example.com&age=28&tags[]=crystal&tags[]=amber

Amber reuses the router's cached form parse when method override or another request step has already inspected the body. It does not consume the form once for routing and then hand an empty stream to the schema.

Multipart forms and files

File: src/schemas/photo_upload_schema.cr.

Crystal
class PhotoUploadSchema < Amber::Schema::Definition
  content_type "multipart/form-data"

  field :title, String, required: true
  field :photo, Hash(String, JSON::Any),
    required: true,
    max_size: 5_000_000,
    allowed_types: ["image/jpeg", "image/png", "image/webp"],
    allowed_extensions: ["jpg", "jpeg", "png", "webp"]
end

The multipart parser exposes uploaded-file metadata to the schema and reuses Amber's cached multipart fields and files. Validation at this layer is an admission check; use the uploads guide for storage ownership, image processing, and serving policy.

XML

File: src/schemas/create_event_schema.cr.

Crystal
class CreateEventSchema < Amber::Schema::Definition
  content_type "application/xml"

  field :name, String, required: true
  field :starts_at, Time, required: true
end

Example HTTP body:

Xml
<?xml version="1.0" encoding="UTF-8"?>
<event>
  <name>Amber meetup</name>
  <starts_at>2026-09-01T18:00:00Z</starts_at>
</event>

XML is available for inbound schema parsing. The schema-aware respond_with encoder currently emits JSON, CBOR, or COSE; do not declare automatic XML response encoding unless the controller implements and tests that response path explicitly.

Deterministic CBOR

application/cbor carries the JSON-compatible contract in a compact binary form. Amber's decoder is bounded to:

  • 1 MiB per document;
  • 32 levels of nesting; and
  • 16,384 collection items.

It rejects indefinite lengths, duplicate map keys, invalid UTF-8, trailing bytes, byte strings where a JSON-compatible value is required, and non-finite numbers. Typed schema validation runs after decoding exactly as it does for JSON.

Authenticated COSE Encrypt0

application/cose carries that deterministic CBOR object in a tagged COSE Encrypt0 envelope using ChaCha20-Poly1305. Amber authenticates and decrypts the request, validates the object, then can encode, authenticate, and encrypt the response with a fresh 96-bit nonce.

There is no built-in development key.

1. Generate a 32-byte deployment key

Run from: the application root.

Terminal
openssl rand -base64 32

Store the result in the deployment secret manager as AMBER_WIRE_KEY. Store a non-empty identifier such as 2026-08 as AMBER_WIRE_KEY_ID. Do not commit either value.

2. Configure the provider

File: config/wire_format.cr — create this file.

Crystal
Amber::Schema::COSE.configure(
  Amber::Schema::COSE::KeyProvider.from_env!
)

File: config/application.cr — require it after Amber and before controller files.

Crystal
require "amber"
require "./wire_format"
require "../src/controllers/application_controller"
require "../src/controllers/**"
require "./routes"

The key provider selects keys by COSE key ID and can retain a grace key during rotation. A COSE request without configuration returns 503. Authentication, unknown-key, malformed-envelope, and replay-policy behavior should be covered by application tests before production use.

Response negotiation

Declare formats on the response schema too:

Crystal
class PetResponseSchema < Amber::Schema::Definition
  content_type "application/json", "application/cbor", "application/cose"

  field :id, Int64, required: true
  field :name, String, required: true
end
  • Accept: application/json returns JSON.
  • Accept: application/cbor returns deterministic CBOR.
  • Accept: application/cose returns authenticated COSE Encrypt0 containing deterministic CBOR.
  • An undeclared representation returns 406.

The X-Amber-Wire-Format header describes Amber's selected COSE profile. It is informational and never replaces client-side authentication of the message.