You can insert, update, read, and delete static cards using simple
REST APIs. In addition, you can attach objects to a static card, such
as a location or media.

## How they work

Static cards reside to the right of the Glass clock by default and display information
relevant to the user at the time of delivery. However, they do not require
immediate attention like [live cards](https://developers.google.com/glass/develop/gdk/live-cards)
and users can choose to read or act on the card
at their own leisure.

![](https://developers.google.com/static/glass/images/diagrams/static-card-timeline.png)

When Glassware inserts static cards into the timeline, Glass may play a notification
sound to alert users. All previous static cards also shift to the right
and disappear from the timeline after 7 days or when 200 cards are newer.

## When to use them

Static cards are great for delivering
[periodic notifications](https://developers.google.com/glass/design/patterns#periodic_notifications)
to users as important things happen.
For example, a news delivery service that
sends the top news stories as they happen. Mirror API static cards
can also start [live cards](https://developers.google.com/glass/develop/gdk/live-cards) or
[immersions](https://developers.google.com/glass/develop/gdk/immersions) through the
[`OPEN_URI`](https://developers.google.com/glass/v1/reference/timeline/insert#menuItems.action)
menu item. This allows you to create hybrid interactions that utilize
static cards as notifications and a live card or immersion for
a more interactive experience.

> [!NOTE]
> **Note:** Because timeline cards are central to the user experience, understand the [best practices](https://developers.google.com/glass/distribute/best-practices) before creating timeline items.

For a full list of possible operations for timeline items, see the [reference
documentation](https://developers.google.com/glass/v1/reference/timeline).

## Inserting static cards

To [insert](https://developers.google.com/glass/v1/reference/timeline/insert) static cards (timeline items), POST a
[JSON representation of a timeline item](https://developers.google.com/glass/v1/reference/timeline) to
the REST endpoint.

> [!NOTE]
> **Note:** Timeline items last for seven days on a user's Glass and are deleted from Google's servers if not updated for ten days.

Most of the fields in a timeline item are optional. In its simplest form,
a timeline item contains only a short text message, like in this example:

### Raw HTTP

    POST /mirror/v1/timeline HTTP/1.1
    Host: www.googleapis.com
    Authorization: Bearer {auth token}
    Content-Type: application/json
    Content-Length: 26

    { "text": "Hello world" }

### Java

    TimelineItem timelineItem = new TimelineItem();
    timelineItem.setText("Hello world");
    service.timeline().insert(timelineItem).execute();

### Python

    timeline_item = {'text': 'Hello world'}
    service.timeline().insert(body=timeline_item).execute()

On success, you receive a `201 Created` response code with a
full copy of the created item. For the previous example, a successful response
might look like this:

### Raw HTTP

    HTTP/1.1 201 Created
    Date: Tue, 25 Sep 2012 23:30:11 GMT
    Content-Type: application/json
    Content-Length: 303

    {
     "kind": "glass#timelineItem",
     "id": "1234567890",
     "selfLink": "https://www.googleapis.com/mirror/v1/timeline/1234567890",
     "created": "2012-09-25T23:28:43.192Z",
     "updated": "2012-09-25T23:28:43.192Z",
     "etag": "\"G5BI0RWvj-0jWdBrdWrPZV7xPKw/t25selcGS3uDEVT6FB09hAG-QQ\"",
     "text": "Hello world"
    }

The inserted item that would appear in the user's timeline looks like this:

![](https://developers.google.com/static/glass/images/glass-screens/hello_world_320.png)

### Inserting a timeline item with an attachment

A picture is worth a thousand words, which is a lot more than you can fit into a
timeline item. To this end, you can also attach images and video
to a timeline item. Here's an example of how to insert a timeline item with a
photo attachment:

### Raw HTTP

    POST /upload/mirror/v1/timeline HTTP/1.1
    Host: www.googleapis.com
    Authorization: Bearer {auth token}
    Content-Type: multipart/related; boundary="mymultipartboundary"
    Content-Length: {length}

    --mymultipartboundary
    Content-Type: application/json; charset=UTF-8

    { "text": "A solar eclipse of Saturn. Earth is also in this photo. Can you find it?" }
    --mymultipartboundary
    Content-Type: image/jpeg
    Content-Transfer-Encoding: binary

    [binary image data]
    --mymultipartboundary--

### Java

    TimelineItem timelineItem = new TimelineItem();
    timelineItem.setText("Hello world");
    InputStreamContent mediaContent = new InputStreamContent(contentType, attachment);
    service.timeline().insert(timelineItem, mediaContent).execute();

### Python

    timeline_item = {'text': 'Hello world'}
    media_body = MediaIoBaseUpload(
        io.BytesIO(attachment), mimetype=content_type, resumable=True)
    service.timeline().insert(body=timeline_item, media_body=media_body).execute()

A timeline item with an attached image looks something like this on Glass:

![](https://developers.google.com/static/glass/images/glass-screens/photo_attach_saturn_640.png)

> [!NOTE]
> **Note:** At a low level, attachments are uploaded using [HTTP Multipart](http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html). Google APIs client libraries make this easy using [media upload](https://developers.google.com/glass/media-upload).

### Attaching video

If you are attaching video files to your timeline items, we recommend that you
stream the video instead of uploading the entire payload at once.
The Google Mirror API supports streaming with HTTP live streaming,
progressive download, and the real time streaming protocol (RTSP).
RTSP is frequently blocked by firewalls, so use the other options when
possible.

To stream video, use the [`PLAY_VIDEO`](https://developers.google.com/glass/reference/timeline#menuItems.action)
built-in menu item and specify the video's URL to be the menu item's
[`payload`](https://developers.google.com/glass/reference/timeline#menuItems.payload). See
[Adding built-in menu items](https://developers.google.com/glass/develop/mirror/static-cards#built-in-menu-items) and
[supported media formats](http://developer.android.com/guide/appendix/media-formats.html)
for more information.

> [!NOTE]
> **Note:** Attaching a streaming video URL with a `content-type` of `video/vnd.google-glass.stream-url` is deprecated. Use the `PLAY_VIDEO` built-in menu item instead.

## Paginating

You can paginate timeline items that do not fit on a single timeline card,
but should otherwise be associated with the same card. Paginated
items all share the same [`timeline.id`](https://developers.google.com/glass/v1/reference/timeline#id) and therefore have the
same set of menu items. When a user taps a paginated timeline item, a
**Read more** menu item appears.

Glass automatically paginates timeline items that display
[`text`](https://developers.google.com/glass/v1/reference/timeline#text). To have Glass automatically
paginate [`html`](https://developers.google.com/glass/v1/reference/timeline#html), use the `article`
tag with its class property set to `auto-paginate` like in the following example:

    <article class="auto-paginate">
     <h3>Very long list</h3>
     <ul>
       <li>First item</li>
       <li>Second item</li>
       <li>Third item</li>
       <li>Fourth item</li>
       <li>Fifth item</li>
       <li>Sixth item</li>
       <li>...</li>
     </ul>
    <article>

> [!NOTE]
> **Note:** The `section` tag is currently not supported when using the `auto-paginate` class.

To paginate manually, use the `article` tag for the content
that you want to display on each card. Glass displays the contents of each
`article` tag in a separate sub-timeline card. For example, you can create a
paginated timeline item with the following HTML:

    <article>
     <section>
       <p>First page</p>
     </section>
    </article>

    <article>
     <section>
       <p>Second page</p>
     </section>
    </article>

    <article>
     <section>
       <p>Third page</p>
     </section>
    </article>

By default, the paginated timeline item's first card is displayed as the
cover card and is again displayed when the user selects the **Read more**
menu item. To prevent the first card from appearing again after tapping
**Read more** , you can specify the `cover-only` CSS class for the first
`<article>` tag:

    <article class="cover-only">
    ...

The `cover-only` class also supports auto-paginated timeline items:

    <article class="auto-paginate cover-only">
    ...

## Bundling

Bundling allows you to group related but distinct items together, like for
individual messages in an email thread. Bundles have a main cover card that a
user taps to display a sub-timeline that contains the other cards in the bundle.
Bundles are distinguished from normal timeline cards by a corner fold in the upper
right corner of the bundle's cover card.

To bundle timeline items, create them with the same value for
[`bundleId`](https://developers.google.com/glass/v1/reference/timeline#bundleId). The most recently added
item is the bundle's cover card.

> [!NOTE]
> **Note:** To set a specific timeline item as the bundle cover card, set its [`isBundleCover`](https://developers.google.com/glass/v1/reference/timeline#isBundleCover) property to `true`.

The following images show a bundle
cover card with the corner fold at the top right corner and two bundled
cards below it.

![](https://developers.google.com/static/glass/images/glass-screens/hybrid_bundle_flowers_1_320.jpg)

![](https://developers.google.com/static/glass/images/glass-screens/hybrid_bundle_flowers_2_320.jpg)
![](https://developers.google.com/static/glass/images/glass-screens/hybrid_bundle_flowers_3_320.jpg)

## Reading timeline items

Your service can access all timeline items that it created, and all timeline
items that were shared with it. Here's how to
[list](https://developers.google.com/glass/v1/reference/timeline/list) the timeline items that are
visible to your service.

### Raw HTTP

    GET /mirror/v1/timeline HTTP/1.1
    Host: www.googleapis.com
    Authorization: Bearer {auth token}

### Java

    TimelineItem timelineItem = new TimelineItem();
    service.timeline().list().execute();

### Python

    service.timeline().list().execute()

You can use other REST operations to [get](https://developers.google.com/glass/v1/reference/timeline/get),
[update](https://developers.google.com/glass/reference/timeline/update) and
[delete](https://developers.google.com/glass/v1/reference/timeline/delete) timeline items.

### Accessing attachments

You can access attachments to a timeline item through
an array property named [`attachments`](https://developers.google.com/glass/v1/reference/timeline#attachments).
You can then obtain the binary data of an attachment through the
[`contentUrl`](https://developers.google.com/glass/v1/reference/timeline#attachments.contentUrl)
property of the attachment or with the
[attachments endpoint](https://developers.google.com/glass/v1/reference/timeline/attachments/get).

> [!NOTE]
> **Note:** The attachment content is protected by OAuth 2.0, just like other calls to the API endpoints. Google API client libraries provide access to the binary content of attachments using the media download feature.

### Raw HTTP

    GET /mirror/v1/timeline/{itemId}/attachments/{attachmentId} HTTP/1.1
    Host: www.googleapis.com
    Authorization: Bearer {auth token}

### Java

    TimelineItem item = service.timeline().get(itemId).execute();
    String attachmentId = item.getAttachments().get(0).getId();
    service.attachments().get(itemId, attachmentId).executeAsInputStream();

## Creating menu items

Menu items allow users to request actions that are related to the timeline card,
and come in two types: built-in menu items and custom menu items.

Built-in menu items provide access to special functionalities provided by
Glass, such as reading a timeline card aloud, navigating to a
location, sharing an image, or replying to a message:

![](https://developers.google.com/static/glass/images/glass-screens/sms_inbound_640.jpg)

Custom menu items allow your application to expose behavior that is specific
to your Glassware, and you can also provide a menu item icon to match your
branding.

### Adding built-in menu items

You can add built-in menu items to your timeline items by populating the
[`menuItems array`](https://developers.google.com/glass/v1/reference/timeline#menuItems) when you insert them.
To use a built-in menu item, you only need to populate the
[`action`](https://developers.google.com/glass/v1/reference/timeline#menuItems.action) of each `menuItem`.

> [!NOTE]
> **Note:** When using the `REPLY` or `REPLY_ALL` built-in menu item, do not require users to speak a limited set of options, such as possible moves in a game or commands for a service. These menu items are intended to capture free form voice input.

### Raw HTTP

    HTTP/1.1 201 Created
    Date: Tue, 25 Sep 2012 23:30:11 GMT
    Content-Type: application/json
    Content-Length: 303

    {
      "text": "Hello world",
      "menuItems": [
        {
          "action": "REPLY"
        }
      ]
    }

> [!NOTE]
> **Note:** The [reference documentation](https://developers.google.com/glass/v1/reference/timeline#menuItems.action) contains a detailed description of the available built in actions.

### Defining custom menu items

If built-in menu items don't work for you, you can create custom menu items with your
own actions by doing the following when inserting or updating a timeline item:

- Specify `CUSTOM` for `menuItem.action`.
- Specify a `menuItem.id`. When users tap the custom menu item, your Glassware receives a notification with `menuItem.id` populated. This allows you to determine the source of the notification.
- Specify `menuItem.values` to add an `iconUrl` and `displayName` that appears on Glass. Point to a 50 x 50 PNG image that is white in color with a transparent background for the `iconUrl`.
- Specify a `displayTime`. If you do not specify a `displayTime`, the timeline item
  moves to the front of the timeline whenever users tap the custom menu item.

### Raw HTTP

    HTTP/1.1 201 Created
    Date: Tue, 25 Sep 2012 23:30:11 GMT
    Content-Type: application/json
    Content-Length: 303

    {
      "text": "Hello world",
      "displayTime": "2013-08-08T22:47:31-07:00",
      "menuItems": [
        {
          "action": "CUSTOM",
          "id": "complete"
          "values": [{
            "displayName": "Complete",
            "iconUrl": "http://example.com/icons/complete.png"
          }]
        }
      ]
    }

### Allowing users to pin your timeline card

You can create a menu item that lets your users pin the timeline card,
which permanently displays the timeline card to the left of the main
clock card. Users can unpin the card as well, by using the same menu
item.

The pinning menu item is a built-in menu item, so all you need to do is provide the `TOGGLE_PINNED`
[`action`](https://developers.google.com/glass/v1/reference/timeline#menuItems.action) for a `menuItem`.

### Raw HTTP

    HTTP/1.1 201 Created
    Date: Tue, 25 Sep 2012 23:30:11 GMT
    Content-Type: application/json
    Content-Length: 303

    {
      "text": "You can pin or unpin this card.",
     "menuItems": [
        {
          "action": "TOGGLE_PINNED"
        }
      ...
     ]
    }

## Subscriptions

The Mirror API allows you to
[subscribe to notifications](https://developers.google.com/glass/reference/subscriptions/insert)
that are sent when the user takes specific actions on a
[Timeline Item](https://developers.google.com/glass/reference/timeline) or when the user location
has been updated. When you subscribe to a notification, you
provide a callback URL that processes the notification.

> [!NOTE]
> **Note:** In production, your callback URL must support SSL. For development purposes, we provide an [subscription proxy server](https://developers.google.com/glass/subscription-proxy) that can forward notifications to your non-SSL development server.

### Receiving notifications

A notification from the Mirror API is sent as a `POST` request to the
subscribed endpoint containing a `JSON` request body.

### Raw HTTP

    {
      "collection": "timeline",
      "itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
      "operation": "UPDATE",
      "userToken": "harold_penguin",
      "verifyToken": "random_hash_to_verify_referer",
      "userActions": [
        {
          "type": "<TYPE>",
          "payload": "<PAYLOAD>"
        }
      ]
    }

### Java

    import com.google.api.client.json.JsonFactory;
    import com.google.api.client.json.jackson.JacksonFactory;
    import com.google.api.services.mirror.model.Notification;

    import java.io.IOException;
    import java.io.InputStream;
    // ...

    public class MyClass {
      // ...

      /**
        * Parse a request body into a Notification object.
        *
        * @param requestBody The notification payload sent by the Mirror API.
        * @return Parsed notification payload if successful, {@code null} otherwise.
        */
      static Notification parseNotification(InputStream requestBody) {
        try {
          JsonFactory jsonFactory = new JacksonFactory();

          return jsonFactory.fromInputStream(requetBody, Notification.class);
        } catch (IOException e) {
          System.out.println("An error occurred: " + e);
          return null;
        }
      }

      // ...
    }

### Python

    import json

    def parse_notification(request_body):
      """Parse a request body into a notification dict.

      Params:
        request_body: The notification payload sent by the Mirror API as a string.
      Returns:
        Dict representing the notification payload.
      """
      return json.load(request_body)

Your service must respond to the API with a `200 OK` HTTP status
code if no error occurred.
If your service responds with an error code, the Mirror API might
try to resend the notification to your service.

> [!NOTE]
> **Note:** The connection will time out after 10 seconds. If a long process is required, respond right away and do the process in another thread.

### Notification types

The Mirror API sends a different notification payload for different events.

#### Reply

The user has replied to your timeline item using the built-in `REPLY`
menu item:

    {
      "collection": "timeline",
      "itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
      "operation": "INSERT",
      "userToken": "harold_penguin",
      "verifyToken": "random_hash_to_verify_referer",
      "userActions": [
        {
          "type": "REPLY"
        }
      ]
    }

The `itemId` attribute is set to the `ID` of the item containing:

- `inReplyTo` attribute set to the `ID` of the timeline item it is a reply to.
- `text` attribute set to the text transcription.
- `recipients` attribute set to the `creator` of the timeline item it is a reply to, if it exists.

Example:

    {
      "kind": "glass#timelineItem",
      "id": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
      "inReplyTo": "3236e5b0-b282-4e00-9d7b-6b80e2f47f3d",
      "text": "This is a text reply",
      "recipients": [
        {
          "id": "CREATOR_ID",
          "displayName": "CREATOR_DISPLAY_NAME",
          "imageUrls": [
            "CREATOR_IMAGE_URL"
          ]
        }
      ]
    }

#### Delete

The user has deleted a timeline item:

    {
      "collection": "timeline",
      "itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
      "operation": "DELETE",
      "userToken": "harold_penguin",
      "verifyToken": "random_hash_to_verify_referer",
      "userActions": [
        {
          "type": "DELETE"
        }
      ]
    }

The `itemId` attribute is set to the ID of the deleted
item. The item no longer contains metadata other than its ID and the
`isDeleted` property.

> [!NOTE]
> **Note:** If the user deletes an item from their timeline, it's recommended that you delete this content from your systems too.

### Custom menu item selected

The user has selected a
[custom menu item](https://developers.google.com/glass/develop/mirror/static-cards#defining_custom_menu_items)
set by your service:

    {
      "collection": "timeline",
      "itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
      "operation": "UPDATE",
      "userToken": "harold_penguin",
      "userActions": [
        {
          "type": "CUSTOM",
          "payload": "PING"
        }
      ]
    }

The `itemId` attribute is set to the ID of the menu item that
the user selected.

The `userActions` array contains the list of custom actions
that the user took on this item. Your service should handle those
actions accordingly.

#### Location update

A new location is available for the current user:

    {
      "collection": "locations",
      "itemId": "latest",
      "operation": "UPDATE",
      "userToken": "harold_penguin",
      "verifyToken": "random_hash_to_verify_referer"
    }

When your Glassware receives a location update, send a
request to the [glass.locations.get](https://developers.google.com/glass/reference/locations/get)
endpoint to retrieve the latest known location. Your Glassware
receives location updates every ten minutes.

> [!NOTE]
> **Note:** Retrieving location information requires the `https://www.googleapis.com/auth/glass.location` scope.

#### Voice command

Your user has activated a voice command, for example:
"Ok Glass, take a note, Cat Stream, Chipotle's birthday is
tomorrow". The following notification is sent to your
Glassware:

    {
      "collection": "timeline",
      "operation": "INSERT",
      "userToken": "chipotle's_owner",
      "verifyToken": "mew mew mew",
      "itemId": "<ITEM_ID>",
      "userActions": [
        {"type": "LAUNCH"}
      ]
    }

This notification is distinguished from other notifications by the `LAUNCH` value
in the [`userActions`](https://developers.google.com/glass/reference/subscriptions#notification.userActions) property.

You can then use the value in `itemId` to fetch the timeline item:

    {
      "id": "<ITEM_ID>",
      "text": "Chipotle's birthday is tomorrow",
      "recipients": [
        {"id": "CAT_STREAM"}
      ]
    }

The `recipients` property contains the `id` of the contact that represents the
voice command used.