├── .gitignore ├── Makefile ├── api-methods-examples ├── open311.services.getInfo.json ├── open311.incidents.getInfo.json ├── open311.incidents.report.json ├── open311.services.getList.json ├── open311.incidents.getStatuses.json ├── open311.incidents.search.json ├── open311.where.getList.json └── README.md ├── README.md ├── bin └── mk-api-docs.py ├── api-methods.json ├── api-methods.md └── api.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | docs: 2 | 3 | python ./bin/mk-api-docs.py ./api-methods.json 4 | 5 | clean: 6 | rm api-methods.md -------------------------------------------------------------------------------- /api-methods-examples/open311.services.getInfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "service": { 3 | "id": 1, 4 | "name": "...", 5 | "description": "..." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /api-methods-examples/open311.incidents.getInfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "incident": { 3 | "id": 999, 4 | "service_id": 2, 5 | "status_id": 1, 6 | "reported": "..." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /api-methods-examples/open311.incidents.report.json: -------------------------------------------------------------------------------- 1 | { 2 | "incident": { 3 | "id": 999, 4 | "service_id": 2, 5 | "status_id": 1, 6 | "reported": "..." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /api-methods-examples/open311.services.getList.json: -------------------------------------------------------------------------------- 1 | { 2 | "total": 3, 3 | "per_page": 100, 4 | "page": 1, 5 | "services": [ 6 | { "id": 1, "name": "..." }, 7 | { "id": 2, "name": "..." }, 8 | { "id": 3, "name": "..." } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /api-methods-examples/open311.incidents.getStatuses.json: -------------------------------------------------------------------------------- 1 | { 2 | "total": 3, 3 | "per_page": 100, 4 | "page": 1, 5 | "statuses": [ 6 | { "id": 1, "name": "open" }, 7 | { "id": 2, "name": "pending" }, 8 | { "id": 3, "name": "closed" } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /api-methods-examples/open311.incidents.search.json: -------------------------------------------------------------------------------- 1 | { 2 | "total": 2, 3 | "per_page": 100, 4 | "page": 1, 5 | "incidents": [ 6 | { "id": 999, "service_id": 2, "status_id": 1, "reported": "..." }, 7 | { "id": 23, "service_id": 3, "status_id": 1, "reported": "..." }, 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /api-methods-examples/open311.where.getList.json: -------------------------------------------------------------------------------- 1 | { 2 | "total": 3, 3 | "per_page": 100, 4 | "page": 1, 5 | "terms": [ 6 | { "prefix": "bbox", "description": "...", "example": "bbox:37.788,-122.344,37.857,-122.256" }, 7 | { "prefix": "near", "description": "...", "example": "near:37.804376,-122.271180" }, 8 | { "prefix": "zip", "description": "...", "example": "zip:94110" } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /api-methods-examples/README.md: -------------------------------------------------------------------------------- 1 | These are example JSON responses for the methods defined in api-methods.json. They are read by the bin/mk-api-docs.py program and are stored as separate files because the API spec is a JSON file and JSON doesn't allow unescaped newlines or tabs and it's just easier to keep the two (the spec and example responses) independent of one another. 2 | 3 | None of these examples are complete as of this writing (20111010). 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Open 311 Simple 2 | == 3 | 4 | Introduction 5 | -- 6 | 7 | This is a draft specification for a simplified Open 311 API. It is very much a 8 | work in progress. Comments, patches and (gentle) cluebats are encouraged. 9 | 10 | API 11 | -- 12 | 13 | The Open 311 Simple API is designed to do (3) things: 14 | 15 | * allow citizens to discover municipal services for which they can report an 16 | incident 17 | 18 | * report an incident (for a sevice) 19 | 20 | * inquire about the status of a reported incident 21 | 22 | Additionally, it should be possible for individual users to search for incident 23 | reports. A search my be scoped to an individual user; by geographic location; by 24 | service type or status; and so on. 25 | 26 | That's it, really. The API itself is short and simple, by design. One way to think about the API is that it doesn't do anything more than you might already be able to do over the telephone. As such editing incident reports or appending media items (photos, videos, etc.) are out of scope for the time being. There's nothing that would make it very hard to add either but in the interests of making the simplest _common_ platform that all cities can implement with a minimum of fuss they've been left out for the time being. 27 | 28 | As of this writing API methods for administrative functions are not included. It's not clear that they should be part of the _core_ Open311 Simple specification. For the sake of discussion a base administrative API should be limited to: 29 | 30 | * create and edit service types 31 | 32 | * create and edit status types 33 | 34 | * update incident reports 35 | 36 | * create and edit incident notes 37 | 38 | **Detailed notes and statements of bias are discussed in the [api.md](https://github.com/straup/open311-simple/blob/master/api.md) document.** 39 | 40 | There is also a [reference implementation](https://github.com/straup/open311-simple-app) built using [Flamework](https://github.com/straup/flamework) (read: PHP and MySQL). 41 | -------------------------------------------------------------------------------- /bin/mk-api-docs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os.path 4 | import json 5 | import sys 6 | import StringIO 7 | 8 | import logging 9 | logging.basicConfig(level=logging.DEBUG) 10 | 11 | # TO DO: import py-markdown and generate other kinds of docs at the same time 12 | 13 | if __name__ == '__main__': 14 | 15 | spec = sys.argv[1] 16 | 17 | path = os.path.abspath(spec) 18 | root = os.path.dirname(path) 19 | 20 | examples = os.path.join(root, 'api-methods-examples') 21 | 22 | fh = open(spec, 'r') 23 | spec = json.load(fh) 24 | 25 | out = StringIO.StringIO() 26 | 27 | out.write("API Methods\n") 28 | out.write("==\n\n") 29 | 30 | out.write("_This file is auto-generated using the [api-methods.json](https://github.com/straup/open311-simple/blob/master/api-methods.json) specification and the [mk-api-docs](https://github.com/straup/open311-simple/blob/master/bin/mk-api-docs.py) program and compliments the [general API notes](https://github.com/straup/open311-simple/blob/master/api.md)_.\n\n") 31 | 32 | """ 33 | First group and sort methods by 'class' 34 | """ 35 | 36 | classes = [] 37 | methods = {} 38 | 39 | for fq_method in spec['methods'].keys(): 40 | 41 | parts = fq_method.split(".") 42 | 43 | method_name = parts.pop() 44 | method_class = ".".join(parts) 45 | 46 | if not methods.get(method_class): 47 | methods[method_class] = [] 48 | classes.append(method_class) 49 | 50 | details = spec['methods'][fq_method] 51 | 52 | if not details['enabled']: 53 | logging.info("the '%s' method is disabled, skipping" % fq_method) 54 | continue 55 | 56 | if not details['documented']: 57 | logging.info("the '%s' method is enabled but undocumented, skipping" % fq_method) 58 | continue 59 | 60 | methods[method_class].append(fq_method) 61 | 62 | classes.sort() 63 | 64 | """ 65 | Okay. Go! 66 | """ 67 | 68 | for class_name in classes: 69 | 70 | class_methods = methods[class_name] 71 | 72 | # see above 73 | 74 | if len(class_methods) == 0: 75 | continue 76 | 77 | class_methods.sort() 78 | 79 | out.write("%s\n" % class_name) 80 | out.write("==\n\n") 81 | 82 | for fq_method in class_methods: 83 | 84 | details = spec['methods'][fq_method] 85 | 86 | out.write("%s\n" % fq_method) 87 | out.write("--\n\n") 88 | 89 | if details['requires_auth']: 90 | out.write("**This method requires authentication**\n\n") 91 | 92 | out.write("%s\n\n" % details['description']) 93 | 94 | """ 95 | method (HTTP) 96 | """ 97 | 98 | out.write("**Method**\n\n") 99 | 100 | out.write("[%s](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html)\n\n" % (details['method'])) 101 | 102 | """ 103 | method parameters 104 | """ 105 | 106 | out.write("**Parameters**\n\n") 107 | 108 | if details.get('parameters', False): 109 | 110 | for p in details['parameters']: 111 | 112 | # guh... 113 | name = p['name'].replace("_", "\_") 114 | desc = p['description'].replace("_", "\_") 115 | 116 | out.write("* **%s** - %s" % (name, desc)) 117 | 118 | if p['required']: 119 | out.write(" - _Required_") 120 | 121 | out.write("\n") 122 | 123 | if details.get('paginated', False): 124 | 125 | out.write("* **page** - The page of results to return. If this argument is omitted, it defaults to 1.\n") 126 | out.write("* **per_page** - Number of results to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is left to the discretion of individual cities.\n") 127 | 128 | out.write("* **format** - The encoding format for results. If this argument is omitted, it defaults to JSON\n") 129 | out.write("\n") 130 | 131 | """ 132 | method notes 133 | """ 134 | 135 | if details.get('notes', False): 136 | 137 | out.write("**Notes**\n\n") 138 | 139 | for n in details['notes']: 140 | out.write("* %s\n\n" % n) 141 | 142 | """ 143 | example 144 | """ 145 | 146 | out.write("**Example**\n\n") 147 | 148 | out.write("\t%s http://example.gov/open311-simple/?method=%s\n\n" % (details['method'], fq_method)) 149 | 150 | rsp = os.path.join(examples, "%s.json" % fq_method) 151 | 152 | if os.path.exists(rsp): 153 | 154 | fh = open(rsp, 'r') 155 | for ln in fh.readlines(): 156 | out.write("\t%s" % ln) 157 | 158 | out.write("\n") 159 | 160 | """ 161 | write to disk 162 | """ 163 | 164 | outfile = os.path.join(root, "api-methods.md") 165 | outfh = open(outfile, 'w') 166 | 167 | md = out.getvalue() 168 | outfh.write(md) 169 | outfh.close() 170 | 171 | logging.info("Wrote new documentation (as markdown) to %s" % outfile) 172 | -------------------------------------------------------------------------------- /api-methods.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "default_format": "json", 4 | 5 | "formats": [ 6 | "json" 7 | ], 8 | 9 | "methods": { 10 | 11 | "open311.services.getList": { 12 | "description": "Returns a list of services for which incidents may be reported. The types of services and their meaning are left to the discretion of individual cities.", 13 | "enabled": 1, 14 | "documented": 1, 15 | "requires_auth": 0, 16 | "paginated": 1, 17 | "method": "GET", 18 | "example" : "file://./api-methods-examples/open311.services.getList.json" 19 | }, 20 | 21 | "open311.services.getInfo": { 22 | "description": "Returns basic information (as included in the _open311.services.getList_ method) as well any additional details that may be relevant to the service.", 23 | "enabled": 1, 24 | "documented": 1, 25 | "requires_auth": 0, 26 | "method": "GET", 27 | "parameters": [ 28 | { "name": "service_id", "description": "A valid service_id to get information about.", "required": 1 } 29 | ], 30 | "example" : "file://./api-methods-examples/open311.services.getInfo.json" 31 | }, 32 | 33 | "open311.statuses.getList": { 34 | "description": "Return a list of valid statuses for incidents. The types of statuses and their meaning are left to the discretion of individual cities.", 35 | "enabled": 1, 36 | "documented": 1, 37 | "paginated": 1, 38 | "method": "GET", 39 | "requires_auth": 0, 40 | "example" : "file://./api-methods-examples/open311.incidents.getStatuses.json" 41 | }, 42 | 43 | "open311.incidents.report": { 44 | "description": "Report an incident for a given service. Returns a unique ID for the incident that may be used to call the _open311.incidents.status_ API method.", 45 | "enabled": 1, 46 | "documented": 1, 47 | "method": "POST", 48 | "requires_auth": 1, 49 | "parameters": [ 50 | { "name": "service_id", "description": "A valid service_id as defined by the city operating the Open 311 (Simple) API", "required": 1 }, 51 | { "name": "latitude", "description": "A valid WGS84 coordinate", "required": 1 }, 52 | { "name": "longitude", "description": "A valid WGS84 coordinate", "required": 1 }, 53 | { "name": "description", "description": "A free-form text field in which the user reporting the incident may leave additional notes.", "required": 0} 54 | ], 55 | "notes": [ 56 | "All dates should be passed in using the [W3C DateTime format](http://www.w3.org/TR/NOTE-datetime) format.", 57 | "All geographic data is expected to be using the unprojected [WGS84](http://spatialreference.org/ref/epsg/4326/) datum (read: plain old latitudes and longitudes)." 58 | ], 59 | "example" : "file://./api-methods-examples/open311.incidents.report.json" 60 | }, 61 | 62 | "open311.incidents.addNote": { 63 | "description": "Add an additional note to an incident report.", 64 | "enabled": 1, 65 | "documented": 1, 66 | "requires_auth": 1, 67 | "method": "POST", 68 | "parameters": [ 69 | { "name": "incident_id", "description": "The unique ID of the incident to add a note for.", "required": 1 }, 70 | { "name": "note", "description": "The body of the note to add to the incident report.", "required": 1 }, 71 | { "name": "public", "description": "Whether or not the note is publicly viewable. Default is false..", "required": 0 } 72 | ], 73 | "notes": [ 74 | ], 75 | "example" : "file://./api-methods-examples/open311.incidents.addNote" 76 | }, 77 | 78 | "open311.incidents.getInfo": { 79 | "description": "Get the detailed information for an incident report.", 80 | "enabled": 1, 81 | "documented": 1, 82 | "requires_auth": 0, 83 | "method": "GET", 84 | "parameters": [ 85 | { "name": "incident_id", "description": "The unique ID of the incident to get information about.", "required": 1 } 86 | ], 87 | "notes": [ 88 | "All dates are recorded using the [W3C DateTime format](http://www.w3.org/TR/NOTE-datetime) format.", 89 | "All geographic data is returned using the unprojected [WGS84](http://spatialreference.org/ref/epsg/4326/) datum (read: plain old latitudes and longitudes)." 90 | ], 91 | "example" : "file://./api-methods-examples/open311.incidents.getInfo" 92 | }, 93 | 94 | "open311.incidents.search": { 95 | "description": "Returns a list of incidents matching a search criteria as defined by the API request.", 96 | "enabled": 1, 97 | "documented": 1, 98 | "paginated": 1, 99 | "method": "GET", 100 | "requires_auth": 0, 101 | "parameters": [ 102 | { "name": "service_id", "description": "The unique ID of the service type to search for. Multiple services may be passed in as a comma-separated list.", "required": 0 }, 103 | { "name": "incident_id", "description": "The unique ID of the incident to search for. Multiple incidents may be passed in as a comma-separated list.", "required": 0 }, 104 | { "name": "status_id", "description": "The unique ID of a status type to search for. Multiple statuses may be passed in as a comma-separated list.", "required": 0 }, 105 | { "name": "created", "description": "The date or date range (see [api.md](https://github.com/straup/open311-simple/blob/master/api.md) for details) of when an incident was reported.", "required": 0 }, 106 | { "name": "modified", "description": "The date or date range (see [api.md](https://github.com/straup/open311-simple/blob/master/api.md) for details) of when an incident was last modified.", "required": 0 }, 107 | { "name": "where", "description": "A geopgraphic location or extent (see [api.md]((https://github.com/straup/open311-simple/blob/master/api.md) for details) for details) in which to scope the query.", "required": 0 } 108 | ], 109 | "notes": [ 110 | "All dates should be passed to the API (and returned in results) using the [W3C DateTime format](http://www.w3.org/TR/NOTE-datetime).", 111 | "All geographic data should be passed to the API using the unprojected [WGS84](http://spatialreference.org/ref/epsg/4326/) datum (read: plain old latitudes and longitudes).", 112 | "If called with a valid OAuth token and signature then the query will be scoped to the user associated with that token.", 113 | "Parameterless searches are not permitted. You must define at least one search criteria." 114 | ], 115 | "example": "file://./api-methods-examples/open311.incidents.search.json" 116 | }, 117 | 118 | "open311.where.getList": { 119 | "description": "Returns a list of geographic prefixes that may be used to query for incident reports using the 'open311.incidents.search' API method.", 120 | "enabled": 1, 121 | "documented": 1, 122 | "requires_auth": 0, 123 | "paginated": 1, 124 | "method": "GET", 125 | "example" : "file://./api-methods-examples/open311.where.getList.json" 126 | } 127 | 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /api-methods.md: -------------------------------------------------------------------------------- 1 | API Methods 2 | == 3 | 4 | _This file is auto-generated using the [api-methods.json](https://github.com/straup/open311-simple/blob/master/api-methods.json) specification and the [mk-api-docs](https://github.com/straup/open311-simple/blob/master/bin/mk-api-docs.py) program and compliments the [general API notes](https://github.com/straup/open311-simple/blob/master/api.md)_. 5 | 6 | open311.incidents 7 | == 8 | 9 | open311.incidents.addNote 10 | -- 11 | 12 | **This method requires authentication** 13 | 14 | Add an additional note to an incident report. 15 | 16 | **Method** 17 | 18 | [POST](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) 19 | 20 | **Parameters** 21 | 22 | * **incident\_id** - The unique ID of the incident to add a note for. - _Required_ 23 | * **note** - The body of the note to add to the incident report. - _Required_ 24 | * **public** - Whether or not the note is publicly viewable. Default is false.. 25 | * **format** - The encoding format for results. If this argument is omitted, it defaults to JSON 26 | 27 | **Example** 28 | 29 | POST http://example.gov/open311-simple/?method=open311.incidents.addNote 30 | 31 | open311.incidents.getInfo 32 | -- 33 | 34 | Get the detailed information for an incident report. 35 | 36 | **Method** 37 | 38 | [GET](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) 39 | 40 | **Parameters** 41 | 42 | * **incident\_id** - The unique ID of the incident to get information about. - _Required_ 43 | * **format** - The encoding format for results. If this argument is omitted, it defaults to JSON 44 | 45 | **Notes** 46 | 47 | * All dates are recorded using the [W3C DateTime format](http://www.w3.org/TR/NOTE-datetime) format. 48 | 49 | * All geographic data is returned using the unprojected [WGS84](http://spatialreference.org/ref/epsg/4326/) datum (read: plain old latitudes and longitudes). 50 | 51 | **Example** 52 | 53 | GET http://example.gov/open311-simple/?method=open311.incidents.getInfo 54 | 55 | { 56 | "incident": { 57 | "id": 999, 58 | "service_id": 2, 59 | "status_id": 1, 60 | "reported": "..." 61 | } 62 | } 63 | 64 | open311.incidents.report 65 | -- 66 | 67 | **This method requires authentication** 68 | 69 | Report an incident for a given service. Returns a unique ID for the incident that may be used to call the _open311.incidents.status_ API method. 70 | 71 | **Method** 72 | 73 | [POST](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) 74 | 75 | **Parameters** 76 | 77 | * **service\_id** - A valid service\_id as defined by the city operating the Open 311 (Simple) API - _Required_ 78 | * **latitude** - A valid WGS84 coordinate - _Required_ 79 | * **longitude** - A valid WGS84 coordinate - _Required_ 80 | * **description** - A free-form text field in which the user reporting the incident may leave additional notes. 81 | * **format** - The encoding format for results. If this argument is omitted, it defaults to JSON 82 | 83 | **Notes** 84 | 85 | * All dates should be passed in using the [W3C DateTime format](http://www.w3.org/TR/NOTE-datetime) format. 86 | 87 | * All geographic data is expected to be using the unprojected [WGS84](http://spatialreference.org/ref/epsg/4326/) datum (read: plain old latitudes and longitudes). 88 | 89 | **Example** 90 | 91 | POST http://example.gov/open311-simple/?method=open311.incidents.report 92 | 93 | { 94 | "incident": { 95 | "id": 999, 96 | "service_id": 2, 97 | "status_id": 1, 98 | "reported": "..." 99 | } 100 | } 101 | 102 | open311.incidents.search 103 | -- 104 | 105 | Returns a list of incidents matching a search criteria as defined by the API request. 106 | 107 | **Method** 108 | 109 | [GET](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) 110 | 111 | **Parameters** 112 | 113 | * **service\_id** - The unique ID of the service type to search for. Multiple services may be passed in as a comma-separated list. 114 | * **incident\_id** - The unique ID of the incident to search for. Multiple incidents may be passed in as a comma-separated list. 115 | * **status\_id** - The unique ID of a status type to search for. Multiple statuses may be passed in as a comma-separated list. 116 | * **created** - The date or date range (see [api.md](https://github.com/straup/open311-simple/blob/master/api.md) for details) of when an incident was reported. 117 | * **modified** - The date or date range (see [api.md](https://github.com/straup/open311-simple/blob/master/api.md) for details) of when an incident was last modified. 118 | * **where** - A geopgraphic location or extent (see [api.md]((https://github.com/straup/open311-simple/blob/master/api.md) for details) for details) in which to scope the query. 119 | * **page** - The page of results to return. If this argument is omitted, it defaults to 1. 120 | * **per_page** - Number of results to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is left to the discretion of individual cities. 121 | * **format** - The encoding format for results. If this argument is omitted, it defaults to JSON 122 | 123 | **Notes** 124 | 125 | * All dates should be passed to the API (and returned in results) using the [W3C DateTime format](http://www.w3.org/TR/NOTE-datetime). 126 | 127 | * All geographic data should be passed to the API using the unprojected [WGS84](http://spatialreference.org/ref/epsg/4326/) datum (read: plain old latitudes and longitudes). 128 | 129 | * If called with a valid OAuth token and signature then the query will be scoped to the user associated with that token. 130 | 131 | * Parameterless searches are not permitted. You must define at least one search criteria. 132 | 133 | **Example** 134 | 135 | GET http://example.gov/open311-simple/?method=open311.incidents.search 136 | 137 | { 138 | "total": 2, 139 | "per_page": 100, 140 | "page": 1, 141 | "incidents": [ 142 | { "id": 999, "service_id": 2, "status_id": 1, "reported": "..." }, 143 | { "id": 23, "service_id": 3, "status_id": 1, "reported": "..." }, 144 | ] 145 | } 146 | 147 | open311.services 148 | == 149 | 150 | open311.services.getInfo 151 | -- 152 | 153 | Returns basic information (as included in the _open311.services.getList_ method) as well any additional details that may be relevant to the service. 154 | 155 | **Method** 156 | 157 | [GET](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) 158 | 159 | **Parameters** 160 | 161 | * **service\_id** - A valid service\_id to get information about. - _Required_ 162 | * **format** - The encoding format for results. If this argument is omitted, it defaults to JSON 163 | 164 | **Example** 165 | 166 | GET http://example.gov/open311-simple/?method=open311.services.getInfo 167 | 168 | { 169 | "service": { 170 | "id": 1, 171 | "name": "...", 172 | "description": "..." 173 | } 174 | } 175 | 176 | open311.services.getList 177 | -- 178 | 179 | Returns a list of services for which incidents may be reported. The types of services and their meaning are left to the discretion of individual cities. 180 | 181 | **Method** 182 | 183 | [GET](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) 184 | 185 | **Parameters** 186 | 187 | * **page** - The page of results to return. If this argument is omitted, it defaults to 1. 188 | * **per_page** - Number of results to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is left to the discretion of individual cities. 189 | * **format** - The encoding format for results. If this argument is omitted, it defaults to JSON 190 | 191 | **Example** 192 | 193 | GET http://example.gov/open311-simple/?method=open311.services.getList 194 | 195 | { 196 | "total": 3, 197 | "per_page": 100, 198 | "page": 1, 199 | "services": [ 200 | { "id": 1, "name": "..." }, 201 | { "id": 2, "name": "..." }, 202 | { "id": 3, "name": "..." } 203 | ] 204 | } 205 | 206 | open311.statuses 207 | == 208 | 209 | open311.statuses.getList 210 | -- 211 | 212 | Return a list of valid statuses for incidents. The types of statuses and their meaning are left to the discretion of individual cities. 213 | 214 | **Method** 215 | 216 | [GET](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) 217 | 218 | **Parameters** 219 | 220 | * **page** - The page of results to return. If this argument is omitted, it defaults to 1. 221 | * **per_page** - Number of results to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is left to the discretion of individual cities. 222 | * **format** - The encoding format for results. If this argument is omitted, it defaults to JSON 223 | 224 | **Example** 225 | 226 | GET http://example.gov/open311-simple/?method=open311.statuses.getList 227 | 228 | open311.where 229 | == 230 | 231 | open311.where.getList 232 | -- 233 | 234 | Returns a list of geographic prefixes that may be used to query for incident reports using the 'open311.incidents.search' API method. 235 | 236 | **Method** 237 | 238 | [GET](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) 239 | 240 | **Parameters** 241 | 242 | * **page** - The page of results to return. If this argument is omitted, it defaults to 1. 243 | * **per_page** - Number of results to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is left to the discretion of individual cities. 244 | * **format** - The encoding format for results. If this argument is omitted, it defaults to JSON 245 | 246 | **Example** 247 | 248 | GET http://example.gov/open311-simple/?method=open311.where.getList 249 | 250 | { 251 | "total": 3, 252 | "per_page": 100, 253 | "page": 1, 254 | "terms": [ 255 | { "prefix": "bbox", "description": "...", "example": "bbox:37.788,-122.344,37.857,-122.256" }, 256 | { "prefix": "near", "description": "...", "example": "near:37.804376,-122.271180" }, 257 | { "prefix": "zip", "description": "...", "example": "zip:94110" } 258 | ] 259 | } 260 | 261 | -------------------------------------------------------------------------------- /api.md: -------------------------------------------------------------------------------- 1 | The Open 311 Simple API 2 | == 3 | 4 | There are two basic classes of API methods: _list_ methods and _info_ 5 | methods. The former are meant return the minimum amount of data around a service 6 | or incident to allow a user to perform a discrete action; the latter return all 7 | the information available for a service or incident. 8 | 9 | Specification 10 | == 11 | 12 | The API method specification is defined as a JSON data structure containing basic metadata about each individual method such that it can be used by actual running code to delegate API requests and generate documentation. 13 | 14 | Developers are still required to implement their own dispatching system in order to serve and process those API methods. The modeling of the API specification in JSON is simply meant as a language-agnostic configuration file that can be easily shared across programming languages (since they basically all have JSON decoders to translate the data in to native data structures). 15 | 16 | API methods are defined in [api-methods.json](https://github.com/straup/open311-simple/blob/master/api-methods.json) document. The [api-methods.md](https://github.com/straup/open311-simple/blob/master/api-methods.md) document contains human-friendly documentation for the API. 17 | 18 | Transport 19 | == 20 | 21 | All API requests and responses are delivered using [HTTP](http://www.w3.org/Protocols/). Wherever possible API requests are performed using the HTTP _GET_ method. In some cases where security considerations demand it (for example, reporting an incident) API requests are performed using the HTTP _POST_ method. 22 | 23 | Successful API methods are returned using HTTP responses in the 200 range. All others are expected to return responses in the 400-500 range although this is left to the discretion of individual cities. 24 | 25 | _The emphasis here is that, as much as possible, the API may be explored and tested by as many people as possible using nothing more complicated that a web browser._ 26 | 27 | Response formats 28 | == 29 | 30 | The default API response format is [JSON](http://www.json.org/). 31 | 32 | Additional responses are left to the discretion of individual cities and are identified in API request using the _format_ parameter. 33 | 34 | (U)IDs 35 | == 36 | 37 | The Open311 Simple specification is agnostic about whether or not unique identifiers (UID) should be strings or integers. This is left to the discretion of individual cities and the requirements of their technical infrastructure. 38 | 39 | Out of necessity, though, _client applications_ may need to treat every UID as a string in order to provide consistent access (read: local database storage) multiple cities. 40 | 41 | The example responses included with the specification assume integer based UIDs. 42 | 43 | See also: [Open311 Simple Issues #1](https://github.com/straup/open311-simple/issues/1) 44 | 45 | Character encoding 46 | == 47 | 48 | UTF-8. 49 | 50 | Pagination 51 | == 52 | 53 | If an API method is a _list_-style method (described above) then it will always paginate its responses. Pagination is simply a way to limit the number of responses returned for a single method call, as defined by a _per_page_ parameter (as in the number of results to return). Offsets are calculated by multiplying the _page_ and _per_page_ parameters (or attributes in the case of API responses). 54 | 55 | Requests to paginated methods may pass the following parameters: 56 | 57 | * **page** – the current offset (calculated by multiplying _page_ by _per_page_) to start fetching results from. 58 | 59 | * **per_page** – the number of results to return for a given API request. 60 | 61 | Default values are left to the discretion of individual cities. 62 | 63 | Paginated responses will always return the following attributes: 64 | 65 | * **total** – The total number of results for the query issued by the API method, regardless of pagination. 66 | 67 | * **pages** – The total number of paginated results (or "pages") that will be returned by the API method. This number is calculated by dividing _total_ by the _per_page_ parameter (or default value, defined above). 68 | 69 | * **page** – The current offset "page" for the total result set. 70 | 71 | For example: 72 | 73 | GET example.com/api?method=open311.services.getList?page=2&per_page=2 74 | 75 | { 76 | "total": 5, 77 | "pages": 3, 78 | "page": 2, 79 | "services": [ 80 | { "id": 1, "name": "...", "type": "..." }, 81 | { "id": 2, "name": "...", "type": "..." }, 82 | ] 83 | } 84 | 85 | Incidents 86 | == 87 | 88 | The minimum (default) data structure for incidents is very simple: It contains a unique identifier, references to the service that was reported, the incident's current status and its location, relevent dates and any descriptive text that the reporting the incident may have included. For example: 89 | 90 | { 91 | "id": 999, 92 | "service_id": 2, 93 | "status_id": 1, 94 | "created": "2011-03-15T23:22:45Z", 95 | "modified": "2011-03-16T10:17:33Z", 96 | "latitude": 37.23, 97 | "longitude": -122.45, 98 | "description": "This fire hydrant has been leaking for two days now", 99 | } 100 | 101 | That's it. Any additional properties passed back in an incident response are left to the discretion of individual cities. Which means the following keys (properties) are reserved: 102 | 103 | * **id** - A unique identifier for the incident report. Incident IDs are generated by and unique to inidividual cities. Client MUST be able to pass this to the _open311.indicidents.getInfo_ API method to retrieve details for a report. 104 | 105 | * **service_id** – A unique identifier for the service associated with an incident report. Service IDs are generated by and unique to inidividual cities. Client MUST be able to call the _open311.services.getList_ API method to retrieve a complete list of service IDs and their descriptions. 106 | 107 | * **status_id** - A unique identifier for the status of an incident report. Status IDs are generated by and unique to inidividual cities. Client MUST be able to call the _open311.incidents.getStatuses_ API method to retrieve a complete list of status IDs and their descriptions. 108 | 109 | * **created** - The date the incident was reported; see the "Dates" section (below) for details on dates are represented. 110 | 111 | * **modified** - The most recent date the incident was modified; see the "Dates" section (below) for details on dates are represented. 112 | 113 | * **latitude** - The latitude for the incident report; see the "Geo" section (below) for details on how coordinates are represented. 114 | 115 | * **longitude** - The longitude for the incident report; see the "Geo" section (below) for details on how coordinates are represented. 116 | 117 | * **description** - Any addition descriptive text that the user who reported the incident may have included with the initial report. 118 | 119 | _For the time being, whether or not the a reference to the individual user reporting an incident is included in an incident report is left to the discretion of individual cities. Trying to reconcile the privacy requirements, and technical implimentations to support them, across multiple jurisdictions is outside the scope of this document._ 120 | 121 | Non-standard properties should, wherever applicable, adopt the [OpenStreetMap (OSM) standard for tagging](https://wiki.openstreetmap.org/wiki/Any_tags_you_like) and encourage users to follow [the guidelines and naming conventions that the OSM community has developed](https://wiki.openstreetmap.org/wiki/Map_Features) and refined over time. This model has been proven to be both robust enough to be used to power the OSM map renderer and flexible enough to meet the needs of tinkerers and playful enough to encourage innovations no one even considered at the outset. 122 | 123 | Dates 124 | == 125 | 126 | All dates are recorded using the [W3C DateTime format](http://www.w3.org/TR/NOTE-datetime). 127 | 128 | All dates are passed a single-value reference to a date or date range, replacing 129 | convential "start" and "stop" prefixes for the various date arguments. 130 | 131 | Because the "-" and ":" characters are used by the W3C DateTime format date 132 | ranges are separated using a semicolon (;) character. 133 | 134 | Example values: 135 | 136 | * A single, full day: 2010-05-26. 137 | 138 | * A full week: 2010-05-23;2010-05-29. 139 | 140 | * A single hour: 2010-05-26T21:00:00Z;2010-05-26T22:00:00Z. 141 | 142 | Geo 143 | == 144 | 145 | All geographic data should be passed to (and returned from) the API using the 146 | unprojected [WGS84](http://spatialreference.org/ref/epsg/4326/) datum. The phrase "unprojected WGS84 data" can be roughly translated as: _Plain-old latitude and longitude, the way those of us who don't study GIS think about things._ 147 | 148 | Geographic coordinates are expressed as latitude followed by longitude. Bounding boxes are expressed as a set of coordinates representing the South-West and North-East edges of the container. 149 | 150 | The "where" argument 151 | -- 152 | 153 | The "where" argument (used by the _open311.incidents.search_ method) wraps all geographic queries in a single 154 | interface. Argument values are prefixed with a human-readable string followed by 155 | a colon (":") followed a string representing a geographic location. 156 | 157 | The prefix is used by parsers to determine how the rest of a "where" string 158 | should be interpreted. For example: 159 | 160 | * Bounding box: ?where=bbox:37.788,-122.344,37.857,-122.256 161 | 162 | * Around a point: ?where=near:37.804376,-122.271180 163 | 164 | * In a geohash: ?where=geohash:9q9p1dhf7 165 | 166 | * In a zip code: ?where=zip:94612 167 | 168 | The single parameter removes possible conflicts or overlaps between other 169 | parameters, and introduces an extensible way to namespace "known" areas like zip 170 | codes, countries and allow individual cities to introduce place types specific 171 | to their jurisdiction (for example: housing lots or building identifiers). 172 | 173 | **All Open311 Simple providers MUST implement the "bbox:" prefix to allow for geographic queries within a bounding box.** All other prefixes are left to the discretion (and technical infrastructure) of individual cities. API clients may request a list of supported prefixed using the _open311.where.getList_ API method. 174 | 175 | Authentication 176 | == 177 | 178 | Not all cities require that incident reports be filed with an associated user (or account) ID. Those that don't really should in order that both cities and individual contributors may review and audit incident reports. Requiring user accounts introduces an extra burden on both cities and users. For cities it means maintaining an additional database of user accounts and for users it means an extra sign-up process and another password to remember. This problem can by and large be mitigated by using one or more social web services are a single-sign-on (or validation) service. Both Facebook and Twitter are happy to perform this role for third-parties and have well-developed and widely adopted platforms for implementing this scenario. 179 | 180 | The Open311 Simple specification dictates that all API methods that require authentication use the [OAuth2](http://oauth.net/2/) delegated authentication standard. Additionally it should be possible for an individual user to scope a query (using the _open311.incidents.search_ API method) an Open311 Simple dataset to themselves by passing a valid OAuth 2 token and signature with their request. 181 | 182 | Questions: 183 | == 184 | 185 | * Should there be a _open311.incidents.getHistory_ API method to return the comments and progress, over time, for an incident report ? 186 | 187 | See also: 188 | == 189 | 190 | * [open311-simple-papp](https://github.com/straup/open311-simple-app) - A reference implementation of the Open311 spec built using [Flamework](https://github.com/straup/flamework) (read: PHP and MySQL). 191 | --------------------------------------------------------------------------------