CivicPlus desires to expose read/write access to data within individual Municode Meetings, now our Essential Agenda and Meeting Management solution, accounts via API. There are numerous use cases for this access. Certain customers desire to develop integration with our Essential solution to present meeting data and documents on their website with complete aesthetic flexibility. Third-party vendors may desire to integrate with our platform to enhance both offerings. Also, our engineering teams will leverage the API access to integrate our Essential with existing and future products in the CivicPlus family.
Important Notes
- To use the API, you must first contact CivicPlus Support to request an access token.
- Users with an access token can access the API on our Public API page (this page will not function for users that do not have an access token).
- Visit our Public API Documentation page to view API examples for other languages.
Article Navigation
Getting Started (Jwt)
To access the API we will need a bearer token...
public static async Task GetTokenAsync(HttpClient client, string clientId, string clientSecret)
{
var tokenPostUrl = $"{authUrl}{tokenPath}";
var request = new HttpRequestMessage(HttpMethod.Post, tokenPostUrl);
request.Content = new FormUrlEncodedContent(new Dictionary
{
["grant_type"] = "client_credentials",
["client_id"] = clientId,
["client_secret"] = clientSecret
});
var response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
response.EnsureSuccessStatusCode();
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
if (payload["error"] != null)
{
throw new InvalidOperationException("An error occurred while retrieving an access token.");
}
return (string)payload["access_token"];
}
Catch and renew an expired token...
var filters = new MeetingListFilters()
{
ArchivedState = (int)EArchivedState.All,
CreatedMeetingFromDate = new DateParams() { Day = -1, Month = -1, Year = -1 },
CreatedMeetingToDate = new DateParams() { Day = -1, Month = -1, Year = -1 },
Words = "",
MeetingTypeIds = new string[] { }
};
var bearerToken = "OldExpiredOrInvalidToken";
try
{
var response = await GetMeetingsAsync(httpClient, bearerToken, filters, 0);
Console.WriteLine("API response: {0}", response);
}
catch (HttpRequestException e)
{
const string unauthorized = "Response status code does not indicate success: 401 (Unauthorized).";
if (e.Message == unauthorized)
{
try
{
//The bearer token was unauthorized, lets try to get a new one.
var refreshedBearerToken = await GetTokenAsync(httpClient, testClientId, testClientSecret);
var response = await GetMeetingsAsync(httpClient, refreshedBearerToken, filters, 0);
Console.WriteLine("API response: {0}", response);
}
catch (Exception exception)
{
Console.WriteLine(exception);
throw;
}
}
}
ReadMeetings(Int32, EntityListDTO.MeetingListFilters)
Declaration
[Route("collection/{offset:int}")]
[HttpPost]
public HttpResponseMessage ReadMeetings(int offset, [FromBody] EntityListDTO.MeetingListFilters filters)
Parameters
Type | Name |
---|---|
System.Int32 | offset |
MunicodeMeetings.Infrastructures.DataTransferObjects.WebHostRole.EntityListDTO.MeetingListFilters | filters |
Returns
Type |
---|
System.Net.Http.HttpResponseMessage |
To fetch all meetings we can use the following method...
public static async Task GetMeetingsAsync(HttpClient client, string token, MeetingListFilters filters, int offset)
{
var urlWithOffset = $"{meetingsDetailUrl}/collection/{offset}";
var serializedContent = JsonConvert.SerializeObject(filters);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.PostAsync(urlWithOffset,
new StringContent(serializedContent, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
Usage...
var filters = new MeetingListFilters()
{
ArchivedState = (int)EArchivedState.All,
CreatedMeetingFromDate = new DateParams() { Day = -1, Month = -1, Year = -1 },
CreatedMeetingToDate = new DateParams() { Day = -1, Month = -1, Year = -1 },
Words = "",
MeetingTypeIds = new string[] { }
};
var resource = await GetMeetingsAsync(httpClient, testClientBearerToken, filters, 0);
Console.WriteLine("API response: {0}", resource);
ReadMeetingDetails(String)
Declaration
[HttpGet]
public HttpResponseMessage ReadMeetingDetails(string id)
Parameters
Type | Name |
---|---|
System.String | id |
Returns
Type |
---|
System.Net.Http.HttpResponseMessage |
To get details on a specific meeting...
public static async Task GetMeetingDetailsAsync(HttpClient client, string token, string id)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var getUrl = $"{meetingsDetailUrl}/{id}";
var response = await client.GetAsync(getUrl);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
Usage...
var result = await GetMeetingDetailsAsync(httpClient, testClientBearerToken,
"0c916582-cbed-40ca-af41-b449b83ec9bd");
Console.WriteLine("API response: {0}", result);
ReadItemDetails(String)
Declaration
[HttpGet]
public HttpResponseMessage ReadItemDetails(string itemId)
Parameters
Type | Name |
---|---|
System.String | itemId |
Returns
Type |
---|
System.Net.Http.HttpResponseMessage |
To get a specific meeting item...
public static async Task GetItemAsync(HttpClient client, string token, string id)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var getUrl = $"{meetingsItemUrl}/{id}";
var response = await client.GetAsync(getUrl);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
Usage...
var result = await GetItemAsync(httpClient, testClientBearerToken,
"cc51ce12-6c51-4b70-97d4-0fdee0a98be2");
Console.WriteLine("API response: {0}", result);
ReadMeetingMultimedia(Guid)
Declaration
[HttpGet]
public HttpResponseMessage ReadMeetingMultimedia(Guid meetingId)
Parameters
Type | Name |
---|---|
System.Guid | meetingId |
Returns
Type |
---|
System.Net.Http.HttpResponseMessage |
To get Video information for a specific meeting...
public static async Task GetMeetingMultimediaAsync(HttpClient client, string token, string id)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var getUrl = $"{meetingsMultimediaUrl}/{id}";
var response = await client.GetAsync(getUrl);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
Usage...
var result = await GetMeetingMultimediaAsync(httpClient, testClientBearerToken,
"e10657d4-bdf0-46a5-8bf9-a9d117c824f6");
JObject jObject = JObject.Parse(result);
MeetingStructure objectResult = jObject["meetingStructure"].ToObject();
Console.WriteLine("API response: {0}", result);
UpdateMeetingMultimediaTimeStamping(Guid, MeetingMultimediaDTO.MeetingStructure)
Declaration
[Route("multimedia/update/{meetingId:guid}")]
[HttpPost]
public HttpResponseMessage UpdateMeetingMultimediaTimeStamping(Guid meetingId,
[FromBody] MeetingMultimediaDTO.MeetingStructure structures)
Parameters
Type | Name |
---|---|
System.Guid | meetingId |
MunicodeMeetings.Infrastructures.DataTransferObjects.WebHostRole.MeetingMultimediaDTO.MeetingStructure | structures |
Returns
Type |
---|
System.Net.Http.HttpResponseMessage |
To Update Video info...
public static async Task UpdateMeetingMultimediaAsync(HttpClient client, string token, string id, MeetingStructure structure)
{
var serializedContent = JsonConvert.SerializeObject(structure);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var getUrl = $"{meetingsMultimediaUrl}/update/{id}";
var response = await client.PostAsync(getUrl,
new StringContent(serializedContent, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
Usage...
//fetch multimedia
var result = await GetMeetingMultimediaAsync(httpClient, testClientBearerToken,
"e10657d4-bdf0-46a5-8bf9-a9d117c824f6");
JObject jObject = JObject.Parse(result);
MeetingStructure objectResult = jObject["meetingStructure"].ToObject();
//change multimedia
objectResult.AudioVideoDetails.AudioVideoUrl = "https://myurl.domain/path";
var firstSectionId = new Guid("03f3207e-307b-4dd9-a8a8-8ef979b1b2b7");
var firstSection = objectResult.MultimediaItems.FirstOrDefault(x => x.EntityId == firstSectionId);
firstSection.TimeStamp = 60;
firstSection.UseDefaultMultimediaSettings = false;
firstSection.MultimediaBaseUrl = "http://www.google.com";
var firstItemId = new Guid("cc51ce12-6c51-4b70-97d4-0fdee0a98be2");
var firstItem = objectResult.MultimediaItems.FirstOrDefault(x => x.EntityId == firstItemId);
firstItem.TimeStamp = 125;
//save changes
var putResult = await UpdateMeetingMultimediaAsync(httpClient, testClientBearerToken,
"e10657d4-bdf0-46a5-8bf9-a9d117c824f6", objectResult);
Console.WriteLine("API response: {0}", putResult);
Variables Used
To get started fetching data, we need to add a few objects and some initial values
const string authUrl = "https://auth.devmunicode.com";
const string baseUrl = "https://meetingsgov.devmunicode.com";
const string tokenPath = "/connect/token";
private static readonly HttpClient httpClient = new HttpClient();
static readonly string meetingsDetailUrl = $"{baseUrl}/PublicApi/meeting";
static readonly string meetingsMultimediaUrl = $"{baseUrl}/PublicApi/meeting/multimedia";
static readonly string meetingsItemUrl = $"{baseUrl}/PublicApi/item";
static readonly string testClientId = "ABC";
static readonly string testClientSecret = "3f43d9b2-2341-4e6e-b66c-533fe63dd88c";
Objects Used
public struct DateParams
{
public int Day;
public int Month;
public int Year;
}
public struct MeetingListFilters
{
public string Words;
public DateParams CreatedMeetingFromDate;
public DateParams CreatedMeetingToDate;
public int ArchivedState;
public string[] MeetingTypeIds;
}
public enum EArchivedState
{
None = -1,
All = 0,
UnArchived = 1,
Archived = 2
}
public struct MeetingStructure
{
public Guid Id;
public Guid MeetingId;
public AudioVideoDetails AudioVideoDetails;
public int PublishState;
public MultimediaItem[] MultimediaItems;
}
public struct AudioVideoDetails
{
public int AudioVideoProviderType;
public string AudioVideoUrl;
}
public struct MultimediaItem
{
public Guid EntityId;
public string EntityName;
public string EntityTypeName;
public int TimeStamp;
public int MultimediaDisplayOptions;
public string CustomDisplay;
public bool IncludeNumbering;
public bool UseDefaultMultimediaSettings;
public string MultimediaBaseUrl;
}
Comments
Let us know what was helpful or not helpful about the article.0 comments
Please sign in to leave a comment.